Original Post
Hi, I am an aspiring game dever, attempting for the first time to switch to C++ as the language of choice for my engine. Previously, I had created a fairly comprehensive 3D framework built upon LWJGL and Java. This framework also included a wrapper for a physics engine (www.trueaxis.com). I finally became fed up at maintaining the JNI/C++ code for each new version of TrueAxis, but badly wanted the new features! So, I endeavor to port, perhaps slightly redesign, my Java engine in C++. I think I'm mostly aware of the fundamental differences between Java and C++, especially with regards to memory management. Except, I find myself paraniod that I may be violating some C++ taboos with my working code so far. So, if you have a free moment, could you spend some time to critique my classes? I'm using Windows XP Pro sp2, Visual C++ 2005 Express and SDL. One thing I liked about developing in Java, was the seamless platform portability. So I'd like to try to remain compatible with Linux in my new engine, also since TrueAxis now has Linux support as well. So the first thing I wanted to do was wrap SDL's nasty display code away. I did so in a callback method reminiscent of JOGL or even OGRE. SDLCallback.h SDLDisplay.h SDLDisplay.cpp Now I wanted to get an FPS read out on the screen so I created a few new classes. FontManager.h TextManager.h And the main implementation of the SDLCallback main.cpp I have a few questions: 1) Is there anything "wrong" with how I've programmed these functions? Anything that inspires that ugly feeling in you? 2) Am I leaking memory everywhere? I try to ensure that every new has a delete, or SDL_Init has it's SDL_Quit. Do I need to worry about the stl containers I've used? 3) Do I over use the STL containers? 4) Am I hopelessly tied to windows already, or is there some hope for linux compatibility? Anything else... Thanks for any insight you guys can provide. And if by chance any of this stuff is useful, feel free to steal the code to your heart's content.
#ifndef SDLCALLBACK_H
#define SDLCALLBACK_H
class SDLCallback {
public:
/* Called when GL and SDL are fully initialized */
virtual void Initialize () = 0;
/* Called every frame */
virtual void Render () = 0;
/* Called when screen is resized */
virtual void DisplayResized (int width, int height) = 0;
/* Called when Iconified/Deiconified or Device changes */
virtual void DisplayChanged (bool active, bool devicelost) = 0;
/* Called at destruction phase while SDL and GL are valid */
virtual void DisplayReleasing () = 0;
/* Called when SDL quit message is received */
virtual void DisplayCloseRequested () = 0;
/* Called when the display is closed SDL and GL are not initialized */
virtual void DisplayClosed () = 0;
};
#endif
#ifndef SDLDISPLAY_H
#define SDLDISPLAY_H
#include <string>
#include <vector>
using namespace std;
#include <SDL.h>
#include "SDLCallback.h"
class SDLDisplay {
public:
SDLDisplay ();
~SDLDisplay ();
void SetDefaultDisplay ();
void SetWidth(int value) { width = value; }
void SetHeight(int value) { height = value; }
void SetFrequency(int value) { frequency = value; }
void SetColorDepth(int value) { colorDepth = value; }
void SetX(int value) { x = value; }
void SetY(int value) { y = value; }
void SetAlpha(int value) { alpha = value; }
void SetDepth(int value) { depth = value; }
void SetStencil(int value) { stencil = value; }
void SetFullscreen(bool value) { fullscreen = value; }
void SetUndecorated(bool value) { undecorated = value; }
void SetVsync(bool value) { vsync = value; }
void AddListener (SDLCallback* listener);
void Create ();
void Destroy ();
int GetWidth () { return width; }
int GetHeight () { return height; }
private:
SDL_Surface *screen;
SDL_Event event;
bool running;
int width, height, frequency, colorDepth, x, y;
int alpha, depth, stencil, samples, numAuxBuffers, accumBpp, accumAlpha;
bool created, fullscreen, undecorated, vsync, stereo;
std::string title;
std::vector<SDLCallback*> callbacks;
};
#endif
#include "SDLDisplay.h"
#include "SDLCallback.h"
SDLDisplay::SDLDisplay () {
this->SetDefaultDisplay();
}
SDLDisplay::~SDLDisplay() {
}
void SDLDisplay::Create() {
created = true;
running = true;
const SDL_VideoInfo *videoInfo;
int result;
result = SDL_Init(SDL_INIT_VIDEO);
if (result < 0) Destroy();
videoInfo = SDL_GetVideoInfo();
if (!videoInfo) Destroy();
int sdlFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE | SDL_RESIZABLE | SDL_HWSURFACE | SDL_HWACCEL;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
screen = SDL_SetVideoMode(this->width, this->height, this->colorDepth, sdlFlags);
if (!screen) Destroy();
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->Initialize();
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->DisplayResized(width, height);
while (running) {
while (SDL_PollEvent (&event)) {
switch (event.type) {
case SDL_ACTIVEEVENT:
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->DisplayChanged(event.active.gain != 0, false);
break;
case SDL_VIDEORESIZE:
screen = SDL_SetVideoMode(event.resize.w, event.resize.h, colorDepth, sdlFlags);
width = event.resize.w;
height = event.resize.h;
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->DisplayResized(width, height);
break;
case SDL_KEYDOWN:
break;
case SDL_QUIT:
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->DisplayCloseRequested();
break;
}
}
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->Render();
SDL_GL_SwapBuffers( );
}
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->DisplayReleasing();
SDL_Quit();
for (unsigned int i = 0; i < callbacks.size(); i++)
callbacks->DisplayClosed();
exit(0);
}
void SDLDisplay::Destroy () {
running = false;
}
void SDLDisplay::SetDefaultDisplay() {
width = 1280;
height = 1024;
frequency = 75;
colorDepth = 32;
alpha = 8;
stencil = 8;
depth = 8;
vsync = false;
fullscreen = false;
}
void SDLDisplay::AddListener(SDLCallback *listener) {
this->callbacks.push_back(listener);
}
#ifndef FONTMANAGER_H
#define FONTMANAGER_H
#include "SDL_ttf.h"
#include <map>
#include <string>
using namespace std;
class FontManager {
public:
class Font {
public:
TextureData* textureData;
vector<int>* charWidths;
TTF_Font* font;
Font (TextureData* textureData, vector<int>* charWidths, TTF_Font* font) {
this->textureData = textureData;
this->charWidths = charWidths;
this->font = font;
}
~Font () {
TTF_CloseFont(font);
delete charWidths;
delete textureData;
}
};
std::string characters;
FontManager () {
characters.append(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
TTF_Init();
}
~FontManager () {
TTF_Quit();
}
Font* LoadFont (std::string filename) {
unsigned int textureDim = 1024;
unsigned int charDim = 64;
SDL_Color color = {255, 255, 255, 255};
TTF_Font* font = TTF_OpenFont(filename.c_str(), 48);
SDL_Surface* fontImage = SDL_CreateRGBSurface(SDL_SWSURFACE, textureDim, textureDim, 24, 0xff000000, 0x00ff0000, 0x0000ff00, 0);
SDL_SetAlpha(fontImage, SDL_SRCALPHA, 0);
int r2 = SDL_FillRect(fontImage, &fontImage->clip_rect, SDL_MapRGBA(fontImage->format, 0, 0, 0, 0));
std::vector<int>* charWidths = new std::vector<int>;
unsigned int charIdx = 0;
for (unsigned int i = 0; i < textureDim; i += charDim) {
for (unsigned int j = 0; j < textureDim; j += charDim, charIdx++) {
if (charIdx > characters.size()) break;
char c = characters[charIdx];
std::string cs;
cs += c;
int fw, fh;
TTF_SizeText(font, cs.c_str(), &fw, &fh);
charWidths->push_back(fw);
SDL_Surface* fontSurf = TTF_RenderText_Blended(font, cs.c_str(), color);
SDL_Rect destRect;
destRect.x = j;
destRect.y = i;
destRect.w = fw;
destRect.h = fh;
int ret = SDL_BlitSurface(fontSurf, &fontSurf->clip_rect, fontImage, &destRect);
SDL_FreeSurface(fontSurf);
}
}
TextureData* td = new TextureData(fontImage);
return new Font (td, charWidths, font);
}
};
#endif
#ifndef TEXTMANAGER_H
#define TEXTMANAGER_H
#include <map>
#include <vector>
using namespace std;
class TextManager {
public:
class Text {
public:
bool show;
std::string message;
float x, y;
float xscale, yscale;
float r, g, b, a;
FontManager::Font* font;
Text () {
r = 1;
g = 1;
b = 1;
a = 1;
xscale = 1;
yscale = 1;
x = 0;
y = 0;
show = true;
}
~Text () {
}
};
TextManager () {
FONT_DIM = 16;
}
void Render () {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
glActiveTexture(GL_TEXTURE0);
for (unsigned int i = 0; i < texts.size(); i++) {
RenderText(texts);
}
glDisable(GL_BLEND);
}
void Add (TextManager::Text* t) {
texts.push_back(t);
}
void AddFont (FontManager::Font* f) {
Texture* t = new Texture(f->textureData);
int listBase = glGenLists(8 * 16);
unsigned int cnt = 0;
std::vector<int>* charWidths = f->charWidths;
for (unsigned int y = 0; y < 8; y++) {
for (unsigned int x = 0; x < 16; x++, cnt++) {
glNewList(listBase + cnt, GL_COMPILE);
float fdim = FONT_DIM;
if (cnt < charWidths->size()) {
//fdim = (float) charWidths->at(cnt);
}
float uoffset = (float) x / FONT_DIM;
uoffset += fdim / FONT_DIM / FONT_DIM;
glBegin(GL_QUADS);
glMultiTexCoord2f(GL_TEXTURE0, (float) x / FONT_DIM, (float) y / FONT_DIM);
glVertex2f(0, 0);
glMultiTexCoord2f(GL_TEXTURE0, (float) x / FONT_DIM, (float) (y+1) / FONT_DIM);
glVertex2f(0, FONT_DIM);
glMultiTexCoord2f(GL_TEXTURE0, uoffset, (float) (y+1) / FONT_DIM);
glVertex2f(fdim, FONT_DIM);
glMultiTexCoord2f(GL_TEXTURE0, uoffset, (float) y / FONT_DIM);
glVertex2f(fdim, 0);
glEnd();
if (cnt < charWidths->size()) {
glTranslatef(fdim, 0, 0);
} else {
glTranslatef(FONT_DIM, 0, 0);
}
glEndList();
}
}
fontTextures[f] = t;
fontBase[f] = listBase;
}
private:
std::vector<TextManager::Text*> texts;
std::map<FontManager::Font*, Texture*> fontTextures;
std::map<FontManager::Font*, int> fontBase;
float FONT_DIM;
void RenderText (Text* t) {
if (t->show){
fontTextures[t->font]->Bind();
glListBase(-32 + fontBase[t->font]);
glColor4f(t->r, t->g, t->b, t->a);
glPushMatrix();
glTranslatef(t->x, t->y, 0);
glScalef(t->xscale, t->yscale, 1);
glCallLists(t->message.size(), GL_UNSIGNED_BYTE, t->message.c_str());
glPopMatrix();
}
}
};
#endif
#include <SDL.h>
#pragma comment (lib, "SDLmain.lib")
#pragma comment (lib, "SDL.lib")
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")
#pragma comment (lib, "SDL_image.lib")
#pragma comment (lib, "SDL_ttf.lib")
#include <windows.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <sstream>
#include "SDLDisplay.h"
#include "TextureData.h"
#include "Texture.h"
#include "FontManager.h"
#include "TextManager.h"
#include "Timer.h"
class ListenTest : public SDLCallback {
private:
SDLDisplay* sd;
public:
TextureData* td;
Texture* t;
FontManager* fm;
TextManager* textManager;
TextManager::Text* FpsText;
Timer* frameTimer;
ListenTest () {
td = new TextureData(std::string("test.png"), TextureData::TextureType::PNG);
fm = new FontManager();
sd = new SDLDisplay;
sd->SetDefaultDisplay();
sd->SetWidth(1024);
sd->SetHeight(768);
sd->AddListener(this);
sd->Create();
}
void Initialize () {
GLenum err = glewInit();
FontManager::Font* font = fm->LoadFont("tangle.ttf");
textManager = new TextManager();
textManager->AddFont(font);
FpsText = new TextManager::Text();
FpsText->font = font;
FpsText->message.append("Rawr!");
FpsText->x = 10;
FpsText->y = 10;
textManager->Add(FpsText);
glClearColor(1, 0, 0, 0.2f);
frameTimer = new Timer();
t = new Texture(td);
}
void Render () {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat ratio = (GLfloat)sd->GetWidth() / (GLfloat)sd->GetHeight();
glViewport(0, 0, (GLsizei)sd->GetWidth(), (GLsizei)sd->GetHeight());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, ratio, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 4, 2, 0, 0, 0, 0, 1, 0);
t->Bind();
glBegin(GL_QUADS);
glTexCoord2f(1, 1);
glVertex3f(-1, 0, -1);
glTexCoord2f(1, 0);
glVertex3f(-1, 0, 1);
glTexCoord2f(0, 0);
glVertex3f(1, 0, 1);
glTexCoord2f(0, 1);
glVertex3f(1, 0, -1);
glEnd();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, sd->GetWidth(), sd->GetHeight(), 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
textManager->Render();
glEnable(GL_DEPTH_TEST);
glPopMatrix();
double frameTime = 1 / frameTimer->GetElapsedSeconds();
ostringstream sstest;
sstest << frameTime;
FpsText->message.clear();
FpsText->message.append(sstest.str());
glFinish();
frameTimer->Update();
}
void DisplayResized (int width, int height) {
}
void DisplayChanged (bool active, bool lost) {
}
void DisplayReleasing () {
delete t;
delete td;
delete FpsText;
delete textManager;
delete fm;
}
void DisplayCloseRequested () {
sd->Destroy();
}
void DisplayClosed () {
}
};
int main(int argc, char* argv[])
{
ListenTest* lt = new ListenTest;
delete lt;
return 0;
}