#include "CPGraphics.h"
// Class startup. Still requires Init, though.
CPGraphicsEngine::CPGraphicsEngine(void)
{
m_initialized = false;
#ifdef USE_SDL
m_screen = NULL;
m_font = NULL;
#else
m_spriteHandler = NULL;
#endif
}
// Class cleanup.
CPGraphicsEngine::~CPGraphicsEngine(void)
{
if(m_initialized)
{
#ifdef USE_SDL
//Close the font that was used
TTF_CloseFont( m_font );
SDL_FreeSurface(m_screen);
//SDL_quit();
#else
if (m_spriteHandler != NULL)
m_spriteHandler->Release();
if (m_font != NULL)
m_font->Release();
#endif
}
}
// Initialize the graphics engine (DX or SDL)
bool CPGraphicsEngine::Init_Graphics(long windowPointer /*hwnd*/, int width, int height, int fullscreen)
{
m_initialized = true;
#ifdef USE_SDL
#ifdef USE_OPENGL
// SDL + OpenGL
// Try to use OpenGL
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
//Create Window
if( SDL_SetVideoMode( width, height, 32, SDL_OPENGL ) == NULL )
{
cout << "ERROR: Unable to set Video Mode (" << width << "x" << height << "x32)" << endl;
return false;
}
//Initialize OpenGL
if( init_GL(width,height,fullscreen) == false )
{
cout << "ERROR: Unable to Init OpenGL" << endl;
return false;
}
SDL_WM_SetCaption( "BugHunt 2942AD (SDL + OpenGL)", NULL );
#else
//SDL + SDL_gfx
// Setup the screen:
Uint32 tempFlags = SDL_SWSURFACE; // | SDL_DOUBLEBUF | SDL_FULLSCREEN;
if ( fullscreen ) tempFlags = tempFlags && SDL_FULLSCREEN;
m_screen = SDL_SetVideoMode( width, height, 32, tempFlags );
if (m_screen == NULL)
{
cout << "ERROR: Unable to set Video Mode (" << width << "x" << height << "x32)" << endl;
m_initialized = false;
return false;
}
SDL_WM_SetCaption( "BugHunt 2942AD (SDL)", NULL );
#endif
// SDL text fonts
//Initialize SDL_ttf
if( TTF_Init() == -1 )
{
cout << "Error: Unable to initialize True Type Fonts" << endl;
return false;
}
//Open the font
m_font = TTF_OpenFont( "Comic_Sans_MS_Bold.ttf", 14 ); // pitch size
#else
if (!Init_Direct3D((HWND)windowPointer, width, height, fullscreen))
m_initialized = false;
else
{
//create sprite handler object
HRESULT result = D3DXCreateSprite(d3ddev, &m_spriteHandler);
if (result != D3D_OK)
m_initialized = false;
// load the font
result = D3DXCreateFont( d3ddev, 14, 0, FW_NORMAL, 1, false, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
"Comic Sans", &m_font); // Arial Black
if (result != D3D_OK)
m_initialized = false;
}
m_inFrame = false;
#endif
return m_initialized;
}
// Called at the start of rendering a single frame.
// in SDL this is probably a no-op, except maybe to clear the screen...
bool CPGraphicsEngine::StartFrame()
{
#ifdef USE_SDL
#ifdef USE_OPENGL
//Clear the screen
glClear( GL_COLOR_BUFFER_BIT );
#else
Uint32 black = SDL_MapRGB( m_screen->format, 0, 0, 0 );
SDL_FillRect(m_screen, NULL, black);
#endif
return true;
#else
if(!m_inFrame)
{
HRESULT result;
result = d3ddev->BeginScene();
//if (result)
//{
//erase the entire background
d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0,0,0));
//start sprite handler
m_spriteHandler->Begin(D3DXSPRITE_ALPHABLEND);
m_inFrame = true;
return true;
//}
//else return false;
}
#endif
}
// Called at the end of rendering a single frame.
// This probably includes "flip buffer" in SDL
void CPGraphicsEngine::EndFrame()
{
#ifdef USE_SDL
#ifdef USE_OPENGL
//Update screen
SDL_GL_SwapBuffers();
#else
SDL_Flip( m_screen );
#endif
#else
if (m_inFrame)
{
//stop drawing
m_spriteHandler->End();
//stop rendering
d3ddev->EndScene();
//display the back buffer on the screen
d3ddev->Present(NULL, NULL, NULL, NULL);
m_inFrame = false;
}
#endif
}
//Draw a texture to the screen. This is full featured with rotation and scaling.
void CPGraphicsEngine::DrawTexture(float x, float y, int width, int height, float sx, float sy, float angle, unsigned char alpha, CPTexture& texture, CPRECT *rc, bool FixAngle)
{
// some angles are messed up, got to add 90 before drawing
int tAngle = angle;
if (FixAngle)
{
tAngle += 90;
if (tAngle < 0)
tAngle = 360 + tAngle;
if (tAngle >= 360)
tAngle = tAngle - 360;
}
#ifdef USE_SDL
tAngle = -tAngle; // fix difference in orientation of DX vs. SDL
if (m_initialized) // Don't draw before we are initialized.
{
SDL_Rect src;
SDL_Rect dest;
int centerX, centerY;
centerX = x + (width/2);
centerY = y + (height/2);
if (rc != NULL) // use the part of the image they told us about...
{
src.x = rc->left;
src.y = rc->top;
src.h = rc->bottom - rc->top;
src.w = rc->right - rc->left;
}
else // ... or use the whole thing.
{
src.x = 0;
src.y = 0;
src.h = height;
src.w = width;
}
#ifdef USE_OPENGL
// Convert to OpenGL texture ... FIRST ... this could be done as they are loaded to save time
// This is a handle to our texture object
GLuint oGLTexture;
SDL_Surface *surface;
// create a temporary image (extra blit, but allows us to take one frame of an animation)
surface = CPTexture::CreateDisplayFormatSurface(width, height);
// blit the part of the original image that we need to the current one.
SDL_BlitSurface( (SDL_Surface*)(texture.GetNativeTexture()), &src, surface, NULL );
// Have OpenGL generate a texture object handle for us
glGenTextures( 1, &oGLTexture );
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, oGLTexture );
// 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 );
// Edit the texture object's image data using the information SDL_Surface gives us
glTexImage2D( GL_TEXTURE_2D, 0, texture.nOfColors, surface->w, surface->h, 0,
texture.texture_format, GL_UNSIGNED_BYTE, surface->pixels );
int SQUARE_WIDTH = width * sx;
int SQUARE_HEIGHT = height * sy;
// OK - now DRAW it
//Move to offset (x,y) of destination
glTranslatef(x+(width/2), y+(height/2), 0 );
// offset to center the sprite
// now rotate it
glRotatef( angle, 0.0, 0.0, 1.0 );
glBegin( GL_QUADS );
//Top-left vertex (corner)
glTexCoord2i( 0, 0 );
glVertex3f( -(SQUARE_WIDTH/2), -(SQUARE_HEIGHT/2), 0 );
//Top-right vertex (corner)
glTexCoord2i( 1, 0 );
glVertex3f( (SQUARE_WIDTH/2), -(SQUARE_HEIGHT/2), 0 );
//Bottom-right vertex (corner)
glTexCoord2i( 1, 1 );
glVertex3f( (SQUARE_WIDTH/2), (SQUARE_HEIGHT/2), 0 );
//Bottom-left vertex (corner)
glTexCoord2i( 0, 1 );
glVertex3f( -(SQUARE_WIDTH/2), (SQUARE_HEIGHT/2), 0 );
glEnd();
//Reset
glLoadIdentity();
// Free the SDL_Surface only if it was successfully created
SDL_FreeSurface( surface );
// delete the openGl texture handle
glDeleteTextures( 1, &oGLTexture );
return;
#else
// Use SDL_gfx stuff:
// if no rotation and no scaling ...
if ((abs(angle
) <= 1
.0f
) && (sx
== 1
.0f
) && (sy
== 1
.0f
))
{
dest.x = x;//centerX - (width/2);
dest.y = y;//centerY - (height/2);
dest.w = width;//centerX + (width/2);
dest.h = height;//centerY + (height/2);
// draw to screen:
SDL_BlitSurface( (SDL_Surface*)(texture.GetNativeTexture()), &src, m_screen, &dest);
}
else
{
SDL_Surface *optSurf,*rotZoomPic;
// create a temporary image (extra blit, but allows us to take one frame of an animation)
// TODO: Optimize when rc is NULL to not do this...
optSurf = CPTexture::CreateDisplayFormatSurface(width, height);
// blit the part of the original image that we need to the current one.
SDL_BlitSurface( (SDL_Surface*)(texture.GetNativeTexture()), &src, optSurf, NULL );
// draw with rotate/scale.
// TODO: If rotate = 0, scale = 1, optimize and just use SDL_BlitSurface
rotZoomPic = rotozoomSurfaceXY( optSurf, static_cast<double>(tAngle), sx, sy, SMOOTHING_OFF);
//Set all pixels of color R:0xFF, G:0x00, B:0xFF to be transparent
Uint32 colorkey = SDL_MapRGB( rotZoomPic->format, 0, 0, 0);
SDL_SetColorKey( rotZoomPic, SDL_SRCCOLORKEY, colorkey );
// final extents (rotating and scaling changes this)
dest.x = centerX - (rotZoomPic->w/2);
dest.y = centerY - (rotZoomPic->h/2);
dest.w = rotZoomPic->w;
dest.h = rotZoomPic->h;
// draw to screen, finally:
SDL_BlitSurface( rotZoomPic,NULL, m_screen, &dest );
// cleanup:
SDL_FreeSurface(rotZoomPic);
SDL_FreeSurface(optSurf);
}
#endif
}
#else
if (m_initialized)
{
if (!texture.isSurface)
{
const int halfWidth = width/2; //desc.Width >> 1;
const int halfHeight = height/2; //desc.Height >> 1;
D3DXMATRIX rotate, trasl, scale, result;
D3DXMatrixIdentity(&result);
D3DXMatrixTranslation(&trasl, -halfWidth, -halfHeight, 0);
D3DXMatrixMultiply(&result, &result, &trasl);
D3DXMatrixScaling(&scale, sx, sy, 1.0f);
D3DXMatrixMultiply(&result, &result, &scale);
D3DXMatrixRotationZ(&rotate, D3DXToRadian(tAngle));
D3DXMatrixMultiply(&result, &result, &rotate);
D3DXMatrixTranslation(&trasl, +halfWidth, +halfHeight, 0);
D3DXMatrixMultiply(&result, &result, &trasl);
D3DXMatrixTranslation(&trasl, x, y, 0);
D3DXMatrixMultiply(&result, &result, &trasl);
m_spriteHandler->SetTransform(&result);
m_spriteHandler->Draw((LPDIRECT3DTEXTURE9)(texture.GetNativeTexture()), (RECT *)rc, NULL, NULL, (0xFFFFFF) | (alpha << 24));
}
}
#endif
}
// NOTE: This is used to draw the background image(s) for each level and the health bars //
// ALSO: It does NOT use the same ScaleX and ScaleY that the avatars use //
//Draw a texture in a slightly simpler version--scales it to fit from source rect to destination rect.
void CPGraphicsEngine::DrawStretchTexture(CPTexture& texture, CPRECT sourceRect, CPRECT destRect)
{
#ifdef USE_SDL
#ifdef USE_OPENGL
// Convert to OpenGL texture ... FIRST ... this could be done as they are loaded to save time
// This is a handle to our texture object
GLuint oGLTexture;
SDL_Surface *surface;
int height = sourceRect.bottom - sourceRect.top;
int width = sourceRect.right - sourceRect.left;
// convert source to SDL_Rect
SDL_Rect src;
src.x = sourceRect.left;
src.y = sourceRect.top;
src.h = sourceRect.bottom - sourceRect.top;
src.w = sourceRect.right - sourceRect.left;
// create a temporary image (extra blit, but allows us to take one frame of an animation)
surface = CPTexture::CreateDisplayFormatSurface(width, height);
// blit the part of the original image that we need to the current one.
SDL_BlitSurface( (SDL_Surface*)(texture.GetNativeTexture()), &src, surface, NULL );
// Have OpenGL generate a texture object handle for us
glGenTextures( 1, &oGLTexture );
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, oGLTexture );
// 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 );
// Edit the texture object's image data using the information SDL_Surface gives us
glTexImage2D( GL_TEXTURE_2D, 0, texture.nOfColors, surface->w, surface->h, 0,
texture.texture_format, GL_UNSIGNED_BYTE, surface->pixels );
int SQUARE_HEIGHT = destRect.bottom - destRect.top;
int SQUARE_WIDTH = destRect.right - destRect.left;
// OK - now DRAW it
//Move to offset (x,y) of destination
glTranslatef(destRect.left, destRect.top, 0 );
glBegin( GL_QUADS );
//Top-left vertex (corner)
glTexCoord2i( 0, 0 );
glVertex3f( 0, 0, 0 );
//Top-right vertex (corner)
glTexCoord2i( 1, 0 );
glVertex3f( SQUARE_WIDTH, 0, 0 );
//Bottom-right vertex (corner)
glTexCoord2i( 1, 1 );
glVertex3f( SQUARE_WIDTH, SQUARE_HEIGHT, 0 );
//Bottom-left vertex (corner)
glTexCoord2i( 0, 1 );
glVertex3f( 0, SQUARE_HEIGHT, 0 );
glEnd();
//Reset
glLoadIdentity();
// Free the SDL_Surface
SDL_FreeSurface( surface );
// delete the openGl texture handle
glDeleteTextures( 1, &oGLTexture );
return;
#else
// use SGL_gfx stuff:
double scaleX, scaleY;
// SDL_Surface* rotZoomPic;
SDL_Rect src, dst;
src.x = sourceRect.left;
src.y = sourceRect.top;
src.h = sourceRect.bottom - sourceRect.top;
src.w = sourceRect.right - sourceRect.left;
dst.x = destRect.left;
dst.y = destRect.top;
dst.h = destRect.bottom - destRect.top;
dst.w = destRect.right - destRect.left;
scaleX = static_cast<double>(dst.w) / static_cast<double>(src.w);
scaleY = static_cast<double>(dst.h) / static_cast<double>(src.h);
// Removing this doubles the FPS ... likely because the BG surfaces use this and they are 2048x2048x32bpp in size
// rotZoomPic = rotozoomSurfaceXY( (SDL_Surface*)(texture.GetNativeTexture()), 0.0, scaleX, scaleY, SMOOTHING_OFF);
// src.x = src.x * scaleX;
// src.y = src.y * scaleY;
src.h = src.h * scaleY;
src.w = src.w * scaleX;
// SDL_BlitSurface( rotZoomPic, &src, m_screen, &dst );
SDL_BlitSurface( (SDL_Surface*)(texture.GetNativeTexture()), &src, m_screen, &dst );
// SDL_FreeSurface(rotZoomPic);
#endif
#else
if (texture.isSurface)
d3ddev->StretchRect(texture.m_nativeSurface, (RECT *)(&sourceRect), backbuffer, (RECT *)(&destRect), D3DTEXF_NONE);
#endif
}
void CPGraphicsEngine::DrawTextToRect(std::string text, CPRECT rect, CPCOLOR color)
{
#ifndef USE_SDL
m_font->DrawTextA( NULL,
text.c_str(),
text.length(),
(LPRECT)&rect,
DT_LEFT, //DT_RIGHT,
color
);
#else
// TTF fonts
//The color of the font
SDL_Color textColor = { CPCOLOR_REDVALUE(color), CPCOLOR_GREENVALUE(color), CPCOLOR_BLUEVALUE(color) };
//Render the text
SDL_Surface *message = TTF_RenderText_Solid( m_font, text.c_str(), textColor );
//If there was an error in rendering the text
if( message == NULL )
{
cout << "Error rendering text: " << text << endl;
return;
}
//Holds offsets
SDL_Rect offset;
//Get offsets
offset.x = rect.left;
offset.y = rect.top;
//Blit
SDL_BlitSurface( message, NULL, m_screen, &offset );
#endif
}
// Init object
CPTexture::CPTexture()
{
#ifndef USE_SDL
m_nativeTexture = NULL;
#endif
m_nativeSurface = NULL;
isSurface = false;
}
// cleanup object
CPTexture::~CPTexture(void)
{
if(m_nativeSurface != NULL)
#ifdef USE_SDL
SDL_FreeSurface(m_nativeSurface);
#else
m_nativeSurface->Release();
if(!m_nativeTexture == NULL)
m_nativeTexture->Release();
#endif
}
// Load a texture
bool CPTexture::LoadTextureFromFile(char* filename, CPCOLOR color, bool createDirectlyInVideoMemory)
{
#ifdef USE_SDL
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
cout << "Loading from file: " << filename << endl;
// load the image:
loadedImage = IMG_Load( filename );
// optimize it for the screen:
if ( loadedImage != NULL )
{
optimizedImage = SDL_DisplayFormat( loadedImage );
SDL_FreeSurface( loadedImage ); // (don't need anymore)
}
// set cyan to be transparent
if( optimizedImage != NULL )
{
//Map the color key
Uint32 colorkey = SDL_MapRGB( optimizedImage->format, 0xFF, 0, 0xFF );
//Set all pixels of color R 0xFF, G 0, B 0xFF to be transparent
SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, colorkey );
}
m_nativeSurface = optimizedImage;
#ifdef USE_OPENGL
// Convert to OpenGL format
// Check that the image's width is a power of 2
if ( (optimizedImage->w % 2) != 0 )
{
cout << "warning: image.bmp's width is not a power of 2 (w=" << optimizedImage->w << ")" << endl;
}
// Also check if the height is a power of 2
if ( (optimizedImage->h % 2) != 0 )
{
cout << "warning: image.bmp's height is not a power of 2 (h=" << optimizedImage->h << ")" << endl;
}
// get the number of channels in the SDL surface
nOfColors = optimizedImage->format->BytesPerPixel;
if (nOfColors == 4) // contains an alpha channel
{
if (optimizedImage->format->Rmask == 0x000000ff)
texture_format = GL_RGBA;
else
texture_format = GL_BGRA;
}
else if (nOfColors == 3) // no alpha channel
{
if (optimizedImage->format->Rmask == 0x000000ff)
texture_format = GL_RGB;
else
texture_format = GL_BGR;
}
else
{
cout << "warning: the image is not truecolor.. this will probably break (noOfColors="
<< nOfColors << ")"
<< endl;
// this error should not go unhandled
}
#endif
// ...
return true;
#else
if (!createDirectlyInVideoMemory)
{
m_nativeTexture = LoadTexture(filename, (D3DCOLOR)color);
if (m_nativeTexture == NULL)
return false;
isSurface = false;
return true;
}
else
{
m_nativeSurface = LoadSurface(filename, (D3DCOLOR)color);
if (m_nativeSurface == NULL)
return false;
isSurface = true;
return true;
}
#endif
}
bool CPTexture::CreateOffscreenTexture(int width, int height)
{
#ifdef USE_SDL
return true;
#else
HRESULT result = d3ddev->CreateOffscreenPlainSurface(
width, height,
D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT,
&m_nativeSurface,
NULL);
if (result != D3D_OK)
return false;
else
{
isSurface = true;
return true;
}
#endif
}
// Get the DX or SDL object (mostly for use by CPGraphicsEngine...)
long CPTexture::GetNativeTexture()
{
#ifdef USE_SDL
return (long)m_nativeSurface;
#else
if (isSurface) return (long)m_nativeSurface;
else return (long)m_nativeTexture;
#endif
}
// checks if surface created. "return true;" in Linux.
bool CPTexture::CreatedInVideoMemory()
{
#ifdef USE_SDL
return true;
#else
return isSurface;
#endif
}
#ifdef USE_SDL
bool IntersectCPRect( CPRECT& dest, const CPRECT src1, const CPRECT src2)
{
int px0,py0,px1,py1;
int cx0,cy0,cx1,cy1;
int rx0,ry0,rx1,ry1;
// fill in default (NULL) result rectangle
dest.left = 0; // x
dest.top = 0; // y
dest.right = 0; // w
dest.bottom = 0; // h
// get coordinates of the rectangles
px0 = src1.left;
py0 = src1.top;
px1 = src1.left + src1.right - 1;
py1 = src1.top + src1.bottom - 1;
cx0 = src2.left;
cy0 = src2.top;
cx1 = src2.left + src2.right - 1;
cy1 = src2.top + src2.bottom - 1;
// check if the rectangles intersect
//if ((cx0 < px0) && (cx1 < px0))
if (cx1 < px0)
return false;
// if((cx0 > px1) && (cx1 > px1) )
if(cx0 > px1)
return false;
// if ((cy0 < py0) && (cy1 < py0))
if (cy1 < py0)
return false;
// if ((cy0 > py1) && (cy1 > py1))
if (cy0 > py1)
return false;
// intersect x
if(cx0 <= px0) rx0 = px0;
else rx0 = cx0;
if(cx1 >= px1) rx1 = px1;
else rx1 = cx1;
// intersect y
if(cy0 <= py0) ry0 = py0;
else ry0 = cy0;
if(cy1 >= py1) ry1 = py1;
else ry1 = cy1;
// fill in result rect
dest.left = rx0;
dest.top = ry0;
dest.right = (rx1-rx0)+1;
dest.bottom = (ry1-ry0)+1;
return true;
};
SDL_Surface* CPTexture::CreateDisplayFormatSurface(int width, int height)
{
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
SDL_Surface* newSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, bmask, gmask, rmask, amask);
SDL_Surface* optimizedSurface = SDL_DisplayFormat(newSurface);
SDL_FreeSurface(newSurface);
return optimizedSurface;
}
int CPTexture::GetWidth()
{
return m_nativeSurface->w;
}
int CPTexture::GetHeight()
{
return m_nativeSurface->h;
}
#ifdef USE_OPENGL
bool init_GL(int width, int height, int fullscreen)
{
glEnable( GL_TEXTURE_2D ); // added
//Set clear color
glClearColor( 0, 0, 0, 0 );
glViewport( 0, 0, width, height ); // added
glClear( GL_COLOR_BUFFER_BIT ); // added
//Set projection
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, width, height, fullscreen, -1, 1 );
//Initialize modelview matrix
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
//If there was any errors
if( glGetError() != GL_NO_ERROR )
{
cout << "Error: Init_GL failed !" << endl;
return false;
}
//If everything initialized
return true;
}
#endif
#endif