#include "loadingscreen.hpp" #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwrender/vismask.hpp" #include "backgroundimage.hpp" namespace MWGui { LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) : WindowBase("openmw_loading_screen.layout") , mVFS(vfs) , mViewer(viewer) , mTargetFrameRate(120.0) , mLastWallpaperChangeTime(0.0) , mLastRenderTime(0.0) , mLoadingOnTime(0.0) , mImportantLabel(false) , mProgress(0) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); getWidget(mLoadingBox, "LoadingBox"); mProgressBar->setScrollViewPage(1); mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); setVisible(false); findSplashScreens(); } LoadingScreen::~LoadingScreen() { } void LoadingScreen::findSplashScreens() { const std::map& index = mVFS->getIndex(); std::string pattern = "Splash/"; mVFS->normalizeFilename(pattern); std::map::const_iterator found = index.lower_bound(pattern); while (found != index.end()) { const std::string& name = found->first; if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern) { size_t pos = name.find_last_of('.'); if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".tga") == 0) mSplashScreens.push_back(found->first); } else break; ++found; } if (mSplashScreens.empty()) std::cerr << "No splash screens found!" << std::endl; } void LoadingScreen::setLabel(const std::string &label, bool important) { mImportantLabel = important; mLoadingText->setCaptionWithReplacing(label); int padding = mLoadingBox->getWidth() - mLoadingText->getWidth(); MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight()); size.width = std::max(300, size.width); mLoadingBox->setSize(size); mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mLoadingBox->getTop()); } void LoadingScreen::setVisible(bool visible) { WindowBase::setVisible(visible); mBackgroundImage->setVisible(visible); } class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback { public: CopyFramebufferToTextureCallback(osg::Texture2D* texture, int w, int h) : mTexture(texture), mWidth(w), mHeight(h) { } virtual void operator () (osg::RenderInfo& renderInfo) const { mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, mWidth, mHeight); // Callback removes itself when done if (renderInfo.getCurrentCamera()) renderInfo.getCurrentCamera()->setInitialDrawCallback(NULL); } private: osg::ref_ptr mTexture; int mWidth, mHeight; }; void LoadingScreen::loadingOn() { mLoadingOnTime = mTimer.time_m(); // Early-out if already on if (mMainWidget->getVisible()) return; if (mViewer->getIncrementalCompileOperation()) { mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(mTargetFrameRate); } bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); if (!showWallpaper) { // Copy the current framebuffer onto a texture and display that texture as the background image // Note, we could also set the camera to disable clearing and have the background image transparent, // but then we get shaking effects on buffer swaps. if (!mTexture) { mTexture = new osg::Texture2D; mTexture->setInternalFormat(GL_RGB); mTexture->setResizeNonPowerOfTwoHint(false); } int width = mViewer->getCamera()->getViewport()->width(); int height = mViewer->getCamera()->getViewport()->height(); mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture, width, height)); if (!mGuiTexture.get()) { mGuiTexture.reset(new osgMyGUI::OSGTexture(mTexture)); } mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } setVisible(true); if (showWallpaper) { changeWallpaper(); } MWBase::Environment::get().getWindowManager()->pushGuiMode(showWallpaper ? GM_LoadingWallpaper : GM_Loading); } void LoadingScreen::loadingOff() { if (mLastRenderTime < mLoadingOnTime) { // the loading was so fast that we didn't show loading screen at all // we may still want to show the label if the caller requested it if (mImportantLabel) { MWBase::Environment::get().getWindowManager()->messageBox(mLoadingText->getCaption()); mImportantLabel = false; } } else mImportantLabel = false; // label was already shown on loading screen //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; setVisible(false); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } void LoadingScreen::changeWallpaper () { if (!mSplashScreens.empty()) { std::string const & randomSplash = mSplashScreens.at(Misc::Rng::rollDice(mSplashScreens.size())); // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 // we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3 bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); mBackgroundImage->setBackgroundImage(randomSplash, true, stretch); } } void LoadingScreen::setProgressRange (size_t range) { mProgressBar->setScrollRange(range+1); mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(0); mProgress = 0; } void LoadingScreen::setProgress (size_t value) { // skip expensive update if there isn't enough visible progress if (value - mProgress < mProgressBar->getScrollRange()/200.f) return; value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(static_cast(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); draw(); } void LoadingScreen::increaseProgress (size_t increase) { mProgressBar->setScrollPosition(0); size_t value = mProgress + increase; value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; mProgressBar->setTrackSize(static_cast(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); draw(); } bool LoadingScreen::needToDrawLoadingScreen() { if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0) return false; // the minimal delay before a loading screen shows const float initialDelay = 0.05; bool alreadyShown = (mLastRenderTime > mLoadingOnTime); float diff = (mTimer.time_m() - mLoadingOnTime); if (!alreadyShown) { // bump the delay by the current progress - i.e. if during the initial delay the loading // has almost finished, no point showing the loading screen now diff -= mProgress / static_cast(mProgressBar->getScrollRange()) * 100.f; } bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); if (!showWallpaper && diff < initialDelay*1000) return false; return true; } void LoadingScreen::draw() { if (!needToDrawLoadingScreen()) return; bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); if (showWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1) { mLastWallpaperChangeTime = mTimer.time_m(); changeWallpaper(); } // Turn off rendering except the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); MWBase::Environment::get().getInputManager()->update(0, true, true); //osg::Timer timer; // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); //std::cout << "frame took " << timer.time_m() << std::endl; //if (mViewer->getIncrementalCompileOperation()) //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl; // resume 3d rendering mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask); mViewer->getCamera()->setCullMask(oldCullMask); mLastRenderTime = mTimer.time_m(); } }