mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 12:53:51 +00:00
408 lines
12 KiB
C++
408 lines
12 KiB
C++
#include "renderer.hpp"
|
|
#include "fader.hpp"
|
|
#include "particles.hpp"
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_syswm.h>
|
|
|
|
#include "OgreRoot.h"
|
|
#include "OgreRenderWindow.h"
|
|
#include "OgreLogManager.h"
|
|
#include "OgreLog.h"
|
|
#include "OgreTextureManager.h"
|
|
#include "OgreTexture.h"
|
|
#include "OgreHardwarePixelBuffer.h"
|
|
#include <OgreParticleSystemManager.h>
|
|
#include "OgreParticleAffectorFactory.h"
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <components/files/ogreplugin.hpp>
|
|
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <stdexcept>
|
|
|
|
#ifdef __MACOSX__
|
|
#include "osx_utils.h"
|
|
#endif
|
|
|
|
using namespace Ogre;
|
|
using namespace OEngine::Render;
|
|
|
|
void OgreRenderer::cleanup()
|
|
{
|
|
delete mFader;
|
|
mFader = NULL;
|
|
|
|
delete mRoot;
|
|
mRoot = NULL;
|
|
|
|
if (mWindowIconSurface)
|
|
SDL_FreeSurface(mWindowIconSurface);
|
|
|
|
// If we don't do this, the desktop resolution is not restored on exit
|
|
SDL_SetWindowFullscreen(mSDLWindow, 0);
|
|
|
|
SDL_DestroyWindow(mSDLWindow);
|
|
mSDLWindow = NULL;
|
|
|
|
unloadPlugins();
|
|
}
|
|
|
|
void OgreRenderer::start()
|
|
{
|
|
mRoot->startRendering();
|
|
}
|
|
|
|
void OgreRenderer::loadPlugins()
|
|
{
|
|
#ifdef ENABLE_PLUGIN_GL
|
|
mGLPlugin = new Ogre::GLPlugin();
|
|
mRoot->installPlugin(mGLPlugin);
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_Direct3D9
|
|
mD3D9Plugin = new Ogre::D3D9Plugin();
|
|
mRoot->installPlugin(mD3D9Plugin);
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_CgProgramManager
|
|
mCgPlugin = new Ogre::CgPlugin();
|
|
mRoot->installPlugin(mCgPlugin);
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_OctreeSceneManager
|
|
mOctreePlugin = new Ogre::OctreePlugin();
|
|
mRoot->installPlugin(mOctreePlugin);
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_ParticleFX
|
|
mParticleFXPlugin = new Ogre::ParticleFXPlugin();
|
|
mRoot->installPlugin(mParticleFXPlugin);
|
|
#endif
|
|
}
|
|
|
|
void OgreRenderer::unloadPlugins()
|
|
{
|
|
std::vector<Ogre::ParticleEmitterFactory*>::iterator ei;
|
|
for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();ei++)
|
|
OGRE_DELETE (*ei);
|
|
mEmitterFactories.clear();
|
|
|
|
std::vector<Ogre::ParticleAffectorFactory*>::iterator ai;
|
|
for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();ai++)
|
|
OGRE_DELETE (*ai);
|
|
mAffectorFactories.clear();
|
|
|
|
#ifdef ENABLE_PLUGIN_GL
|
|
delete mGLPlugin;
|
|
mGLPlugin = NULL;
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_Direct3D9
|
|
delete mD3D9Plugin;
|
|
mD3D9Plugin = NULL;
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_CgProgramManager
|
|
delete mCgPlugin;
|
|
mCgPlugin = NULL;
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_OctreeSceneManager
|
|
delete mOctreePlugin;
|
|
mOctreePlugin = NULL;
|
|
#endif
|
|
#ifdef ENABLE_PLUGIN_ParticleFX
|
|
delete mParticleFXPlugin;
|
|
mParticleFXPlugin = NULL;
|
|
#endif
|
|
}
|
|
|
|
void OgreRenderer::update(float dt)
|
|
{
|
|
mFader->update(dt);
|
|
}
|
|
|
|
void OgreRenderer::screenshot(const std::string &file)
|
|
{
|
|
mWindow->writeContentsToFile(file);
|
|
}
|
|
|
|
float OgreRenderer::getFPS()
|
|
{
|
|
return mWindow->getLastFPS();
|
|
}
|
|
|
|
void OgreRenderer::configure(const std::string &logPath,
|
|
const std::string& renderSystem,
|
|
const std::string& rttMode,
|
|
bool _logging)
|
|
{
|
|
// Set up logging first
|
|
new LogManager;
|
|
Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log"));
|
|
logging = _logging;
|
|
|
|
if(logging)
|
|
// Full log detail
|
|
log->setLogDetail(LL_BOREME);
|
|
else
|
|
// Disable logging
|
|
log->setDebugOutputEnabled(false);
|
|
|
|
mRoot = new Root("", "", "");
|
|
|
|
#if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX)
|
|
loadPlugins();
|
|
#endif
|
|
|
|
std::string pluginDir;
|
|
const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR");
|
|
if (pluginEnv)
|
|
pluginDir = pluginEnv;
|
|
else
|
|
{
|
|
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
|
|
pluginDir = ".\\";
|
|
#endif
|
|
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
|
pluginDir = OGRE_PLUGIN_DIR;
|
|
#endif
|
|
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
|
pluginDir = OGRE_PLUGIN_DIR_REL;
|
|
#endif
|
|
}
|
|
|
|
boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir));
|
|
|
|
pluginDir = absPluginPath.string();
|
|
|
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot);
|
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot);
|
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot);
|
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot);
|
|
Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot);
|
|
Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot);
|
|
|
|
|
|
Ogre::ParticleEmitterFactory *emitter;
|
|
emitter = OGRE_NEW NifEmitterFactory();
|
|
Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter);
|
|
mEmitterFactories.push_back(emitter);
|
|
|
|
|
|
Ogre::ParticleAffectorFactory *affector;
|
|
affector = OGRE_NEW GrowFadeAffectorFactory();
|
|
Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector);
|
|
mAffectorFactories.push_back(affector);
|
|
|
|
affector = OGRE_NEW GravityAffectorFactory();
|
|
Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector);
|
|
mAffectorFactories.push_back(affector);
|
|
|
|
|
|
RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem);
|
|
if (rs == 0)
|
|
throw std::runtime_error ("RenderSystem with name " + renderSystem + " not found, make sure the plugins are loaded");
|
|
mRoot->setRenderSystem(rs);
|
|
|
|
if (rs->getName().find("OpenGL") != std::string::npos)
|
|
rs->setConfigOption ("RTT Preferred Mode", rttMode);
|
|
}
|
|
|
|
void OgreRenderer::recreateWindow(const std::string &title, const WindowSettings &settings)
|
|
{
|
|
Ogre::ColourValue viewportBG = mView->getBackgroundColour();
|
|
|
|
mRoot->destroyRenderTarget(mWindow);
|
|
NameValuePairList params;
|
|
params.insert(std::make_pair("title", title));
|
|
params.insert(std::make_pair("FSAA", settings.fsaa));
|
|
params.insert(std::make_pair("vsync", settings.vsync ? "true" : "false"));
|
|
|
|
mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms);
|
|
|
|
// Create one viewport, entire window
|
|
mView = mWindow->addViewport(mCamera);
|
|
mView->setBackgroundColour(viewportBG);
|
|
|
|
adjustViewport();
|
|
}
|
|
|
|
void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings)
|
|
{
|
|
assert(mRoot);
|
|
mRoot->initialise(false);
|
|
|
|
NameValuePairList params;
|
|
params.insert(std::make_pair("title", title));
|
|
params.insert(std::make_pair("FSAA", settings.fsaa));
|
|
params.insert(std::make_pair("vsync", settings.vsync ? "true" : "false"));
|
|
|
|
// Create an application window with the following settings:
|
|
mSDLWindow = SDL_CreateWindow(
|
|
"OpenMW", // window title
|
|
SDL_WINDOWPOS_UNDEFINED, // initial x position
|
|
SDL_WINDOWPOS_UNDEFINED, // initial y position
|
|
settings.window_x, // width, in pixels
|
|
settings.window_y, // height, in pixels
|
|
SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE
|
|
| (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0)
|
|
);
|
|
|
|
//get the native whnd
|
|
struct SDL_SysWMinfo wmInfo;
|
|
SDL_VERSION(&wmInfo.version);
|
|
|
|
if(-1 == SDL_GetWindowWMInfo(mSDLWindow, &wmInfo))
|
|
throw std::runtime_error("Couldn't get WM Info!");
|
|
|
|
Ogre::String winHandle;
|
|
|
|
switch(wmInfo.subsystem)
|
|
{
|
|
#ifdef WIN32
|
|
case SDL_SYSWM_WINDOWS:
|
|
// Windows code
|
|
winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window);
|
|
break;
|
|
#elif __MACOSX__
|
|
case SDL_SYSWM_COCOA:
|
|
//required to make OGRE play nice with our window
|
|
params.insert(std::make_pair("macAPI", "cocoa"));
|
|
params.insert(std::make_pair("macAPICocoaUseNSView", "true"));
|
|
|
|
winHandle = Ogre::StringConverter::toString(WindowContentViewHandle(wmInfo));
|
|
break;
|
|
#else
|
|
case SDL_SYSWM_X11:
|
|
winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.display);
|
|
winHandle += ":0:";
|
|
winHandle += Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.window);
|
|
break;
|
|
#endif
|
|
default:
|
|
throw std::runtime_error("Unexpected WM!");
|
|
break;
|
|
}
|
|
|
|
params.insert(std::make_pair("externalWindowHandle", winHandle));
|
|
|
|
mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms);
|
|
|
|
// Set the window icon
|
|
if (settings.icon != "")
|
|
{
|
|
mWindowIconSurface = ogreTextureToSDLSurface(settings.icon);
|
|
SDL_SetWindowIcon(mSDLWindow, mWindowIconSurface);
|
|
}
|
|
|
|
// create the semi-transparent black background texture used by the GUI.
|
|
// has to be created in code with TU_DYNAMIC_WRITE_ONLY param
|
|
// so that it can be modified at runtime.
|
|
Ogre::TextureManager::getSingleton().createManual(
|
|
"transparent.png",
|
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
|
Ogre::TEX_TYPE_2D,
|
|
1, 1,
|
|
0,
|
|
Ogre::PF_A8R8G8B8,
|
|
Ogre::TU_WRITE_ONLY);
|
|
}
|
|
|
|
void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip)
|
|
{
|
|
assert(mRoot);
|
|
assert(mWindow);
|
|
// Get the SceneManager, in this case a generic one
|
|
mScene = mRoot->createSceneManager(ST_GENERIC);
|
|
|
|
// Create the camera
|
|
mCamera = mScene->createCamera(camName);
|
|
mCamera->setNearClipDistance(nearClip);
|
|
mCamera->setFOVy(Degree(fov));
|
|
|
|
// Create one viewport, entire window
|
|
mView = mWindow->addViewport(mCamera);
|
|
|
|
// Alter the camera aspect ratio to match the viewport
|
|
mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight()));
|
|
|
|
mFader = new Fader(mScene);
|
|
}
|
|
|
|
void OgreRenderer::adjustViewport()
|
|
{
|
|
// Alter the camera aspect ratio to match the viewport
|
|
if(mCamera != NULL)
|
|
{
|
|
mView->setDimensions(0, 0, 1, 1);
|
|
mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight()));
|
|
}
|
|
}
|
|
|
|
void OgreRenderer::setWindowEventListener(Ogre::WindowEventListener* listener)
|
|
{
|
|
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, listener);
|
|
}
|
|
|
|
void OgreRenderer::removeWindowEventListener(Ogre::WindowEventListener* listener)
|
|
{
|
|
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, listener);
|
|
}
|
|
|
|
void OgreRenderer::setFov(float fov)
|
|
{
|
|
mCamera->setFOVy(Degree(fov));
|
|
}
|
|
|
|
SDL_Surface* OgreRenderer::ogreTextureToSDLSurface(const std::string& name)
|
|
{
|
|
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(name, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
|
|
if (texture.isNull())
|
|
{
|
|
std::stringstream error;
|
|
error << "Window icon not found: " << name;
|
|
throw std::runtime_error(error.str());
|
|
}
|
|
Ogre::Image image;
|
|
texture->convertToImage(image);
|
|
|
|
SDL_Surface* surface = SDL_CreateRGBSurface(0,texture->getWidth(),texture->getHeight(),32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);
|
|
|
|
//copy the Ogre texture to an SDL surface
|
|
for(size_t x = 0; x < texture->getWidth(); ++x)
|
|
{
|
|
for(size_t y = 0; y < texture->getHeight(); ++y)
|
|
{
|
|
Ogre::ColourValue clr = image.getColourAt(x, y, 0);
|
|
|
|
//set the pixel on the SDL surface to the same value as the Ogre texture's
|
|
int bpp = surface->format->BytesPerPixel;
|
|
/* Here p is the address to the pixel we want to set */
|
|
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
|
Uint32 pixel = SDL_MapRGBA(surface->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255);
|
|
switch(bpp) {
|
|
case 1:
|
|
*p = pixel;
|
|
break;
|
|
|
|
case 2:
|
|
*(Uint16 *)p = pixel;
|
|
break;
|
|
|
|
case 3:
|
|
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
|
|
p[0] = (pixel >> 16) & 0xff;
|
|
p[1] = (pixel >> 8) & 0xff;
|
|
p[2] = pixel & 0xff;
|
|
} else {
|
|
p[0] = pixel & 0xff;
|
|
p[1] = (pixel >> 8) & 0xff;
|
|
p[2] = (pixel >> 16) & 0xff;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
*(Uint32 *)p = pixel;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return surface;
|
|
}
|