/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS #include "graphics_engine.h" #include "resource_manager.h" #include "event_handler.h" #include "state.h" #include "world_entity.h" #include "render_2d.h" #include "text_engine.h" #include "light.h" #include "shader.h" #include "debug.h" #include "parser/ini_parser/ini_parser.h" #include "substring.h" #include "text.h" #include "globals.h" #include "texture.h" #include "effects/graphics_effect.h" #include "shell_command.h" #ifdef __WIN32__ #include "class_list.h" #include "static_model.h" #endif using namespace std; SHELL_COMMAND(wireframe, GraphicsEngine, wireframe); /** * standard constructor */ GraphicsEngine::GraphicsEngine () { this->setClassID(CL_GRAPHICS_ENGINE, "GraphicsEngine"); this->setName("GraphicsEngine"); this->isInit = false; this->bDisplayFPS = false; this->minFPS = 9999; this->maxFPS = 0; this->geTextCFPS = NULL; this->geTextMaxFPS = NULL; this->geTextMinFPS = NULL; this->fullscreenFlag = 0; this->videoFlags = 0; this->screen = NULL; // Hardware this->hwRenderer = NULL; this->hwVendor = NULL; this->hwVersion = NULL; this->hwExtensions = NULL; // initialize the TextEngine TextEngine::getInstance(); } /** * The Pointer to this GraphicsEngine */ GraphicsEngine* GraphicsEngine::singletonRef = NULL; /** * destructs the graphicsEngine. */ GraphicsEngine::~GraphicsEngine () { // delete what has to be deleted here delete this->geTextCFPS; delete this->geTextMaxFPS; delete this->geTextMinFPS; delete[] this->hwRenderer; delete[] this->hwVendor; delete[] this->hwVersion; delete this->hwExtensions; //TextEngine delete TextEngine::getInstance(); // render 2D delete Render2D::getInstance(); SDL_QuitSubSystem(SDL_INIT_VIDEO); // if (this->screen != NULL) // SDL_FreeSurface(this->screen); GraphicsEngine::singletonRef = NULL; } /** * initializes the GraphicsEngine with default settings. */ int GraphicsEngine::init() { if (this->isInit) return -1; this->initVideo(640, 480, 16); } /** * loads the GraphicsEngine's settings from a given ini-file and section * @param iniParser the iniParser to load from * @param section the Section in the ini-file to load from * @returns nothing usefull */ int GraphicsEngine::initFromIniFile(IniParser* iniParser) { // looking if we are in fullscreen-mode const char* fullscreen = iniParser->getVar(CONFIG_NAME_FULLSCREEN, CONFIG_SECTION_VIDEO, "0"); if (strchr(fullscreen, '1') || !strcasecmp(fullscreen, "true")) this->fullscreenFlag = SDL_FULLSCREEN; // looking if we are in fullscreen-mode const char* textures = iniParser->getVar(CONFIG_NAME_TEXTURES, CONFIG_SECTION_VIDEO_ADVANCED, "0"); if (strchr(textures, '1') || !strcasecmp(textures, "true")) Texture::setTextureEnableState( true); else Texture::setTextureEnableState(false); // searching for a usefull resolution SubString resolution(iniParser->getVar(CONFIG_NAME_RESOLUTION, CONFIG_SECTION_VIDEO, "640x480"), 'x'); //resolution.debug(); this->initVideo(atoi(resolution.getString(0)), atoi(resolution.getString(1)), 16); } /** * initializes the Video for openGL. * * This has to be done only once when starting orxonox. */ int GraphicsEngine::initVideo(unsigned int resX, unsigned int resY, unsigned int bbp) { if (this->isInit) return -1; // initialize SDL_VIDEO if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { PRINTF(1)("could not initialize SDL Video\n"); // return -1; } // initialize SDL_GL-settings this->setGLattribs(); // setting the Video Flags. this->videoFlags = SDL_OPENGL | SDL_HWPALETTE | SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_GL_DOUBLEBUFFER; /* query SDL for information about our video hardware */ const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo (); if( videoInfo == NULL) { PRINTF(1)("Failed getting Video Info :%s\n", SDL_GetError()); SDL_Quit (); } if( videoInfo->hw_available) this->videoFlags |= SDL_HWSURFACE; else this->videoFlags |= SDL_SWSURFACE; /* if(VideoInfo -> blit_hw) VideoFlags |= SDL_HWACCEL; */ // setting up the Resolution this->setResolution(resX, resY, bbp); // GRABBING ALL GL-extensions this->grabHardwareSettings(); // Enable default GL stuff glEnable(GL_DEPTH_TEST); Render2D::getInstance(); this->isInit = true; } /** * sets the Window Captions and the Name of the icon. * @param windowName The name of the Window * @param icon The name of the Icon on the Disc */ void GraphicsEngine::setWindowName(const char* windowName, const char* icon) { SDL_Surface* iconSurf = SDL_LoadBMP(icon); if (iconSurf != NULL) { Uint32 colorkey = SDL_MapRGB(iconSurf->format, 0, 0, 0); SDL_SetColorKey(iconSurf, SDL_SRCCOLORKEY, colorkey); SDL_WM_SetIcon(iconSurf,NULL); SDL_FreeSurface(iconSurf); } SDL_WM_SetCaption (windowName, icon); } /** * Sets the GL-attributes */ int GraphicsEngine::setGLattribs() { // Set video mode // TO DO: parse arguments for settings //SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); //SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); //SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); //SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0); SDL_GL_SetAttribute( SDL_GL_ACCUM_RED_SIZE, 0); SDL_GL_SetAttribute( SDL_GL_ACCUM_GREEN_SIZE, 0); SDL_GL_SetAttribute( SDL_GL_ACCUM_BLUE_SIZE, 0); SDL_GL_SetAttribute( SDL_GL_ACCUM_ALPHA_SIZE, 0); } /** * grabs the Hardware Specifics * checks for all the different HW-types */ void GraphicsEngine::grabHardwareSettings() { const char* renderer = (const char*) glGetString(GL_RENDERER); const char* vendor = (const char*) glGetString(GL_VENDOR); const char* version = (const char*) glGetString(GL_VERSION); const char* extensions = (const char*) glGetString(GL_EXTENSIONS); // printf("%s %s %s\n %s", renderer, vendor, version, extensions); if (this->hwRenderer == NULL && renderer != NULL) { this->hwRenderer = new char[strlen(renderer)+1]; strcpy(this->hwRenderer, renderer); } if (this->hwVendor == NULL && vendor != NULL) { this->hwVendor = new char[strlen(vendor)+1]; strcpy(this->hwVendor, vendor); } if (this->hwVersion == NULL && version != NULL) { this->hwVersion = new char[strlen(version)+11]; strcpy(this->hwVersion, version); } if (this->hwExtensions == NULL && extensions != NULL) this->hwExtensions = new SubString((char*)glGetString(GL_EXTENSIONS), " \n\t,"); PRINT(4)("Running on : vendor: %s, renderer: %s, version:%s\n", vendor, renderer, version); PRINT(4)("Extensions:\n"); if (this->hwExtensions != NULL) for (unsigned int i = 0; i < this->hwExtensions->getCount(); i++) PRINT(4)("%d: %s\n", i, this->hwExtensions->getString(i)); // inizializing GLEW GLenum err = glewInit(); if (GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ PRINTF(1)("%s\n", glewGetErrorString(err)); } PRINTF(4)("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); } /** * sets the Resolution of the Screen to display the Graphics to. * @param width The width of the window * @param height The height of the window * @param bpp bits per pixel */ int GraphicsEngine::setResolution(int width, int height, int bpp) { this->resolutionX = width; this->resolutionY = height; this->bitsPerPixel = bpp; State::setResolution( width, height); if (this->screen != NULL) SDL_FreeSurface(screen); if((this->screen = SDL_SetVideoMode(this->resolutionX, this->resolutionY, this->bitsPerPixel, this->videoFlags | this->fullscreenFlag)) == NULL) { PRINTF(1)("Could not SDL_SetVideoMode(%d, %d, %d, %d): %s\n", this->resolutionX, this->resolutionY, this->bitsPerPixel, this->videoFlags, SDL_GetError()); // SDL_Quit(); // return -1; } glMatrixMode(GL_PROJECTION_MATRIX); glLoadIdentity(); glViewport(0, 0, width, height); // Reset The Current Viewport #ifdef __WIN32__ // REBUILDING TEXTURES (ON WINDOWS CONTEXT SWITCH) const std::list* texList = ClassList::getList(CL_TEXTURE); if (texList != NULL) { std::list::const_iterator reTex; for (reTex = texList->begin(); reTex != texList->end(); reTex++) dynamic_cast(*reTex)->rebuild(); } // REBUILDING MODELS const std::list* modelList = ClassList::getList(CL_STATIC_MODEL); if (texList != NULL) { std::list::const_iterator reModel; for (reModel = modelList->begin(); reModel != modelList->end(); reModel++) dynamic_cast(*reModel)->rebuild(); } #endif /* __WIN32__ */ } /** * sets Fullscreen mode * @param fullscreen true if fullscreen, false if windowed */ void GraphicsEngine::setFullscreen(bool fullscreen) { if (fullscreen) this->fullscreenFlag = SDL_FULLSCREEN; else this->fullscreenFlag = 0; this->setResolution(this->resolutionX, this->resolutionY, this->bitsPerPixel); } /** * sets the background color * @param red the red part of the background * @param blue the blue part of the background * @param green the green part of the background * @param alpha the alpha part of the background */ void GraphicsEngine::setBackgroundColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { glClearColor(red, green, blue, alpha); } /** * Signalhandler, for when the resolution has changed * @param resizeInfo SDL information about the size of the new screen size */ int GraphicsEngine::resolutionChanged(const SDL_ResizeEvent& resizeInfo) { this->setResolution(resizeInfo.w, resizeInfo.h, this->bitsPerPixel); } /** * * @param show if The mouse-cursor should be visible */ void GraphicsEngine::showMouse(bool show) { if (show) SDL_ShowCursor(SDL_ENABLE); else SDL_ShowCursor(SDL_DISABLE); } /** * * @returns The Visinility of the mouse-cursor (true if visible, false if it is invisible) */ bool GraphicsEngine::isMouseVisible() { if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) return true; else return false; } /** * * @param steal If the Winodow-Managers Events should be stolen to this app * (steals the mouse, and all WM-clicks) * * This only happens, if the HARD-Debug-level is set to 0,1,2, because otherwise a Segfault could * result in the loss of System-controll */ void GraphicsEngine::stealWMEvents(bool steal) { #if DEBUG < 3 if (steal) SDL_WM_GrabInput(SDL_GRAB_ON); else SDL_WM_GrabInput(SDL_GRAB_OFF); #endif } /** * * @returns true if Events are stolen from the WM, false if not. */ bool GraphicsEngine::isStealingEvents() { if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) return true; else return false; }; /** * entering 2D Mode this is a GL-Projection-mode, that is orthogonal, for placing the font in fron of everything else */ void GraphicsEngine::enter2DMode() { //GraphicsEngine::storeMatrices(); SDL_Surface *screen = SDL_GetVideoSurface(); /* Note, there may be other things you need to change, depending on how you have your OpenGL state set up. */ glPushAttrib(GL_ENABLE_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); // will be set back when leaving 2D-mode glViewport(0, 0, screen->w, screen->h); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, (GLdouble)screen->w, (GLdouble)screen->h, 0.0, 0.0, 1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } /** * leaves the 2DMode again also @see Font::enter2DMode() */ void GraphicsEngine::leave2DMode() { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); } void GraphicsEngine::wireframe() { glPolygonMode(GL_FRONT, GL_LINE); } /** * stores the GL_matrices */ void GraphicsEngine::storeMatrices() { glGetDoublev(GL_PROJECTION_MATRIX, GraphicsEngine::projMat); glGetDoublev(GL_MODELVIEW_MATRIX, GraphicsEngine::modMat); glGetIntegerv(GL_VIEWPORT, GraphicsEngine::viewPort); } //! the stored ModelView Matrix. GLdouble GraphicsEngine::modMat[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //! the stored Projection Matrix GLdouble GraphicsEngine::projMat[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //! The ViewPort GLint GraphicsEngine::viewPort[4] = {0,0,0,0}; /** * outputs all the Fullscreen modes. */ void GraphicsEngine::listModes() { /* Get available fullscreen/hardware modes */ this->videoModes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); /* Check is there are any modes available */ if(this->videoModes == (SDL_Rect **)0){ PRINTF(1)("No modes available!\n"); exit(-1); } /* Check if our resolution is restricted */ if(this->videoModes == (SDL_Rect **)-1){ PRINTF(2)("All resolutions available.\n"); } else{ /* Print valid modes */ PRINT(0)("Available Resoulution Modes are\n"); for(int i = 0; this->videoModes[i]; ++i) PRINT(4)(" | %d x %d\n", this->videoModes[i]->w, this->videoModes[i]->h); } } /** * checks wether a certain extension is availiable * @param extension the Extension to check for (ex. GL_ARB_texture_env_dot3) * @return true if it is, false otherwise */ bool GraphicsEngine::hwSupportsEXT(const char* extension) { if (this->hwExtensions != NULL) for (unsigned int i = 0; i < this->hwExtensions->getCount(); i++) if (!strcmp(extension, this->hwExtensions->getString(i))) return true; return false; } /** * updates everything that is to be updated in the GraphicsEngine */ void GraphicsEngine::update(float dt) { Render2D::getInstance()->update(dt); } /** * ticks the Text * @param dt the time passed */ void GraphicsEngine::tick(float dt) { if( unlikely(this->bDisplayFPS)) { this->currentFPS = 1.0/dt; if( unlikely(this->currentFPS > this->maxFPS)) this->maxFPS = this->currentFPS; if( unlikely(this->currentFPS < this->minFPS)) this->minFPS = this->currentFPS; #ifndef NO_TEXT char tmpChar1[20]; sprintf(tmpChar1, "Current: %4.0f", this->currentFPS); this->geTextCFPS->setText(tmpChar1); char tmpChar2[20]; sprintf(tmpChar2, "Max: %4.0f", this->maxFPS); this->geTextMaxFPS->setText(tmpChar2); char tmpChar3[20]; sprintf(tmpChar3, "Min: %4.0f", this->minFPS); this->geTextMinFPS->setText(tmpChar3); #endif /* NO_TEXT */ } Render2D::getInstance()->tick(dt); } void GraphicsEngine::draw() const { LightManager::getInstance()->draw(); GraphicsEngine::storeMatrices(); Shader::suspendShader(); Render2D::getInstance()->draw(E2D_LAYER_ALL); Shader::restoreShader(); } void GraphicsEngine::draw(const std::list& drawList ) const { std::list::const_iterator entity; for (entity = drawList.begin(); entity != drawList.end(); entity++) if ((*entity)->isVisible()) (*entity)->draw(); } /** * displays the Frames per second * @param display if the text should be displayed */ void GraphicsEngine::displayFPS(bool display) { #ifndef NO_TEXT if( display ) { if (this->geTextCFPS == NULL) { this->geTextCFPS = new Text("fonts/arial_black.ttf", 15); this->geTextCFPS->setName("curFPS"); this->geTextCFPS->setAlignment(TEXT_ALIGN_LEFT); this->geTextCFPS->setAbsCoor2D(5, 0); } if (this->geTextMaxFPS == NULL) { this->geTextMaxFPS = new Text("fonts/arial_black.ttf", 15); this->geTextMaxFPS->setName("MaxFPS"); this->geTextMaxFPS->setAlignment(TEXT_ALIGN_LEFT); this->geTextMaxFPS->setAbsCoor2D(5, 20); } if (this->geTextMinFPS == NULL) { this->geTextMinFPS = new Text("fonts/arial_black.ttf", 15); this->geTextMinFPS->setName("MinFPS"); this->geTextMinFPS->setAlignment(TEXT_ALIGN_LEFT); this->geTextMinFPS->setAbsCoor2D(5, 40); } } else { delete this->geTextCFPS; this->geTextCFPS = NULL; delete this->geTextMaxFPS; this->geTextMaxFPS = NULL; delete this->geTextMinFPS; this->geTextMinFPS = NULL; } this->bDisplayFPS = display; #else this->bDisplayFPS = false; #endif /* NO_TEXT */ } /** processes the events for the GraphicsEngine class * @param the event to handle */ void GraphicsEngine::process(const Event &event) { switch (event.type) { case EV_VIDEO_RESIZE: this->resolutionChanged(event.resize); break; } } /** * loads a GraphicsEffect into the engine * @param effect the GraphicsEffect to add */ bool GraphicsEngine::loadGraphicsEffect(GraphicsEffect* effect) { list::iterator it; for (it = this->graphicsEffects.begin(); it != this->graphicsEffects.end(); it++) if( (*it) == effect) return false; this->graphicsEffects.push_back(effect); return true; } /** * unloads a GraphicsEffect from the engine * @param effect the GraphicsEffect to remove */ bool GraphicsEngine::unloadGraphicsEffect(GraphicsEffect* effect) { list::iterator it; for (it = this->graphicsEffects.begin(); it != this->graphicsEffects.end(); it++) { if( (*it) == effect) { this->graphicsEffects.erase(it); return true; } } return false; }