| 1 | /* | 
|---|
| 2 |  *   ORXONOX - the hottest 3D action shooter ever to exist | 
|---|
| 3 |  *                    > www.orxonox.net < | 
|---|
| 4 |  * | 
|---|
| 5 |  * | 
|---|
| 6 |  *   License notice: | 
|---|
| 7 |  * | 
|---|
| 8 |  *   This program is free software; you can redistribute it and/or | 
|---|
| 9 |  *   modify it under the terms of the GNU General Public License | 
|---|
| 10 |  *   as published by the Free Software Foundation; either version 2 | 
|---|
| 11 |  *   of the License, or (at your option) any later version. | 
|---|
| 12 |  * | 
|---|
| 13 |  *   This program is distributed in the hope that it will be useful, | 
|---|
| 14 |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 15 |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 16 |  *   GNU General Public License for more details. | 
|---|
| 17 |  * | 
|---|
| 18 |  *   You should have received a copy of the GNU General Public License | 
|---|
| 19 |  *   along with this program; if not, write to the Free Software | 
|---|
| 20 |  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
|---|
| 21 |  * | 
|---|
| 22 |  *   Author: | 
|---|
| 23 |  *      Gion-Andri Cantieni | 
|---|
| 24 |  *   Co-authors: | 
|---|
| 25 |  *      Damian 'Mozork' Frick | 
|---|
| 26 |  * | 
|---|
| 27 |  */ | 
|---|
| 28 |  | 
|---|
| 29 | /** | 
|---|
| 30 |     @file SkyboxGenerator.cc | 
|---|
| 31 |     @brief Implementation of the SkyboxGenerator class. | 
|---|
| 32 | */ | 
|---|
| 33 |  | 
|---|
| 34 | #include "SkyboxGenerator.h" | 
|---|
| 35 |  | 
|---|
| 36 | #include <string> | 
|---|
| 37 | #include <OgreRenderWindow.h> | 
|---|
| 38 | #include <OgreCamera.h> | 
|---|
| 39 |  | 
|---|
| 40 | #include "util/ScopedSingletonManager.h" | 
|---|
| 41 | #include "core/CoreIncludes.h" | 
|---|
| 42 | #include "core/config/ConfigValueIncludes.h" | 
|---|
| 43 | #include "core/GraphicsManager.h" | 
|---|
| 44 | #include "core/PathConfig.h" | 
|---|
| 45 | #include "core/Resource.h" | 
|---|
| 46 | #include "core/command/ConsoleCommand.h" | 
|---|
| 47 | #include "core/command/CommandExecutor.h" | 
|---|
| 48 |  | 
|---|
| 49 | #include "controllers/HumanController.h" | 
|---|
| 50 | #include "graphics/Camera.h" | 
|---|
| 51 | #include "worldentities/ControllableEntity.h" | 
|---|
| 52 |  | 
|---|
| 53 | #include "ScreenshotManager.h" | 
|---|
| 54 |  | 
|---|
| 55 | // #include <X11/Xlib.h> TODO: Needed? | 
|---|
| 56 |  | 
|---|
| 57 | namespace orxonox | 
|---|
| 58 | { | 
|---|
| 59 |  | 
|---|
| 60 |     SetConsoleCommand("SkyboxGenerator", "createSkybox", &SkyboxGenerator::createSkybox).addShortcut(); | 
|---|
| 61 |  | 
|---|
| 62 |     ManageScopedSingleton(SkyboxGenerator, ScopeID::Graphics, false); | 
|---|
| 63 |  | 
|---|
| 64 |     /** | 
|---|
| 65 |     @brief | 
|---|
| 66 |         Constructor. Registers and initializes the singleton. | 
|---|
| 67 |     */ | 
|---|
| 68 |     SkyboxGenerator::SkyboxGenerator() | 
|---|
| 69 |     { | 
|---|
| 70 |         RegisterObject(SkyboxGenerator); | 
|---|
| 71 |  | 
|---|
| 72 |         this->setConfigValues(); | 
|---|
| 73 |          | 
|---|
| 74 |         this->bGenerateSkybox_ = false; | 
|---|
| 75 |         this->bCaptionsRemoved_ = false; | 
|---|
| 76 |         this->bSetup_ = true; | 
|---|
| 77 |         this->bWait_ = false; | 
|---|
| 78 |         this->bCreateFace_ = true; | 
|---|
| 79 |         this->bCleanup_ = true; | 
|---|
| 80 |         this->faceCounter_ = 0; | 
|---|
| 81 |          | 
|---|
| 82 |         this->names_.push_back("fr"); | 
|---|
| 83 |         this->names_.push_back("lf"); | 
|---|
| 84 |         this->names_.push_back("bk"); | 
|---|
| 85 |         this->names_.push_back("rt"); | 
|---|
| 86 |         this->names_.push_back("up"); | 
|---|
| 87 |         this->names_.push_back("dn"); | 
|---|
| 88 |          | 
|---|
| 89 |         this->rotations_.push_back(std::pair<int, int>(90, 0)); | 
|---|
| 90 |         this->rotations_.push_back(std::pair<int, int>(90, 0)); | 
|---|
| 91 |         this->rotations_.push_back(std::pair<int, int>(90, 0)); | 
|---|
| 92 |         this->rotations_.push_back(std::pair<int, int>(90, 90)); | 
|---|
| 93 |         this->rotations_.push_back(std::pair<int, int>(0, 180)); | 
|---|
| 94 |         this->rotations_.push_back(std::pair<int, int>(0, 90)); | 
|---|
| 95 |     } | 
|---|
| 96 |  | 
|---|
| 97 |     /** | 
|---|
| 98 |     @brief | 
|---|
| 99 |         Destructor. | 
|---|
| 100 |     */ | 
|---|
| 101 |     SkyboxGenerator::~SkyboxGenerator() | 
|---|
| 102 |     { | 
|---|
| 103 |  | 
|---|
| 104 |     } | 
|---|
| 105 |  | 
|---|
| 106 |     /** | 
|---|
| 107 |     @brief | 
|---|
| 108 |         Sets some config values. | 
|---|
| 109 |     */ | 
|---|
| 110 |     void SkyboxGenerator::setConfigValues( ) | 
|---|
| 111 |     { | 
|---|
| 112 |         SetConfigValue(skyboxPrefix_, "SkyboxFile_"); | 
|---|
| 113 |         SetConfigValue(imageExtension_, ".png"); | 
|---|
| 114 |         SetConfigValue(size_, 1024); | 
|---|
| 115 |     } | 
|---|
| 116 |      | 
|---|
| 117 |     /** | 
|---|
| 118 |     @brief | 
|---|
| 119 |         Generate the 6 faces of a skybox. | 
|---|
| 120 |     */ | 
|---|
| 121 |     void SkyboxGenerator::createSkybox() | 
|---|
| 122 |     { | 
|---|
| 123 |         // Pause | 
|---|
| 124 |         CommandExecutor::execute("pause"); | 
|---|
| 125 |         // Start the skybox generation process. | 
|---|
| 126 |         SkyboxGenerator::getInstance().startSkyboxGeneration(); | 
|---|
| 127 |     } | 
|---|
| 128 |  | 
|---|
| 129 |     /** | 
|---|
| 130 |     @brief | 
|---|
| 131 |         This is where the skybox generation happens. | 
|---|
| 132 |         Generating a skybox takes several (up to 10) ticks. | 
|---|
| 133 |     */ | 
|---|
| 134 |     void SkyboxGenerator::tick(float dt) | 
|---|
| 135 |     { | 
|---|
| 136 |         // Whether a skybox is currently being generated. | 
|---|
| 137 |         if(this->bGenerateSkybox_) | 
|---|
| 138 |         { | 
|---|
| 139 |             // Wait one tick. | 
|---|
| 140 |             if(this->bWait_) | 
|---|
| 141 |             { | 
|---|
| 142 |                 this->bWait_ = false; | 
|---|
| 143 |                 return; | 
|---|
| 144 |             } | 
|---|
| 145 |  | 
|---|
| 146 |             ControllableEntity* entity = NULL; | 
|---|
| 147 |             if(HumanController::getLocalControllerSingleton() != NULL && HumanController::getLocalControllerSingleton()->getControllableEntity() != NULL) | 
|---|
| 148 |                 entity = HumanController::getLocalControllerSingleton()->getControllableEntity(); | 
|---|
| 149 |             else | 
|---|
| 150 |             { | 
|---|
| 151 |                 orxout(user_error) << "You must be in a level to generate a skybox." << endl; | 
|---|
| 152 |                 this->bGenerateSkybox_ = false; | 
|---|
| 153 |                 return; | 
|---|
| 154 |             } | 
|---|
| 155 |             Ogre::Camera* camera = entity->getCamera()->getOgreCamera(); | 
|---|
| 156 |             Ogre::RenderWindow* renderWindow = GraphicsManager::getInstance().getRenderWindow(); | 
|---|
| 157 |              | 
|---|
| 158 |             // Setup the SkyboxGenerator to generate a skybox. | 
|---|
| 159 |             if(this->bSetup_) | 
|---|
| 160 |             { | 
|---|
| 161 |                 // If there are captions being displayed, don't. | 
|---|
| 162 |                 if(!this->bCaptionsRemoved_) | 
|---|
| 163 |                 { | 
|---|
| 164 |                     CommandExecutor::execute("GametypeStatus displayCaption false"); | 
|---|
| 165 |                     this->bCaptionsRemoved_ = true; | 
|---|
| 166 |                     return; | 
|---|
| 167 |                 } | 
|---|
| 168 |                  | 
|---|
| 169 |                 // Store the settings for the camera. | 
|---|
| 170 |                 this->fovy_ = camera->getFOVy(); | 
|---|
| 171 |                 this->aspectRatio_ = camera->getAspectRatio(); | 
|---|
| 172 |                 // Setup the render window. | 
|---|
| 173 |                 this->setupRenderWindow(renderWindow); | 
|---|
| 174 |                 // Add the log path to the standard resource group. | 
|---|
| 175 |                 Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getInstance().getLogPathString(), "FileSystem", Resource::getDefaultResourceGroup()); | 
|---|
| 176 |                  | 
|---|
| 177 |                 orxout(internal_status) << "Setting up SkyboxGenerator..." << endl; | 
|---|
| 178 |                  | 
|---|
| 179 |                 this->bSetup_ = false; | 
|---|
| 180 |                 this->bWait_ = true; | 
|---|
| 181 |             } | 
|---|
| 182 |             // Create one of the faces. (faceCounter_ decides which) | 
|---|
| 183 |             else if(this->bCreateFace_) | 
|---|
| 184 |             { | 
|---|
| 185 |                 // Setup the camera. | 
|---|
| 186 |                 this->setupCamera(camera); | 
|---|
| 187 |                 // Take the picture using the ScreenshotManager and save it. | 
|---|
| 188 |                 this->saveImage(ScreenshotManager::getInstance().getScreenshot(camera), this->skyboxPrefix_+this->names_[this->faceCounter_]+this->imageExtension_); | 
|---|
| 189 |                 // Rotate the camera to be ready for taking the next picture. | 
|---|
| 190 |                 std::pair<int, int> rotate = this->rotations_[this->faceCounter_]; | 
|---|
| 191 |                 if(rotate.first != 0) | 
|---|
| 192 |                     entity->yaw(Degree((float)rotate.first)); | 
|---|
| 193 |                 if(rotate.second != 0) | 
|---|
| 194 |                     entity->pitch(Degree((float)rotate.second)); | 
|---|
| 195 |                  | 
|---|
| 196 |                 orxout(internal_info) << "Created face number " << this->faceCounter_ << "." << endl; | 
|---|
| 197 |                 // Check whether we've generated all 6 faces. | 
|---|
| 198 |                 if(++this->faceCounter_ >= 6) | 
|---|
| 199 |                     this->bCreateFace_ = false; | 
|---|
| 200 |             } | 
|---|
| 201 |             // Cleanup after the successful creation of a skybox. | 
|---|
| 202 |             else if(this->bCleanup_) | 
|---|
| 203 |             { | 
|---|
| 204 |                 // Reset the camera parameters. | 
|---|
| 205 |                 camera->setAspectRatio(this->aspectRatio_); | 
|---|
| 206 |                 camera->setFOVy(this->fovy_); | 
|---|
| 207 |                 // Restore the render window. | 
|---|
| 208 |                 this->restoreRenderWindow(renderWindow); | 
|---|
| 209 |                 // Remove the log path from the standard resource group. | 
|---|
| 210 |                 Ogre::ResourceGroupManager::getSingleton().removeResourceLocation(PathConfig::getInstance().getLogPathString(), Resource::getDefaultResourceGroup()); | 
|---|
| 211 |                  | 
|---|
| 212 |                 // Reset the flow parameters for the next skybox generation. | 
|---|
| 213 |                 this->bGenerateSkybox_ = false; | 
|---|
| 214 |                 this->bSetup_ = true; | 
|---|
| 215 |                 this->bWait_ = false; | 
|---|
| 216 |                 this->bCreateFace_ = true; | 
|---|
| 217 |                 this->bCleanup_ = true; | 
|---|
| 218 |                 this->faceCounter_ = 0; | 
|---|
| 219 |                  | 
|---|
| 220 |                 // Display captions again. | 
|---|
| 221 |                 CommandExecutor::execute("GametypeStatus displayCaption true"); | 
|---|
| 222 |                 this->bCaptionsRemoved_ = false; | 
|---|
| 223 |                  | 
|---|
| 224 |                 // Unpause. | 
|---|
| 225 |                 CommandExecutor::execute("pause"); | 
|---|
| 226 |                  | 
|---|
| 227 |                 orxout(user_info) << "Skybox with face size " << this->size_ << "x" << this->size_ << " pixels created. Storing in log/." << endl; | 
|---|
| 228 |             } | 
|---|
| 229 |         } | 
|---|
| 230 |     } | 
|---|
| 231 |      | 
|---|
| 232 |     /** | 
|---|
| 233 |     @brief | 
|---|
| 234 |         Set up the input camera to be ready to generate a skybox face. | 
|---|
| 235 |     @param camera | 
|---|
| 236 |         The camera to be set up. | 
|---|
| 237 |     */ | 
|---|
| 238 |     void SkyboxGenerator::setupCamera(Ogre::Camera* camera) | 
|---|
| 239 |     { | 
|---|
| 240 |         camera->setFOVy(Degree(90)); | 
|---|
| 241 |         camera->setAspectRatio(1.0); | 
|---|
| 242 |     } | 
|---|
| 243 |      | 
|---|
| 244 |     /** | 
|---|
| 245 |     @brief | 
|---|
| 246 |         Setup the input render window to be ready to generate the skybox. | 
|---|
| 247 |     @param renderWindow | 
|---|
| 248 |         The render window to be set up. | 
|---|
| 249 |     */ | 
|---|
| 250 |     void SkyboxGenerator::setupRenderWindow(Ogre::RenderWindow* renderWindow) | 
|---|
| 251 |     { | 
|---|
| 252 |         // Store current window properties. | 
|---|
| 253 |         this->windowWidth_ = renderWindow->getWidth(); | 
|---|
| 254 |         this->windowHeight_ = renderWindow->getHeight(); | 
|---|
| 255 |         this->windowFullScreen_ = renderWindow->isFullScreen(); | 
|---|
| 256 |         // Store current ScreenshotManager grid size. | 
|---|
| 257 |         this->gridSize_ = ScreenshotManager::getInstance().getGridSize(); | 
|---|
| 258 |          | 
|---|
| 259 |         unsigned int size = this->size_; | 
|---|
| 260 |          | 
|---|
| 261 |         // If the desired skybox face size is bigger than what the current render window can accommodate we find a size that is a multiple of 256, that fits into the current render window adjust the grid size of the ScreenshotManager such that the screenshots generated are at least as big as we need.  | 
|---|
| 262 |         if(this->windowHeight_ < this->size_ || this->windowWidth_ < this->size_) | 
|---|
| 263 |         { | 
|---|
| 264 |             unsigned int min = std::min(this->windowHeight_, this->windowWidth_); | 
|---|
| 265 |             unsigned int step = 256; | 
|---|
| 266 |             assert(min >= step); | 
|---|
| 267 |             size = step; | 
|---|
| 268 |             while(min >= size+step) | 
|---|
| 269 |                 size += step; | 
|---|
| 270 |              | 
|---|
| 271 |             unsigned int gridSize = 1; | 
|---|
| 272 |             while(gridSize*size < this->size_) | 
|---|
| 273 |                 gridSize++; | 
|---|
| 274 |              | 
|---|
| 275 |             renderWindow->setFullscreen(false, size, size); | 
|---|
| 276 |             ScreenshotManager::getInstance().setGridSize(gridSize); | 
|---|
| 277 |         } | 
|---|
| 278 |         else | 
|---|
| 279 |             ScreenshotManager::getInstance().setGridSize(1); | 
|---|
| 280 |  | 
|---|
| 281 |     } | 
|---|
| 282 |      | 
|---|
| 283 |     /** | 
|---|
| 284 |     @brief | 
|---|
| 285 |         Restore the render window. | 
|---|
| 286 |         Reset the window size, reset the grid size of the ScreenshotManager. | 
|---|
| 287 |     @param renderWindow | 
|---|
| 288 |         The render window to be restored. | 
|---|
| 289 |     */ | 
|---|
| 290 |     void SkyboxGenerator::restoreRenderWindow(Ogre::RenderWindow* renderWindow) | 
|---|
| 291 |     { | 
|---|
| 292 |         // Restore window size. | 
|---|
| 293 |         renderWindow->setFullscreen(this->windowFullScreen_, this->windowWidth_, this->windowHeight_); | 
|---|
| 294 |         // Restore grid size. | 
|---|
| 295 |         ScreenshotManager::getInstance().setGridSize(this->gridSize_); | 
|---|
| 296 |     } | 
|---|
| 297 |      | 
|---|
| 298 |     /** | 
|---|
| 299 |     @brief | 
|---|
| 300 |         Resizes and saves the input image under the input name. | 
|---|
| 301 |     @param image | 
|---|
| 302 |         A pointer to the image to be saved. The image is deleted afterwards, | 
|---|
| 303 |     @param name | 
|---|
| 304 |         The desired filename of the image. | 
|---|
| 305 |     */ | 
|---|
| 306 |     void SkyboxGenerator::saveImage(Ogre::Image* image, const std::string& name) const | 
|---|
| 307 |     { | 
|---|
| 308 |         image->save(PathConfig::getInstance().getLogPathString()+name); | 
|---|
| 309 |         delete image; | 
|---|
| 310 |         // Loading the resizing, then saving again. This seems stupid, but resizing doesn't seem to work otherwise. | 
|---|
| 311 |         // If someone figures this out, feel free to adjust. | 
|---|
| 312 |         image = new Ogre::Image(); | 
|---|
| 313 |         image->load(name, Resource::getDefaultResourceGroup()); | 
|---|
| 314 |         image->resize(this->size_, this->size_); | 
|---|
| 315 |         image->save(PathConfig::getInstance().getLogPathString()+name); | 
|---|
| 316 |         delete image; | 
|---|
| 317 |         ScreenshotManager::getInstance().cleanup(); // Free memory in ScreenshotManager. | 
|---|
| 318 |     } | 
|---|
| 319 | } | 
|---|