1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-22 19:53:52 +00:00

Added SDL+OpenGL 2D rendering backend (ony basic features)

This commit is contained in:
Nicolay Korslund 2010-09-08 15:09:39 +02:00
parent b08c2f78dc
commit 4d25730388
7 changed files with 522 additions and 2 deletions

View file

@ -0,0 +1,321 @@
#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 );
// Bottom-left vertex (corner)
glTexCoord2i( 0, 0 );
glVertex3f(0,0,0);
// Bottom-right vertex (corner)
glTexCoord2i( 1, 0 );
glVertex3f( realDisp->w, 0, 0.f );
// Top-right vertex (corner)
glTexCoord2i( 1, 1 );
glVertex3f( realDisp->w, realDisp->h, 0.f );
// Top-left vertex (corner)
glTexCoord2i( 0, 1 );
glVertex3f( 0, realDisp->h, 0.f );
glEnd();
/*
glBegin(GL_TRIANGLES);
glColor3ub(255, 0, 0);
glVertex2d(0, 0);
glColor3ub(0, 255, 0);
glVertex2d(100,0);
glColor3ub(0, 0, 255);
glVertex2d(50, 50);
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(); }

View file

@ -0,0 +1,132 @@
#ifndef MANGLE_DRAW2D_SDLGL_H
#define MANGLE_DRAW2D_SDLGL_H
/** This driver is similar to SDLDriver, except that it uses SDL on
top of OpenGL.
I've decided to make it a separate file instead of just adding
optional OpenGL support to the original, so that pure SDL users
don't have to add OpenGL as a dependency.
*/
#include "../driver.hpp"
// Predeclarations keep the streets safe at night
struct SDL_Surface;
struct SDL_RWops;
namespace Mangle
{
namespace Rend2D
{
/// SDL-implementation of Sprite
struct SDLGL_Sprite : Sprite
{
/** Draw a sprite in the given position. Can only draw other SDL
sprites.
*/
void draw(Sprite *s, // Must be SDLGL_Sprite
int x, int y, // Destination position
int sx=0, int sy=0, // Source position
int w=-1, int h=-1 // Amount to draw. -1 means remainder.
);
SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true);
~SDLGL_Sprite();
// Information retrieval
virtual int width();
virtual int height();
SDL_Surface *getSurface() { return surface; }
// Fill with a given pixel value
void fill(int value);
private:
// The SDL surface
SDL_Surface* surface;
// If true, delete this surface when the canvas is destructed
bool autoDel;
};
class SDLGLDriver : public Driver
{
// The main display surface
SDLGL_Sprite *display;
// The screen surface. This is completely unused.
SDL_Surface *screen;
// The display surface and main GL texture. These are used when
// drawing the entire screen as one surface, as a drop-in
// replacement for SDLDriver.
SDL_Surface *realDisp;
unsigned int texture;
int nOfColors, texture_format;
public:
SDLGLDriver();
~SDLGLDriver();
/// Sets the video mode. Will create the window if it is not
/// already set up. Note that for SDL, bpp=0 means use current
/// bpp.
void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false);
/// Update the screen
void update();
/// Calls SDL_GL_SwapBuffers
void swap();
/// Draw surface to screen but do not call SDL_GL_SwapBuffers()
void updateNoSwap();
/// Set the window title, as well as the title of the window
/// when "iconified"
void setWindowTitle(const std::string &title,
const std::string &icon);
// Include overloads from our Glorious parent
using Driver::setWindowTitle;
/// Load sprite from an image file, using SDL_image.
Sprite* loadImage(const std::string &file);
/// Load sprite from an SDL_RWops structure. autoFree determines
/// whether the RWops struct should be closed/freed after use.
Sprite* loadImage(SDL_RWops *src, bool autoFree=false);
/// Load a sprite from an image file stored in memory. Uses
/// SDL_image.
Sprite* loadImage(const void* data, size_t size);
/// Set gamma value
void setGamma(float gamma) { setGamma(gamma,gamma,gamma); }
/// Set gamma individually for red, green, blue
void setGamma(float red, float green, float blue);
/// Convert an existing SDL surface into a sprite
Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true);
// Get width and height
int width() { return display ? display->width() : 0; }
int height() { return display ? display->height() : 0; }
/// Get the screen sprite
Sprite *getScreen() { return display; }
/// Not really a graphic-related function, but very
/// handly. Sleeps the given number of milliseconds using
/// SDL_Delay().
void sleep(int ms);
/// Get the number of ticks since SDL initialization, using
/// SDL_GetTicks().
unsigned int ticks();
};
}
}
#endif

View file

@ -1,9 +1,15 @@
GCC=g++ -Wall
GCC=g++ -Wall -Werror
all: sdl_test
all: sdl_test sdl_move_test sdlgl_move_test
sdl_test: sdl_test.cpp
$(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image
sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp
$(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image
sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp
$(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL
clean:
rm *_test

View file

View file

View file

@ -0,0 +1,30 @@
#include <iostream>
#include <fstream>
using namespace std;
#include "../servers/sdl_driver.hpp"
using namespace Mangle::Rend2D;
int main()
{
SDLDriver sdl;
sdl.setVideoMode(640,480,0,false);
sdl.setWindowTitle("Testing 123");
Sprite *screen = sdl.getScreen();
const char* imgName = "tile1-blue.png";
Sprite *image = sdl.loadImage(imgName);
for(int frames=0; frames<170; frames++)
{
screen->fill(0);
for(int j=0; j<10; j++)
for(int i=0; i<25; i++)
screen->draw(image, 2*frames+30*j, 20*i);
sdl.update();
sdl.sleep(10);
}
return 0;
}

View file

@ -0,0 +1,31 @@
#include <iostream>
#include <fstream>
using namespace std;
#include "../servers/sdl_gl_driver.hpp"
using namespace Mangle::Rend2D;
int main()
{
SDLGLDriver sdl;
sdl.setVideoMode(640,480,0,false);
sdl.setWindowTitle("Testing 123");
Sprite *screen = sdl.getScreen();
const char* imgName = "tile1-blue.png";
Sprite *image = sdl.loadImage(imgName);
for(int frames=0; frames<170; frames++)
{
screen->fill(0);
for(int j=0; j<10; j++)
for(int i=0; i<25; i++)
screen->draw(image, 2*frames+30*j, 20*i);
sdl.update();
sdl.sleep(5);
}
return 0;
}