/* 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 "util/loading/resource_manager.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 "util/preferences.h" #include "substring.h" #include "text.h" #include "globals.h" #include "texture.h" #include "effects/graphics_effect.h" #include "effects/fog_effect.h" #include "effects/lense_flare.h" #include "shell_command.h" #include "parser/tinyxml/tinyxml.h" #include "util/loading/load_param.h" #include "util/loading/factory.h" #include "class_list.h" #ifdef __WIN32__ #include "static_model.h" #endif SHELL_COMMAND(wireframe, GraphicsEngine, wireframe); SHELL_COMMAND(fps, GraphicsEngine, toggleFPSdisplay); /** * @brief standard constructor */ GraphicsEngine::GraphicsEngine () { this->setClassID(CL_GRAPHICS_ENGINE, "GraphicsEngine"); this->setName("GraphicsEngine"); this->isInit = false; this->bDisplayFPS = false; this->bAntialiasing = false; this->bDedicated = 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; // initialize the Modules TextEngine::getInstance(); this->graphicsEffects = NULL; } /** * @brief The Pointer to this GraphicsEngine */ GraphicsEngine* GraphicsEngine::singletonRef = NULL; /** * @brief destructs the graphicsEngine. */ GraphicsEngine::~GraphicsEngine () { // delete what has to be deleted here this->displayFPS( false ); //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; } /** * @brief loads the GraphicsEngine Specific Parameters. * @param root: the XML-Element to load the Data From */ void GraphicsEngine::loadParams(const TiXmlElement* root) { LoadParamXML(root, "GraphicsEffect", this, GraphicsEngine, loadGraphicsEffects) .describe("loads a graphics effect"); } /** * @param root The XML-element to load GraphicsEffects from */ void GraphicsEngine::loadGraphicsEffects(const TiXmlElement* root) { LOAD_PARAM_START_CYCLE(root, element); { PRINTF(4)("element is: %s\n", element->Value()); Factory::fabricate(element); } LOAD_PARAM_END_CYCLE(element); } /** * @brief initializes the GraphicsEngine with default settings. */ int GraphicsEngine::init() { if (this->isInit) return -1; this->initVideo(640, 480, 16); return 1; } /** * @brief loads the GraphicsEngine's settings from a given ini-file and section * @returns nothing usefull */ int GraphicsEngine::initFromPreferences() { // looking if we are in fullscreen-mode MultiType fullscreen = Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO, CONFIG_NAME_FULLSCREEN, "0"); if (fullscreen.getBool()) this->fullscreenFlag = SDL_FULLSCREEN; // looking if we are in fullscreen-mode MultiType textures = Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO_ADVANCED, CONFIG_NAME_TEXTURES, "1"); Texture::setTextureEnableState(textures.getBool()); // check it is a dedicated network node: so no drawings are made MultiType dedicated = Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO, CONFIG_NAME_NO_RENDER, "0"); this->bDedicated = dedicated.getBool(); // searching for a usefull resolution SubString resolution(Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO, CONFIG_NAME_RESOLUTION, "640x480").c_str(), 'x'); ///FIXME //resolution.debug(); MultiType x = resolution.getString(0), y = resolution.getString(1); return this->initVideo(x.getInt(), y.getInt(), 16); // GraphicsEffect* fe = new FogEffect(NULL); // this->loadGraphicsEffect(fe); // fe->activate(); // PRINTF(0)("--------------------------------------------------------------\n"); //LenseFlare* ge = new LenseFlare(); //this->loadGraphicsEffect(ge); //ge->addFlare("pictures/lense_flare/sun.png"); //sun //ge->addFlare("pictures/lense_flare/lens2.png"); //first halo //ge->addFlare("pictures/lense_flare/lens1.png"); //small birst //ge->addFlare("pictures/lense_flare/lens3.png"); //second halo //ge->addFlare("pictures/lense_flare/lens4.png"); //ge->addFlare("pictures/lense_flare/lens1.png"); //ge->addFlare("pictures/lense_flare/lens3.png"); //ge->activate(); } /** * @brief 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 ; /* 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); glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); Render2D::getInstance(); this->isInit = true; return 1; } /** * @brief 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 std::string& windowName, const std::string& icon) { SDL_Surface* iconSurf = SDL_LoadBMP(icon.c_str()); 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.c_str(), icon.c_str()); } /** * @brief Sets the GL-attributes */ void 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); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); //Use at least 5 bits of Red SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); //Use at least 5 bits of Green SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); //Use at least 5 bits of Blue SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); //Use at least 16 bits for the depth buffer SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //Enable double buffering // enable antialiasing? if( this->bAntialiasing) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,4); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,1); } } /** * @brief 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 (renderer != NULL) { this->hwRenderer == renderer; } if (vendor != NULL) { this->hwVendor == vendor; } if (version != NULL) { this->hwVersion == version; } if (extensions != NULL) this->hwExtensions.split(extensions, " \n\t,"); PRINT(4)("Running on : vendor: %s, renderer: %s, version:%s\n", vendor, renderer, version); PRINT(4)("Extensions:\n"); for (unsigned int i = 0; i < this->hwExtensions.size(); i++) PRINT(4)("%d: %s\n", i, this->hwExtensions[i].c_str()); // 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)); } /** * @brief 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; return -1; } 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__ */ return 1; } /** * @brief 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); } void GraphicsEngine::toggleFullscreen() { if (this->fullscreenFlag == SDL_FULLSCREEN) this->fullscreenFlag = 0; else this->fullscreenFlag = SDL_FULLSCREEN; this->setResolution(this->resolutionX, this->resolutionY, this->bitsPerPixel); } /** * @brief 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); } /** * @brief Signalhandler, for when the resolution has changed * @param resizeInfo SDL information about the size of the new screen size */ void GraphicsEngine::resolutionChanged(const SDL_ResizeEvent& resizeInfo) { this->setResolution(resizeInfo.w, resizeInfo.h, this->bitsPerPixel); } /** * @brief 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 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(); } /** * @brief leaves the 2DMode again also @see Font::enter2DMode() */ void GraphicsEngine::leave2DMode() { glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } /** * @brief changes to wireframe-mode. */ void GraphicsEngine::wireframe() { glPolygonMode(GL_FRONT, GL_LINE); } /** * @brief 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}; /** * @brief 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); } } /** * @brief 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 std::string& extension) { for (unsigned int i = 0; i < this->hwExtensions.size(); i++) if ( this->hwExtensions.getString(i) == extension) return true; return false; } /** * @brief updates everything that is to be updated in the GraphicsEngine */ void GraphicsEngine::update(float dt) { Render2D::getInstance()->update(dt); } /** * @brief 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); // tick the graphics effects if (this->graphicsEffects != NULL || (this->graphicsEffects = ClassList::getList(CL_GRAPHICS_EFFECT)) != NULL) { std::list::const_iterator it; for (it = this->graphicsEffects->begin(); it != this->graphicsEffects->end(); it++) dynamic_cast(*it)->tick(dt); } } /** * @brief draws all Elements that should be displayed on the Background. */ void GraphicsEngine::drawBackgroundElements() const { GraphicsEngine::storeMatrices(); Render2D::getInstance()->draw(E2D_LAYER_BELOW_ALL, E2D_LAYER_BELOW_ALL); } /** * this draws the graphics engines graphics effecs */ void GraphicsEngine::draw() const { if( this->graphicsEffects != NULL) { //draw the graphics effects std::list::const_iterator it; for (it = this->graphicsEffects->begin(); it != this->graphicsEffects->end(); it++) dynamic_cast(*it)->draw(); } Shader::suspendShader(); Render2D::getInstance()->draw(E2D_LAYER_BOTTOM, E2D_LAYER_ABOVE_ALL); Shader::restoreShader(); } void GraphicsEngine::toggleFPSdisplay() { this->displayFPS(!this->bDisplayFPS); } /** * @brief 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 */ } /** * @brief 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; } }