diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 794ca3d7b..de3e23965 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -259,20 +259,36 @@ int CS::Editor::run() std::auto_ptr CS::Editor::setupGraphics() { - // TODO: setting - Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); + std::string renderer = +#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + "Direct3D9 Rendering Subsystem"; +#else + "OpenGL Rendering Subsystem"; +#endif + std::string renderSystem = mUserSettings.setting("Video/render system", renderer.c_str()).toStdString(); + + Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName(renderSystem)); Ogre::Root::getSingleton().initialise(false); // Create a hidden background window to keep resources Ogre::NameValuePairList params; params.insert(std::make_pair("title", "")); - params.insert(std::make_pair("FSAA", "0")); + + std::string antialiasing = mUserSettings.settingValue("Video/antialiasing").toStdString(); + if(antialiasing == "MSAA 16") antialiasing = "16"; + else if(antialiasing == "MSAA 8") antialiasing = "8"; + else if(antialiasing == "MSAA 4") antialiasing = "4"; + else if(antialiasing == "MSAA 2") antialiasing = "2"; + else antialiasing = "0"; + params.insert(std::make_pair("FSAA", antialiasing)); + params.insert(std::make_pair("vsync", "false")); params.insert(std::make_pair("hidden", "true")); #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE params.insert(std::make_pair("macAPI", "cocoa")); #endif + // NOTE: fullscreen mode not supported (doesn't really make sense for opencs) Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); @@ -286,7 +302,28 @@ std::auto_ptr CS::Editor::setupGraphics() std::auto_ptr factory (new sh::Factory (platform)); - factory->setCurrentLanguage (sh::Language_GLSL); /// \todo make this configurable + QString shLang = mUserSettings.settingValue("General/shader mode"); + QString rend = renderSystem.c_str(); + bool openGL = rend.contains(QRegExp("^OpenGL", Qt::CaseInsensitive)); + bool glES = rend.contains(QRegExp("^OpenGL ES", Qt::CaseInsensitive)); + + // force shader language based on render system + if(shLang == "" + || (openGL && shLang == "hlsl") + || (!openGL && shLang == "glsl") + || (glES && shLang != "glsles")) + { + shLang = openGL ? (glES ? "glsles" : "glsl") : "hlsl"; + //no group means "General" group in the "ini" file standard + mUserSettings.setDefinitions("shader mode", (QStringList() << shLang)); + } + enum sh::Language lang; + if(shLang == "glsl") lang = sh::Language_GLSL; + else if(shLang == "glsles") lang = sh::Language_GLSLES; + else if(shLang == "hlsl") lang = sh::Language_HLSL; + else lang = sh::Language_CG; + + factory->setCurrentLanguage (lang); factory->setWriteSourceCache (true); factory->setReadSourceCache (true); factory->setReadMicrocodeCache (true); @@ -294,16 +331,27 @@ std::auto_ptr CS::Editor::setupGraphics() factory->loadAllFiles(); - sh::Factory::getInstance().setGlobalSetting ("fog", "true"); + bool shaders = mUserSettings.setting("Objects/shaders", QString("true")) == "true" ? true : false; + sh::Factory::getInstance ().setShadersEnabled (shaders); + + std::string fog = mUserSettings.setting("Shader/fog", QString("true")).toStdString(); + sh::Factory::getInstance().setGlobalSetting ("fog", fog); + + + std::string shadows = mUserSettings.setting("Shader/shadows", QString("false")).toStdString(); + sh::Factory::getInstance().setGlobalSetting ("shadows", shadows); - sh::Factory::getInstance().setGlobalSetting ("shadows", "false"); - sh::Factory::getInstance().setGlobalSetting ("shadows_pssm", "false"); + std::string shadows_pssm = mUserSettings.setting("Shader/shadows_pssm", QString("false")).toStdString(); + sh::Factory::getInstance().setGlobalSetting ("shadows_pssm", shadows_pssm); - sh::Factory::getInstance ().setGlobalSetting ("render_refraction", "false"); + std::string render_refraction = mUserSettings.setting("Shader/render_refraction", QString("false")).toStdString(); + sh::Factory::getInstance ().setGlobalSetting ("render_refraction", render_refraction); + // internal setting - may be switched on or off by the use of shader configurations sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); - sh::Factory::getInstance ().setGlobalSetting ("num_lights", "8"); + std::string num_lights = mUserSettings.setting("Objects/num_lights", QString("8")).toStdString(); + sh::Factory::getInstance ().setGlobalSetting ("num_lights", num_lights); /// \todo add more configurable shiny settings diff --git a/apps/opencs/model/settings/setting.cpp b/apps/opencs/model/settings/setting.cpp index 2f86d4ff8..404ec58f7 100644 --- a/apps/opencs/model/settings/setting.cpp +++ b/apps/opencs/model/settings/setting.cpp @@ -194,6 +194,16 @@ QString CSMSettings::Setting::page() const return property (Property_Page).at(0); } +void CSMSettings::Setting::setStyleSheet (const QString &value) +{ + setProperty (Property_StyleSheet, value); +} + +QString CSMSettings::Setting::styleSheet() const +{ + return property (Property_StyleSheet).at(0); +} + void CSMSettings::Setting::setPrefix (const QString &value) { setProperty (Property_Prefix, value); diff --git a/apps/opencs/model/settings/setting.hpp b/apps/opencs/model/settings/setting.hpp index e40302f00..0068fd158 100644 --- a/apps/opencs/model/settings/setting.hpp +++ b/apps/opencs/model/settings/setting.hpp @@ -80,6 +80,9 @@ namespace CSMSettings void setPage (const QString &value); QString page() const; + void setStyleSheet (const QString &value); + QString styleSheet() const; + void setPrefix (const QString &value); QString prefix() const; diff --git a/apps/opencs/model/settings/support.hpp b/apps/opencs/model/settings/support.hpp index 229e293b8..1e122a214 100644 --- a/apps/opencs/model/settings/support.hpp +++ b/apps/opencs/model/settings/support.hpp @@ -35,12 +35,13 @@ namespace CSMSettings Property_TickInterval = 19, Property_TicksAbove = 20, Property_TicksBelow = 21, + Property_StyleSheet = 22, //Stringlists should always be the last items - Property_DefaultValues = 22, - Property_DeclaredValues = 23, - Property_DefinedValues = 24, - Property_Proxies = 25 + Property_DefaultValues = 23, + Property_DeclaredValues = 24, + Property_DefinedValues = 25, + Property_Proxies = 26 }; ///Basic setting widget types. @@ -106,7 +107,7 @@ namespace CSMSettings "is_multi_line", "widget_width", "view_row", "view_column", "delimiter", "is_serializable","column_span", "row_span", "minimum", "maximum", "special_value_text", "prefix", "suffix", "single_step", "wrapping", - "tick_interval", "ticks_above", "ticks_below", + "tick_interval", "ticks_above", "ticks_below", "stylesheet", "defaults", "declarations", "definitions", "proxies" }; @@ -135,6 +136,7 @@ namespace CSMSettings "1", //tick interval "false", //ticks above "true", //ticks below + "", //StyleSheet "", //default values "", //declared values "", //defined values diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 04f98f0d6..11fdc54f4 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -4,12 +4,16 @@ #include #include +#include #include #include "setting.hpp" #include "support.hpp" +#include #include +#include + /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ @@ -28,20 +32,111 @@ namespace boost CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0; -CSMSettings::UserSettings::UserSettings (const Files::ConfigurationManager& configurationManager) -: mCfgMgr (configurationManager) + CSMSettings::UserSettings::UserSettings (const Files::ConfigurationManager& configurationManager) + : mCfgMgr (configurationManager) + , mSettingDefinitions(NULL) + , mSettingCfgDefinitions(NULL) { assert(!mUserSettingsInstance); mUserSettingsInstance = this; - mSettingDefinitions = 0; - buildSettingModelDefaults(); + + // for overriding opencs.ini settings with those from settings.cfg + mSettingCfgDefinitions = new QSettings(QSettings::IniFormat, QSettings::UserScope, "", QString(), this); } void CSMSettings::UserSettings::buildSettingModelDefaults() { - QString section = "Window Size"; + QString section; + + section = "Objects"; + { + Setting *numLights = createSetting (Type_SpinBox, section, "num_lights"); + numLights->setDefaultValue(8); + numLights->setEditorSetting(true); + numLights->setColumnSpan (1); + numLights->setMinimum (0); + numLights->setMaximum (100); // FIXME: not sure what the max value should be + numLights->setWidgetWidth (10); + numLights->setViewLocation(1, 2); + + Setting *shaders = createSetting (Type_CheckBox, section, "shaders"); + shaders->setDeclaredValues(QStringList() << "true" << "false"); + shaders->setDefaultValue("true"); + shaders->setEditorSetting(true); + shaders->setSpecialValueText("Enable Shaders"); + shaders->setWidgetWidth(25); + shaders->setColumnSpan (3); + shaders->setStyleSheet ("QGroupBox { border: 0px; }"); + shaders->setViewLocation(2, 1); + } + + section = "Scene"; + { + Setting *fastFactor = createSetting (Type_SpinBox, section, "fast factor"); + fastFactor->setDefaultValue(4); + fastFactor->setEditorSetting(true); + fastFactor->setColumnSpan (1); + fastFactor->setMinimum (1); + fastFactor->setSpecialValueText ("1"); // FIXME: workaround + fastFactor->setMaximum (100); // FIXME: not sure what the max value should be + fastFactor->setWidgetWidth (10); + fastFactor->setViewLocation(1, 2); + + Setting *farClipDist = createSetting (Type_DoubleSpinBox, section, "far clip distance"); + farClipDist->setDefaultValue(300000); + farClipDist->setEditorSetting(true); + farClipDist->setColumnSpan (1); + farClipDist->setMinimum (0); + farClipDist->setMaximum (1000000); // FIXME: not sure what the max value should be + farClipDist->setWidgetWidth (10); + farClipDist->setViewLocation(2, 2); + + Setting *timerStart = createSetting (Type_SpinBox, section, "timer start"); + timerStart->setDefaultValue(20); + timerStart->setEditorSetting(true); + timerStart->setColumnSpan (1); + timerStart->setMinimum (0); + timerStart->setMaximum (100); // FIXME: not sure what the max value should be + timerStart->setWidgetWidth (10); + timerStart->setViewLocation(3, 2); + } + + section = "SubView"; + { + Setting *maxSubView = createSetting (Type_SpinBox, section, "max subviews"); + maxSubView->setDefaultValue(256); + maxSubView->setEditorSetting(true); + maxSubView->setColumnSpan (1); + maxSubView->setMinimum (1); + maxSubView->setSpecialValueText ("1"); + maxSubView->setMaximum (256); // FIXME: not sure what the max value should be + maxSubView->setWidgetWidth (10); + maxSubView->setViewLocation(1, 2); + + Setting *minWidth = createSetting (Type_SpinBox, section, "minimum width"); + minWidth->setDefaultValue(325); + minWidth->setEditorSetting(true); + minWidth->setColumnSpan (1); + minWidth->setMinimum (50); + minWidth->setSpecialValueText ("50"); + minWidth->setMaximum (10000); // FIXME: not sure what the max value should be + minWidth->setWidgetWidth (10); + minWidth->setViewLocation(2, 2); + + Setting *reuse = createSetting (Type_CheckBox, section, "reuse"); + reuse->setDeclaredValues(QStringList() << "true" << "false"); + reuse->setDefaultValue("true"); + reuse->setEditorSetting(true); + reuse->setSpecialValueText("Reuse SubView"); + reuse->setWidgetWidth(25); + reuse->setColumnSpan (3); + reuse->setStyleSheet ("QGroupBox { border: 0px; }"); + reuse->setViewLocation(3, 2); + } + + section = "Window Size"; { Setting *width = createSetting (Type_LineEdit, section, "Width"); Setting *height = createSetting (Type_LineEdit, section, "Height"); @@ -100,6 +195,17 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() ritd->setEditorSetting (true); } + section = "Video"; + { + QString defaultValue = "None"; + QStringList values = QStringList() + << defaultValue << "MSAA 2" << "MSAA 4" << "MSAA 8" << "MSAA 16"; + Setting *antialiasing = createSetting (Type_SpinBox, section, "antialiasing"); + antialiasing->setDeclaredValues (values); + antialiasing->setEditorSetting (true); + antialiasing->setWidgetWidth(15); + } + section = "Proxy Selection Test"; { /****************************************************************** @@ -305,10 +411,27 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName) mSettingDefinitions = new QSettings (QSettings::IniFormat, QSettings::UserScope, "opencs", QString(), this); + + // check if override entry exists (default: disabled) + if(!mSettingDefinitions->childGroups().contains("Video", Qt::CaseInsensitive)) + mSettingDefinitions->setValue("Video/use settings.cfg", "false"); } -bool CSMSettings::UserSettings::hasSettingDefinitions - (const QString &viewKey) const +// if the key is not found create one with a defaut value +QString CSMSettings::UserSettings::setting(const QString &viewKey, const QString &value) +{ + if(mSettingDefinitions->contains(viewKey)) + return settingValue(viewKey); + else if(value != QString()) + { + mSettingDefinitions->setValue (viewKey, QStringList() << value); + return value; + } + + return QString(); +} + +bool CSMSettings::UserSettings::hasSettingDefinitions (const QString &viewKey) const { return (mSettingDefinitions->contains (viewKey)); } @@ -326,10 +449,25 @@ void CSMSettings::UserSettings::saveDefinitions() const QString CSMSettings::UserSettings::settingValue (const QString &settingKey) { - if (!mSettingDefinitions->contains (settingKey)) - return QString(); + QStringList defs; + + // check if video settings are overriden + if(settingKey.contains(QRegExp("^Video\\b", Qt::CaseInsensitive)) && + mSettingDefinitions->value("Video/use settings.cfg") == "true" && + settingKey.contains(QRegExp("^Video/\\brender|antialiasing|vsync|fullscreen\\b", Qt::CaseInsensitive))) + { + if (!mSettingCfgDefinitions->contains (settingKey)) + return QString(); + else + defs = mSettingCfgDefinitions->value (settingKey).toStringList(); + } + else + { + if (!mSettingDefinitions->contains (settingKey)) + return QString(); - QStringList defs = mSettingDefinitions->value (settingKey).toStringList(); + defs = mSettingDefinitions->value (settingKey).toStringList(); + } if (defs.isEmpty()) return QString(); @@ -348,6 +486,15 @@ void CSMSettings::UserSettings::updateUserSetting(const QString &settingKey, { mSettingDefinitions->setValue (settingKey ,list); + if(settingKey == "Objects/num_lights" && !list.empty()) + { + sh::Factory::getInstance ().setGlobalSetting ("num_lights", list.at(0).toStdString()); + } + else if(settingKey == "Objects/shaders" && !list.empty()) + { + sh::Factory::getInstance ().setShadersEnabled (list.at(0).toStdString() == "true" ? true : false); + } + emit userSettingUpdated (settingKey, list); } @@ -387,7 +534,6 @@ void CSMSettings::UserSettings::removeSetting } } - CSMSettings::SettingPageMap CSMSettings::UserSettings::settingPageMap() const { SettingPageMap pageMap; diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp index 7e553caf6..0aabd66e5 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -33,8 +33,10 @@ namespace CSMSettings { const Files::ConfigurationManager& mCfgMgr; QSettings *mSettingDefinitions; + QSettings *mSettingCfgDefinitions; QList mSettings; + public: /// Singleton implementation @@ -75,6 +77,8 @@ namespace CSMSettings { ///Save any unsaved changes in the QSettings object void saveDefinitions() const; + QString setting(const QString &viewKey, const QString &value = QString()); + private: void buildSettingModelDefaults(); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 7e7756ff3..940181c24 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -71,6 +71,14 @@ namespace CSMWorld record.load (reader); + if (index==-1) + { + std::string newId = IdAccessorT().getId(record); + int newIndex = this->searchId(newId); + if (newIndex != -1 && id != newId) + index = newIndex; + } + load (record, base, index); } } diff --git a/apps/opencs/model/world/resourcesmanager.cpp b/apps/opencs/model/world/resourcesmanager.cpp index ec6746521..50014f4b5 100644 --- a/apps/opencs/model/world/resourcesmanager.cpp +++ b/apps/opencs/model/world/resourcesmanager.cpp @@ -6,18 +6,20 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources) { mResources.insert (std::make_pair (resources.getType(), resources)); + mResources.insert (std::make_pair (UniversalId::getParentType (resources.getType()), + resources)); } void CSMWorld::ResourcesManager::listResources() { static const char * const sMeshTypes[] = { "nif", 0 }; - addResources (Resources ("meshes", UniversalId::Type_Meshes, sMeshTypes)); - addResources (Resources ("icons", UniversalId::Type_Icons)); - addResources (Resources ("music", UniversalId::Type_Musics)); - addResources (Resources ("sound", UniversalId::Type_SoundsRes)); - addResources (Resources ("textures", UniversalId::Type_Textures)); - addResources (Resources ("videos", UniversalId::Type_Videos)); + addResources (Resources ("meshes", UniversalId::Type_Mesh, sMeshTypes)); + addResources (Resources ("icons", UniversalId::Type_Icon)); + addResources (Resources ("music", UniversalId::Type_Music)); + addResources (Resources ("sound", UniversalId::Type_SoundRes)); + addResources (Resources ("textures", UniversalId::Type_Texture)); + addResources (Resources ("videos", UniversalId::Type_Video)); } const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 9c3c4e5e9..be03f91a5 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -1,5 +1,7 @@ #include "subview.hpp" +#include "view.hpp" + CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view @@ -25,3 +27,10 @@ void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id) mUniversalId = id; setWindowTitle (mUniversalId.toString().c_str()); } + +void CSVDoc::SubView::closeEvent (QCloseEvent *event) +{ + // update title bars of view and subviews + if(mParent) + mParent->updateSubViewIndicies(this); +} diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 733a75bb0..6a3ddd8bd 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -18,11 +18,14 @@ namespace CSMWorld namespace CSVDoc { + class View; + class SubView : public QDockWidget { Q_OBJECT CSMWorld::UniversalId mUniversalId; + View *mParent; // not implemented SubView (const SubView&); @@ -44,6 +47,12 @@ namespace CSVDoc virtual void useHint (const std::string& hint); ///< Default implementation: ignored + void setParent(View *parent) { mParent = parent; } + + private: + + void closeEvent (QCloseEvent *event); + signals: void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 1ed75b0b4..73beee60c 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "../../model/doc/document.hpp" #include "../../model/settings/usersettings.hpp" @@ -103,6 +104,10 @@ void CSVDoc::View::setupViewMenu() mShowStatusBar = new QAction (tr ("Show Status Bar"), this); mShowStatusBar->setCheckable (true); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); + std::string showStatusBar = + CSMSettings::UserSettings::instance().settingValue("Display/show statusbar").toStdString(); + if(showStatusBar == "true") + mShowStatusBar->setChecked(true); view->addAction (mShowStatusBar); QAction *filters = new QAction (tr ("Filters"), this); @@ -283,7 +288,7 @@ void CSVDoc::View::setupUi() setupDebugMenu(); } -void CSVDoc::View::updateTitle() +void CSVDoc::View::updateTitle(const std::string subview) { std::ostringstream stream; @@ -295,9 +300,40 @@ void CSVDoc::View::updateTitle() if (mViewTotal>1) stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]"; + if (subview != "") + stream << " - " << subview; + setWindowTitle (stream.str().c_str()); } +void CSVDoc::View::updateSubViewIndicies(SubView *view) +{ + if(view && mSubViews.contains(view)) + mSubViews.removeOne(view); + + if(mSubViews.size() == 1) + { + if(!mSubViews.at(0)->isFloating()) + { + mSubViews.at(0)->setTitleBarWidget(new QWidget(this)); + updateTitle(mSubViews.at(0)->getUniversalId().getTypeName().c_str()); + } + } + else + { + updateTitle(); + if(mSubViews.size() > 1) + { + foreach(SubView * sb, mSubViews) + { + QWidget * tb = sb->titleBarWidget(); + if(tb) delete tb; + sb->setTitleBarWidget(0); + } + } + } +} + void CSVDoc::View::updateActions() { bool editing = !(mDocument->getState() & CSMDoc::State_Locked); @@ -326,7 +362,11 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to QString height = CSMSettings::UserSettings::instance().settingValue ("Window Size/Height"); - resize (width.toInt(), height.toInt()); + // trick to get the window decorations and their sizes + show(); + hide(); + resize (width.toInt() - (frameGeometry().width() - geometry().width()), + height.toInt() - (frameGeometry().height() - geometry().height())); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); @@ -400,31 +440,64 @@ void CSVDoc::View::updateProgress (int current, int max, int type, int threads) void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::string& hint) { - /// \todo add an user setting for limiting the number of sub views per top level view. Automatically open a new top level view if this - /// number is exceeded + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - /// \todo if the sub view limit setting is one, the sub view title bar should be hidden and the text in the main title bar adjusted - /// accordingly + const std::vector referenceables(CSMWorld::UniversalId::listReferenceableTypes()); + bool isReferenceable = std::find(referenceables.begin(), referenceables.end(), id.getType()) != referenceables.end(); - /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) + // User setting to reuse sub views (on a per top level view basis) + bool reuse = + userSettings.setting("SubView/reuse", QString("true")) == "true" ? true : false; + if(reuse) + { + foreach(SubView *sb, mSubViews) + { + if((isReferenceable && (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()) == CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, sb->getUniversalId().getId()))) + || (!isReferenceable && (id == sb->getUniversalId()))) + { + sb->setFocus(Qt::OtherFocusReason); // FIXME: focus not quite working + return; + } + } + } + + // User setting for limiting the number of sub views per top level view. + // Automatically open a new top level view if this number is exceeded + // + // If the sub view limit setting is one, the sub view title bar is hidden and the + // text in the main title bar is adjusted accordingly + int maxSubView = userSettings.setting("SubView/max subviews", QString("256")).toInt(); + if(mSubViews.size() >= maxSubView) // create a new top level view + { + mViewManager.addView(mDocument, id, hint); + + return; + } - const std::vector referenceables(CSMWorld::UniversalId::listReferenceableTypes()); SubView *view = NULL; if(std::find(referenceables.begin(), referenceables.end(), id.getType()) != referenceables.end()) { view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); - } else + } + else { view = mSubViewFactory.makeSubView (id, *mDocument); } assert(view); + view->setParent(this); + mSubViews.append(view); // only after assert if (!hint.empty()) view->useHint (hint); + int minWidth = userSettings.setting("SubView/minimum width", QString("325")).toInt(); + view->setMinimumWidth(minWidth); + view->setStatusBar (mShowStatusBar->isChecked()); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); + updateSubViewIndicies(); + connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this, SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&))); @@ -642,6 +715,11 @@ void CSVDoc::View::toggleShowStatusBar (bool show) } } +void CSVDoc::View::toggleStatusBar(bool checked) +{ + mShowStatusBar->setChecked(checked); +} + void CSVDoc::View::loadErrorLog() { addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_LoadErrorLog, 0)); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 9b4f2099b..eab274a59 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -35,6 +35,7 @@ namespace CSVDoc CSMDoc::Document *mDocument; int mViewIndex; int mViewTotal; + QList mSubViews; QAction *mUndo; QAction *mRedo; QAction *mSave; @@ -74,7 +75,7 @@ namespace CSVDoc void setupUi(); - void updateTitle(); + void updateTitle(const std::string subview = ""); void updateActions(); @@ -106,11 +107,16 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); + void toggleStatusBar(bool checked); + Operations *getOperations() const; /// Function called by view manager when user preferences are updated void updateEditorSetting (const QString &, const QString &); + // called when subviews are added or removed + void updateSubViewIndicies(SubView *view = 0); + signals: void newGameRequest(); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 638b42d5f..2b46eecd2 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -9,6 +9,7 @@ #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" #include "../../model/world/columns.hpp" +#include "../../model/world/universalid.hpp" #include "../world/util.hpp" #include "../world/enumdelegate.hpp" @@ -140,6 +141,10 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) mViews.push_back (view); + std::string showStatusBar = + CSMSettings::UserSettings::instance().settingValue("Display/show statusbar").toStdString(); + + view->toggleStatusBar (showStatusBar == "true"); view->show(); connect (view, SIGNAL (newGameRequest ()), this, SIGNAL (newGameRequest())); @@ -157,6 +162,14 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) return view; } +CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document, const CSMWorld::UniversalId& id, const std::string& hint) +{ + View* view = addView(document); + view->addSubView(id, hint); + + return view; +} + int CSVDoc::ViewManager::countViews (const CSMDoc::Document *document) const { int count = 0; diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 753d7f0cb..cdd5ac768 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -18,6 +18,11 @@ namespace CSVWorld class CommandDelegateFactoryCollection; } +namespace CSMWorld +{ + class UniversalId; +} + namespace CSVDoc { class View; @@ -52,6 +57,8 @@ namespace CSVDoc View *addView (CSMDoc::Document *document); ///< The ownership of the returned view is not transferred. + View *addView (CSMDoc::Document *document, const CSMWorld::UniversalId& id, const std::string& hint); + int countViews (const CSMDoc::Document *document) const; ///< Return number of views for \a document. diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index ef2aeb4d3..97490d508 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -11,7 +11,7 @@ CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *pare { QHBoxLayout *layout = new QHBoxLayout (this); - layout->setContentsMargins (0, 0, 0, 0); + layout->setContentsMargins (0, 6, 5, 0); QLabel *label = new QLabel("Record Filter", this); label->setIndent(2); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 06b041786..5ddb5e084 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -13,6 +13,7 @@ #include #include "../widget/scenetoolmode.hpp" +#include "../../model/settings/usersettings.hpp" #include "navigation.hpp" #include "lighting.hpp" @@ -27,7 +28,7 @@ namespace CSVRender , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) , mKeyRollLeft (false), mKeyRollRight (false) , mFast (false), mDragging (false), mMod1 (false) - , mFastFactor (4) /// \todo make this configurable + , mFastFactor (4) , mDefaultAmbient (0, 0, 0, 0), mHasDefaultAmbient (false) { setAttribute(Qt::WA_PaintOnScreen); @@ -44,7 +45,14 @@ namespace CSVRender mCamera->setPosition (300, 0, 0); mCamera->lookAt (0, 0, 0); mCamera->setNearClipDistance (0.1); - mCamera->setFarClipDistance (300000); ///< \todo make this configurable + + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + float farClipDist = userSettings.setting("Scene/far clip distance", QString("300000")).toFloat(); + mCamera->setFarClipDistance (farClipDist); + + mFastFactor = userSettings.setting("Scene/fast factor", QString("4")).toInt(); + mCamera->roll (Ogre::Degree (90)); setLighting (&mLightingDay); @@ -52,7 +60,9 @@ namespace CSVRender QTimer *timer = new QTimer (this); connect (timer, SIGNAL (timeout()), this, SLOT (update())); - timer->start (20); ///< \todo make this configurable + + int timerStart = userSettings.setting("Scene/timer start", QString("20")).toInt(); + timer->start (timerStart); /// \todo make shortcut configurable QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); @@ -118,7 +128,16 @@ namespace CSVRender params.insert(std::make_pair("externalWindowHandle", windowHandle.str())); params.insert(std::make_pair("title", windowTitle.str())); - params.insert(std::make_pair("FSAA", "0")); // TODO setting + + std::string antialiasing = + CSMSettings::UserSettings::instance().settingValue("Video/antialiasing").toStdString(); + if(antialiasing == "MSAA 16") antialiasing = "16"; + else if(antialiasing == "MSAA 8") antialiasing = "8"; + else if(antialiasing == "MSAA 4") antialiasing = "4"; + else if(antialiasing == "MSAA 2") antialiasing = "2"; + else antialiasing = "0"; + params.insert(std::make_pair("FSAA", antialiasing)); + params.insert(std::make_pair("vsync", "false")); // TODO setting #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE params.insert(std::make_pair("macAPI", "cocoa")); @@ -382,4 +401,32 @@ namespace CSVRender else if (mode=="bright") setLighting (&mLightingBright); } + + void SceneWidget::updateUserSetting (const QString &key, const QStringList &list) + { + if(key.contains(QRegExp("^\\b(Objects|Shader|Scene)", Qt::CaseInsensitive))) + flagAsModified(); + + if(key == "Scene/far clip distance" && !list.empty()) + { + if(mCamera->getFarClipDistance() != list.at(0).toFloat()) + mCamera->setFarClipDistance(list.at(0).toFloat()); + } + + // minimise unnecessary ogre window creation by updating only when there is a change + if(key == "Video/antialiasing") + { + unsigned int aa = mWindow->getFSAA(); + unsigned int antialiasing = 0; + if(!list.empty()) + { + if(list.at(0) == "MSAA 16") antialiasing = 16; + else if(list.at(0) == "MSAA 8") antialiasing = 8; + else if(list.at(0) == "MSAA 4") antialiasing = 4; + else if(list.at(0) == "MSAA 2") antialiasing = 2; + } + if(aa != antialiasing) + updateOgreWindow(); + } + } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 6361589a4..8301cd446 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -108,6 +108,10 @@ namespace CSVRender LightingNight mLightingNight; LightingBright mLightingBright; + public slots: + + void updateUserSetting (const QString &key, const QStringList &list); + private slots: void update(); diff --git a/apps/opencs/view/settings/booleanview.cpp b/apps/opencs/view/settings/booleanview.cpp index 2a3f0cba6..29f9775af 100644 --- a/apps/opencs/view/settings/booleanview.cpp +++ b/apps/opencs/view/settings/booleanview.cpp @@ -12,16 +12,27 @@ CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting, Page *parent) - : View (setting, parent) + : mType(setting->type()), View (setting, parent) { foreach (const QString &value, setting->declaredValues()) { QAbstractButton *button = 0; - switch (setting->type()) + switch (mType) { - case CSMSettings::Type_CheckBox: - button = new QCheckBox (value, this); + case CSMSettings::Type_CheckBox: { + if(mButtons.empty()) // show only one for checkboxes + { + button = new QCheckBox (value, this); + button->setChecked (setting->defaultValues().at(0) == "true" ? true : false); + + // special visual treatment option for checkboxes + if(setting->specialValueText() != "") { + Frame::setTitle(""); + button->setText(setting->specialValueText()); + } + } + } break; case CSMSettings::Type_RadioButton: @@ -32,14 +43,17 @@ CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting, break; } - connect (button, SIGNAL (clicked (bool)), - this, SLOT (slotToggled (bool))); + if(button && (mType != CSMSettings::Type_CheckBox || mButtons.empty())) + { + connect (button, SIGNAL (clicked (bool)), + this, SLOT (slotToggled (bool))); - button->setObjectName (value); + button->setObjectName (value); - addWidget (button); + addWidget (button); - mButtons[value] = button; + mButtons[value] = button; + } } } @@ -53,8 +67,14 @@ void CSVSettings::BooleanView::slotToggled (bool state) foreach (QString key, mButtons.keys()) { - if (mButtons.value(key)->isChecked()) - values.append (key); + // checkbox values are true/false unlike radio buttons + if(mType == CSMSettings::Type_CheckBox) + values.append(mButtons.value(key)->isChecked() ? "true" : "false"); + else + { + if (mButtons.value(key)->isChecked()) + values.append (key); + } } setSelectedValues (values, false); diff --git a/apps/opencs/view/settings/booleanview.hpp b/apps/opencs/view/settings/booleanview.hpp index 55ef0bb08..53198234a 100644 --- a/apps/opencs/view/settings/booleanview.hpp +++ b/apps/opencs/view/settings/booleanview.hpp @@ -16,6 +16,7 @@ namespace CSVSettings Q_OBJECT QMap mButtons; + enum CSMSettings::SettingType mType; public: explicit BooleanView (CSMSettings::Setting *setting, diff --git a/apps/opencs/view/settings/frame.cpp b/apps/opencs/view/settings/frame.cpp index 019024776..a3df2a993 100644 --- a/apps/opencs/view/settings/frame.cpp +++ b/apps/opencs/view/settings/frame.cpp @@ -3,7 +3,7 @@ #include const QString CSVSettings::Frame::sInvisibleBoxStyle = - QString::fromUtf8("Frame { border:2px; padding 2px; margin: 2px;}"); + QString::fromUtf8("Frame { border:2px; padding: 2px; margin: 2px;}"); CSVSettings::Frame::Frame (bool isVisible, const QString &title, QWidget *parent) @@ -14,7 +14,11 @@ CSVSettings::Frame::Frame (bool isVisible, const QString &title, mVisibleBoxStyle = styleSheet(); if (!isVisible) + { + // must be Page, not a View setStyleSheet (sInvisibleBoxStyle); + mLayout->setContentsMargins(10, 15, 10, 15); + } setLayout (mLayout); } diff --git a/apps/opencs/view/settings/page.cpp b/apps/opencs/view/settings/page.cpp index afd4bff5e..ed4cdd6bc 100644 --- a/apps/opencs/view/settings/page.cpp +++ b/apps/opencs/view/settings/page.cpp @@ -1,4 +1,7 @@ #include "page.hpp" + +#include + #include "view.hpp" #include "booleanview.hpp" #include "textview.hpp" @@ -38,7 +41,18 @@ void CSVSettings::Page::setupViews void CSVSettings::Page::addView (CSMSettings::Setting *setting) { if (setting->viewType() == ViewType_Undefined) - return; + { + if(setting->specialValueText() != "") + { + // hack to put a label + addWidget(new QLabel(setting->specialValueText()), + setting->viewRow(), setting->viewColumn(), + setting->rowSpan(), setting->columnSpan()); + return; + } + else + return; + } View *view = mViewFactories[setting->viewType()]->createView(setting, this); diff --git a/apps/opencs/view/settings/rangeview.cpp b/apps/opencs/view/settings/rangeview.cpp index 8ae6caca0..c96df9859 100644 --- a/apps/opencs/view/settings/rangeview.cpp +++ b/apps/opencs/view/settings/rangeview.cpp @@ -36,8 +36,11 @@ CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting, break; } - mRangeWidget->setFixedWidth (widgetWidth (setting->widgetWidth())); - mRangeWidget->setObjectName (setting->name()); + if(mRangeWidget) + { + mRangeWidget->setFixedWidth (widgetWidth (setting->widgetWidth())); + mRangeWidget->setObjectName (setting->name()); + } addWidget (mRangeWidget); } @@ -75,13 +78,16 @@ void CSVSettings::RangeView::buildSlider (CSMSettings::Setting *setting) break; } - mRangeWidget->setProperty ("minimum", setting->minimum()); - mRangeWidget->setProperty ("maximum", setting->maximum()); - mRangeWidget->setProperty ("tracking", false); - mRangeWidget->setProperty ("singleStep", setting->singleStep()); + if(mRangeWidget) + { + mRangeWidget->setProperty ("minimum", setting->minimum()); + mRangeWidget->setProperty ("maximum", setting->maximum()); + mRangeWidget->setProperty ("tracking", false); + mRangeWidget->setProperty ("singleStep", setting->singleStep()); - connect (mRangeWidget, SIGNAL (valueChanged (int)), - this, SLOT (slotUpdateView (int))); + connect (mRangeWidget, SIGNAL (valueChanged (int)), + this, SLOT (slotUpdateView (int))); + } } void CSVSettings::RangeView::buildSpinBox (CSMSettings::Setting *setting) @@ -127,7 +133,10 @@ void CSVSettings::RangeView::buildSpinBox (CSMSettings::Setting *setting) mRangeWidget->setProperty ("prefix", setting->prefix()); mRangeWidget->setProperty ("suffix", setting->suffix()); mRangeWidget->setProperty ("wrapping", setting->wrapping()); + dynamic_cast (mRangeWidget)->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + if(setting->type() == CSMSettings::Type_SpinBox && setting->declaredValues().isEmpty()) + dynamic_cast (mRangeWidget)->setValue (setting->defaultValues().at(0).toInt()); } void CSVSettings::RangeView::slotUpdateView (int value) diff --git a/apps/opencs/view/settings/settingwindow.cpp b/apps/opencs/view/settings/settingwindow.cpp index 7cdf2bded..590b6f7a5 100644 --- a/apps/opencs/view/settings/settingwindow.cpp +++ b/apps/opencs/view/settings/settingwindow.cpp @@ -19,7 +19,7 @@ void CSVSettings::SettingWindow::createPages() QList connectedSettings; foreach (const QString &pageName, pageMap.keys()) - { + { QList pageSettings = pageMap.value (pageName); mPages.append (new Page (pageName, pageSettings, this)); @@ -129,7 +129,3 @@ void CSVSettings::SettingWindow::saveSettings() mModel->saveDefinitions(); } -void CSVSettings::SettingWindow::closeEvent (QCloseEvent *event) -{ - QApplication::focusWidget()->clearFocus(); -} diff --git a/apps/opencs/view/settings/settingwindow.hpp b/apps/opencs/view/settings/settingwindow.hpp index 2266f130d..11bceee96 100644 --- a/apps/opencs/view/settings/settingwindow.hpp +++ b/apps/opencs/view/settings/settingwindow.hpp @@ -36,8 +36,6 @@ namespace CSVSettings { protected: - virtual void closeEvent (QCloseEvent *event); - ///construct the pages to be displayed in the dialog void createPages(); diff --git a/apps/opencs/view/settings/spinbox.cpp b/apps/opencs/view/settings/spinbox.cpp index 4b1447f8f..c70fc36d1 100644 --- a/apps/opencs/view/settings/spinbox.cpp +++ b/apps/opencs/view/settings/spinbox.cpp @@ -24,7 +24,7 @@ QString CSVSettings::SpinBox::textFromValue(int val) const int CSVSettings::SpinBox::valueFromText(const QString &text) const { if (mValueList.isEmpty()) - return -1; + return text.toInt(); // TODO: assumed integer, untested error handling for alpha types if (mValueList.contains (text)) return mValueList.indexOf(text); diff --git a/apps/opencs/view/settings/view.cpp b/apps/opencs/view/settings/view.cpp index 69109e2b3..f95bb5c03 100644 --- a/apps/opencs/view/settings/view.cpp +++ b/apps/opencs/view/settings/view.cpp @@ -22,6 +22,9 @@ CSVSettings::View::View(CSMSettings::Setting *setting, setObjectName (setting->name()); buildView(); buildModel (setting); + // apply stylesheet to view's frame if exists + if(setting->styleSheet() != "") + Frame::setStyleSheet (setting->styleSheet()); } void CSVSettings::View::buildModel (const CSMSettings::Setting *setting) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 57069bec0..8790601ea 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -392,7 +392,6 @@ void CSVWorld::EditWidget::remake(int row) mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); - this->setMinimumWidth(325); /// \todo replace hardcoded value with a user setting this->setWidget(mMainWidget); this->setWidgetResizable(true); } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 52bd47b54..d10eebb30 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -247,6 +247,8 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW mToolbar = toolbar; connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus())); + connect (this, SIGNAL (updateSceneUserSetting(const QString &, const QStringList &)), + mScene, SLOT (updateUserSetting(const QString &, const QStringList &))); connect (mToolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus())); mLayout->addWidget (mToolbar, 0); @@ -254,4 +256,9 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW mScene->selectDefaultNavigationMode(); setFocusProxy (mScene); -} \ No newline at end of file +} + +void CSVWorld::SceneSubView::updateUserSetting (const QString &key, const QStringList &list) +{ + emit updateSceneUserSetting(key, list); +} diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index b9dcdd6a3..c0905f0d6 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -82,6 +82,14 @@ namespace CSVWorld void cellSelectionChanged (const CSMWorld::UniversalId& id); void handleDrop(const std::vector& data); + + public slots: + + void updateUserSetting (const QString &, const QStringList &); + + signals: + + void updateSceneUserSetting (const QString &, const QStringList &); }; } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2417091f8..e37cd1391 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -40,7 +40,7 @@ add_openmw_dir (mwgui merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog - recharge mode videowidget backgroundimage itemwidget screenfader + recharge mode videowidget backgroundimage itemwidget screenfader debugwindow ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index e8286bdd1..58fa221e6 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,7 +177,7 @@ namespace MWBase virtual void changeCell(MWWorld::CellStore* cell) = 0; ///< change the active cell - virtual void setPlayerPos(const float x, const float y) = 0; + virtual void setPlayerPos(int cellX, int cellY, const float x, const float y) = 0; ///< set player position in map space virtual void setPlayerDir(const float x, const float y) = 0; @@ -337,6 +337,8 @@ namespace MWBase virtual void fadeScreenTo(const int percent, const float time) = 0; /// Darken the screen by \a factor (1.0 = no darkening). Works independently from screen fading. virtual void setScreenFactor (float factor) = 0; + + virtual void toggleDebugWindow() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2055aeeae..73fa92529 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -119,6 +119,7 @@ namespace MWBase virtual void setWaterHeight(const float height) = 0; virtual bool toggleWater() = 0; + virtual bool toggleWorld() = 0; virtual void adjustSky() = 0; @@ -136,7 +137,7 @@ namespace MWBase virtual MWWorld::LocalScripts& getLocalScripts() = 0; virtual bool hasCellChanged() const = 0; - ///< Has the player moved to a different cell, since the last frame? + ///< Has the set of active cells changed, since the last frame? virtual bool isCellExterior() const = 0; diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 520a32ef8..b2befc3ba 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -76,7 +76,7 @@ MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) throw std::runtime_error("Item to copy needs to be from a different container!"); - return *source.getClass().getContainerStore(source).add(item.mBase, count, source, setNewOwner); + return *source.getClass().getContainerStore(source).add(item.mBase, count, source); } void ContainerItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/debugwindow.cpp b/apps/openmw/mwgui/debugwindow.cpp new file mode 100644 index 000000000..fab386bda --- /dev/null +++ b/apps/openmw/mwgui/debugwindow.cpp @@ -0,0 +1,116 @@ +#include "debugwindow.hpp" + + +#include + +namespace +{ + void bulletDumpRecursive(CProfileIterator* pit, int spacing, std::stringstream& os) + { + pit->First(); + if (pit->Is_Done()) + return; + + float accumulated_time=0,parent_time = pit->Is_Root() ? CProfileManager::Get_Time_Since_Reset() : pit->Get_Current_Parent_Total_Time(); + int i,j; + int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset(); + for (i=0;iGet_Current_Parent_Name())+" (total running time: "+MyGUI::utility::toString(parent_time,3)+" ms) ---\n"; + os << s; + //float totalTime = 0.f; + + int numChildren = 0; + + for (i = 0; !pit->Is_Done(); i++,pit->Next()) + { + numChildren++; + float current_total_time = pit->Get_Current_Total_Time(); + accumulated_time += current_total_time; + float fraction = parent_time > SIMD_EPSILON ? (current_total_time / parent_time) * 100 : 0.f; + + for (j=0;jGet_Current_Name()+" ("+MyGUI::utility::toString(fraction,2)+" %) :: "+MyGUI::utility::toString(ms,3)+" ms / frame ("+MyGUI::utility::toString(pit->Get_Current_Total_Calls())+" calls)\n"; + os << s; + //totalTime += current_total_time; + //recurse into children + } + + if (parent_time < accumulated_time) + { + os << "what's wrong\n"; + } + for (i=0;i SIMD_EPSILON ? ((parent_time - accumulated_time) / parent_time) * 100 : 0.f; + s = "Unaccounted: ("+MyGUI::utility::toString(unaccounted,3)+" %) :: "+MyGUI::utility::toString(parent_time - accumulated_time,3)+" ms\n"; + os << s; + + for (i=0;iEnter_Child(i); + bulletDumpRecursive(pit, spacing+3, os); + pit->Enter_Parent(); + } + } + + void bulletDumpAll(std::stringstream& os) + { + CProfileIterator* profileIterator = 0; + profileIterator = CProfileManager::Get_Iterator(); + + bulletDumpRecursive(profileIterator, 0, os); + + CProfileManager::Release_Iterator(profileIterator); + } +} + + +namespace MWGui +{ + + DebugWindow::DebugWindow() + : WindowBase("openmw_debug_window.layout") + { + getWidget(mTabControl, "TabControl"); + + // Ideas for other tabs: + // - Texture / compositor texture viewer + // - Log viewer + // - Material editor + // - Shader editor + + MyGUI::TabItem* item = mTabControl->addItem("Physics Profiler"); + mBulletProfilerEdit = item->createWidgetReal + ("LogEdit", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Stretch); + + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + mMainWidget->setSize(viewSize); + } + + void DebugWindow::onFrame(float dt) + { + if (!isVisible()) + return; + + static float timer = 0; + timer -= dt; + + if (timer > 0) + return; + timer = 1; + + std::stringstream stream; + bulletDumpAll(stream); + + if (mBulletProfilerEdit->isTextSelection()) // pause updating while user is trying to copy text + return; + + size_t previousPos = mBulletProfilerEdit->getVScrollPosition(); + mBulletProfilerEdit->setCaption(stream.str()); + mBulletProfilerEdit->setVScrollPosition(std::min(previousPos, mBulletProfilerEdit->getVScrollRange()-1)); + } + +} diff --git a/apps/openmw/mwgui/debugwindow.hpp b/apps/openmw/mwgui/debugwindow.hpp new file mode 100644 index 000000000..af5b914ea --- /dev/null +++ b/apps/openmw/mwgui/debugwindow.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_MWGUI_DEBUGWINDOW_H +#define OPENMW_MWGUI_DEBUGWINDOW_H + +#include "windowbase.hpp" + +namespace MWGui +{ + + class DebugWindow : public WindowBase + { + public: + DebugWindow(); + + void onFrame(float dt); + + private: + MyGUI::TabControl* mTabControl; + + MyGUI::EditBox* mBulletProfilerEdit; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index a3f82e65d..196e7d8c7 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -562,12 +562,11 @@ namespace MWGui MyGUI::Button* byeButton; getWidget(byeButton, "ByeButton"); - if(MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye) { - byeButton->setEnabled(false); - } - else { - byeButton->setEnabled(true); - } + bool goodbyeEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() || mGoodbye; + byeButton->setEnabled(goodbyeEnabled); + + bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye; + mTopicsList->setEnabled(topicsEnabled); } void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link) @@ -657,7 +656,6 @@ namespace MWGui { mLinks.push_back(new Goodbye()); mGoodbye = true; - mTopicsList->setEnabled(false); mEnabled = false; updateHistory(); } diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 45a1858d2..0ab56200d 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -201,12 +201,23 @@ struct JournalViewModelImpl : JournalViewModel std::set visitedQuests; + // Note that for purposes of the journal GUI, quests are identified by the name, not the ID, so several + // different quest IDs can end up in the same quest log. A quest log should be considered finished + // when any quest ID in that log is finished. for (MWBase::Journal::TQuestIter i = journal->questBegin (); i != journal->questEnd (); ++i) { - if (active_only && i->second.isFinished ()) + const MWDialogue::Quest& quest = i->second; + + bool isFinished = false; + for (MWBase::Journal::TQuestIter j = journal->questBegin (); j != journal->questEnd (); ++j) + { + if (quest.getName() == j->second.getName() && j->second.isFinished()) + isFinished = true; + } + + if (active_only && isFinished) continue; - const MWDialogue::Quest& quest = i->second; // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed // to appear in the quest book. diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 4139a7d24..0bcd5062d 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -490,7 +490,7 @@ namespace unsigned int & page = mStates.top ().mPage; Book book = mStates.top ().mBook; - if (page < book->pageCount () - 2) + if (page+2 < book->pageCount()) { page += 2; updateShowingPages (); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 37bc4d1e3..0262c4d0e 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -159,8 +159,6 @@ namespace MWGui , mLocalMap(NULL) , mPrefix() , mChanged(true) - , mLastPositionX(0.0f) - , mLastPositionY(0.0f) , mLastDirectionX(0.0f) , mLastDirectionY(0.0f) , mCompass(NULL) @@ -425,24 +423,22 @@ namespace MWGui mLocalMap->getParent()->_updateChilds(); } - void LocalMapBase::setPlayerPos(const float x, const float y) + void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny) { - updateMagicMarkers(); - - if (x == mLastPositionX && y == mLastPositionY) - return; + MyGUI::IntPoint pos(widgetSize+nx*widgetSize-16, widgetSize+ny*widgetSize-16); + pos.left += (cellX - mCurX) * widgetSize; + pos.top -= (cellY - mCurY) * widgetSize; - notifyPlayerUpdate (); - - MyGUI::IntSize size = mLocalMap->getCanvasSize(); - MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); - MyGUI::IntCoord viewsize = mLocalMap->getCoord(); - MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); - mLocalMap->setViewOffset(pos); + if (pos != mCompass->getPosition()) + { + notifyPlayerUpdate (); - mCompass->setPosition(MyGUI::IntPoint(widgetSize+x*widgetSize-16, widgetSize+y*widgetSize-16)); - mLastPositionX = x; - mLastPositionY = y; + mCompass->setPosition(pos); + MyGUI::IntPoint middle (pos.left+16, pos.top+16); + MyGUI::IntCoord viewsize = mLocalMap->getCoord(); + MyGUI::IntPoint viewOffset(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); + mLocalMap->setViewOffset(viewOffset); + } } void LocalMapBase::setPlayerDir(const float x, const float y) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index e9c0c1a69..a8aaa8e45 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -75,7 +75,7 @@ namespace MWGui void setCellPrefix(const std::string& prefix); void setActiveCell(const int x, const int y, bool interior=false); void setPlayerDir(const float x, const float y); - void setPlayerPos(const float x, const float y); + void setPlayerPos(int cellX, int cellY, const float nx, const float ny); void onFrame(float dt); @@ -129,8 +129,6 @@ namespace MWGui float mMarkerUpdateTimer; - float mLastPositionX; - float mLastPositionY; float mLastDirectionX; float mLastDirectionY; }; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 80f38ba40..d4520aad7 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -157,12 +157,11 @@ namespace MWGui , mMaxTime(0) { // defines - mBottomPadding = 20; - mNextBoxPadding = 20; + mBottomPadding = 48; + mNextBoxPadding = 4; getWidget(mMessageWidget, "message"); - mMessageWidget->setOverflowToTheLeft(true); mMessageWidget->setCaptionWithReplacing(mMessage); } @@ -178,7 +177,7 @@ namespace MWGui int MessageBox::getHeight () { - return mMainWidget->getHeight()+mNextBoxPadding; // 20 is the padding between this and the next MessageBox + return mMainWidget->getHeight()+mNextBoxPadding; } @@ -204,7 +203,6 @@ namespace MWGui getWidget(mMessageWidget, "message"); getWidget(mButtonsWidget, "buttons"); - mMessageWidget->setOverflowToTheLeft(true); mMessageWidget->setSize(400, mMessageWidget->getHeight()); mMessageWidget->setCaptionWithReplacing(message); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index e3bec3789..2bfc4f141 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -41,12 +41,11 @@ namespace MWGui getWidget(mHeadRotate, "HeadRotate"); - // Mouse wheel step is hardcoded to 50 in MyGUI 3.2 ("FIXME"). - // Give other steps the same value to accomodate. mHeadRotate->setScrollRange(1000); mHeadRotate->setScrollPosition(500); mHeadRotate->setScrollViewPage(50); mHeadRotate->setScrollPage(50); + mHeadRotate->setScrollWheelPage(50); mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); // Set up next/previous buttons diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 3920a5dcb..66c7a9238 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -322,7 +322,8 @@ namespace MWGui if (i == pos) mCurrentSlot = &*it; } - assert(mCurrentSlot && "Can't find selected slot"); + if (!mCurrentSlot) + throw std::runtime_error("Can't find selected slot"); std::stringstream text; time_t time = mCurrentSlot->mTimeStamp; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index cd378aadf..dd6339b4c 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -100,6 +100,15 @@ namespace MWGui if (spell->mData.mType!=ESM::Spell::ST_Spell) continue; // don't try to sell diseases, curses or powers + if (actor.getClass().isNpc()) + { + const ESM::Race* race = + MWBase::Environment::get().getWorld()->getStore().get().find( + actor.get()->mBase->mRace); + if (race->mPowers.exists(spell->mId)) + continue; + } + if (playerHasSpell(iter->first)) continue; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 1116d6543..e2fac9d6d 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -422,12 +422,12 @@ namespace MWGui y *= 1.5; } - mSpell.mData.mCost = int(y); - ESM::EffectList effectList; effectList.mList = mEffects; mSpell.mEffects = effectList; + mSpell.mData.mCost = int(y); mSpell.mData.mType = ESM::Spell::ST_Spell; + mSpell.mData.mFlags = 0; mMagickaCost->setCaption(boost::lexical_cast(int(y))); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index e1283c89d..3abfac997 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -119,7 +119,7 @@ namespace MWGui return mBorrowedToUs; } - void TradeItemModel::transferItems(const MWWorld::Ptr& transferFrom) + void TradeItemModel::transferItems() { std::vector::iterator it = mBorrowedToUs.begin(); for (; it != mBorrowedToUs.end(); ++it) @@ -135,11 +135,9 @@ namespace MWGui if (i == sourceModel->getItemCount()) throw std::runtime_error("The borrowed item disappeared"); + // reset owner while copying, but only for items bought by the player + bool setNewOwner = (mMerchant.isEmpty()); const ItemStack& item = sourceModel->getItem(i); - - bool setNewOwner = Misc::StringUtils::ciEqual(item.mBase.getCellRef().getOwner(), transferFrom.getCellRef().getRefId()) - || item.mBase.getCellRef().getOwner().empty(); - // copy the borrowed items to our model copyItem(item, it->mCount, setNewOwner); // then remove them from the source model diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp index c463bf40b..1bfee9b2a 100644 --- a/apps/openmw/mwgui/tradeitemmodel.hpp +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -30,8 +30,7 @@ namespace MWGui void returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count); /// Permanently transfers items that were borrowed to us from another model to this model - /// @param transferFrom the actor that lent us the items - void transferItems (const MWWorld::Ptr& transferFrom); + void transferItems (); /// Aborts trade void abort(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 081a1e2c2..a4e5bbc16 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -370,8 +370,8 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); // make the item transfer - mTradeModel->transferItems(player); - playerItemModel->transferItems(mPtr); + mTradeModel->transferItems(); + playerItemModel->transferItems(); // transfer the gold if (mCurrentBalance != 0) diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 432510a3e..9c12b04ef 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -76,6 +76,8 @@ void WindowModal::close() NoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget) : mDrag(drag), mWidget(widget), mTransparent(false) { + if (!mWidget) + throw std::runtime_error("NoDrop needs a non-NULL widget!"); } void NoDrop::onFrame(float dt) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index fabdf4dae..16295d3a5 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -72,6 +72,7 @@ #include "backgroundimage.hpp" #include "itemwidget.hpp" #include "screenfader.hpp" +#include "debugwindow.hpp" namespace MWGui { @@ -120,6 +121,7 @@ namespace MWGui , mVideoBackground(NULL) , mVideoWidget(NULL) , mScreenFader(NULL) + , mDebugWindow(NULL) , mTranslationDataStorage (translationDataStorage) , mCharGen(NULL) , mInputBlocker(NULL) @@ -266,6 +268,7 @@ namespace MWGui mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); trackWindow(mCompanionWindow, "companion"); mScreenFader = new ScreenFader(); + mDebugWindow = new DebugWindow(); mInputBlocker = MyGUI::Gui::getInstance().createWidget("",0,0,w,h,MyGUI::Align::Stretch,"Overlay"); @@ -357,6 +360,7 @@ namespace MWGui delete mRecharge; delete mCompanionWindow; delete mScreenFader; + delete mDebugWindow; cleanupGarbage(); @@ -859,6 +863,8 @@ namespace MWGui mCompanionWindow->onFrame(); mScreenFader->update(frameDuration); + + mDebugWindow->onFrame(frameDuration); } void WindowManager::changeCell(MWWorld::CellStore* cell) @@ -874,9 +880,6 @@ namespace MWGui mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->getCell()->getGridX (), cell->getCell()->getGridY ()); mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY()); - - mMap->setCellPrefix("Cell"); - mHud->setCellPrefix("Cell"); } else { @@ -894,14 +897,20 @@ namespace MWGui void WindowManager::setActiveMap(int x, int y, bool interior) { + if (!interior) + { + mMap->setCellPrefix("Cell"); + mHud->setCellPrefix("Cell"); + } + mMap->setActiveCell(x,y, interior); mHud->setActiveCell(x,y, interior); } - void WindowManager::setPlayerPos(const float x, const float y) + void WindowManager::setPlayerPos(int cellX, int cellY, const float x, const float y) { - mMap->setPlayerPos(x,y); - mHud->setPlayerPos(x,y); + mMap->setPlayerPos(cellX, cellY, x, y); + mHud->setPlayerPos(cellX, cellY, x, y); } void WindowManager::setPlayerDir(const float x, const float y) @@ -1749,4 +1758,9 @@ namespace MWGui SDL_free(text); } + void WindowManager::toggleDebugWindow() + { + mDebugWindow->setVisible(!mDebugWindow->isVisible()); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index e7853b84f..1dae77641 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -89,6 +89,7 @@ namespace MWGui class VideoWidget; class WindowModal; class ScreenFader; + class DebugWindow; class WindowManager : public MWBase::WindowManager { @@ -182,7 +183,7 @@ namespace MWGui virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty virtual void changeCell(MWWorld::CellStore* cell); ///< change the active cell - virtual void setPlayerPos(const float x, const float y); ///< set player position in map space + virtual void setPlayerPos(int cellX, int cellY, const float x, const float y); ///< set player position in map space virtual void setPlayerDir(const float x, const float y); ///< set player view direction in map space virtual void setFocusObject(const MWWorld::Ptr& focus); @@ -333,6 +334,8 @@ namespace MWGui /// Darken the screen by \a factor (1.0 = no darkening). Works independently from screen fading. virtual void setScreenFactor (float factor); + virtual void toggleDebugWindow(); + private: bool mConsoleOnlyScripts; @@ -386,6 +389,7 @@ namespace MWGui MyGUI::ImageBox* mVideoBackground; VideoWidget* mVideoWidget; ScreenFader* mScreenFader; + DebugWindow* mDebugWindow; Translation::Storage& mTranslationDataStorage; Cursor* mSoftwareCursor; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 86b01cdc4..10cd34749 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -279,6 +279,9 @@ namespace MWInput case A_ToggleHUD: MWBase::Environment::get().getWindowManager()->toggleGui(); break; + case A_ToggleDebug: + MWBase::Environment::get().getWindowManager()->toggleDebugWindow(); + break; case A_QuickSave: quickSave(); break; @@ -902,6 +905,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey10] = SDL_SCANCODE_0; defaultKeyBindings[A_Screenshot] = SDL_SCANCODE_F12; defaultKeyBindings[A_ToggleHUD] = SDL_SCANCODE_F11; + defaultKeyBindings[A_ToggleDebug] = SDL_SCANCODE_F10; defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK; defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5; defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9; @@ -932,9 +936,11 @@ namespace MWInput { clearAllBindings (control); - if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) + if (defaultKeyBindings.find(i) != defaultKeyBindings.end() + && !mInputBinder->isKeyBound(defaultKeyBindings[i])) mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE); - else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) + else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end() + && !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])) mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index a94b61c8b..346e02ff9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -259,6 +259,8 @@ namespace MWInput A_ToggleHUD, + A_ToggleDebug, + A_Last // Marker for the last item }; }; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index efca99b4e..82f60221b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -470,13 +470,13 @@ namespace MWMechanics return; MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); - const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); // restore fatigue - float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); - float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); + const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); + static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); + static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * endurance; @@ -1122,20 +1122,21 @@ namespace MWMechanics int hostilesCount = 0; // need to know this to play Battle music + // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this + // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) + // This distance could be made configurable later, but the setting must be marked with a big warning: + // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) + const float sqrProcessingDistance = 7168*7168; + // AI and magic effects update for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { updateActor(iter->first, duration); - - // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this - // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) - // This distance could be made configurable later, but the setting must be marked with a big warning: - // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(iter->first.getRefData().getPosition().pos)) - <= 7168*7168) + <= sqrProcessingDistance) { if (timerUpdateAITargets == 0) { @@ -1182,6 +1183,11 @@ namespace MWMechanics CharacterController* playerCharacter = NULL; for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + if (iter->first != player && + Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(iter->first.getRefData().getPosition().pos)) + > sqrProcessingDistance) + continue; + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( ESM::MagicEffect::Paralyze).getMagnitude() > 0) iter->second->skipAnim(); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 98fc64090..324bef322 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -92,7 +92,11 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { - if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete + ESM::Pathgrid::Point point(mX,mY,mZ); + point.mAutogenerated = 0; + point.mConnectionNum = 0; + point.mUnknown = 0; + if(pathTo(actor,point,duration)) //Returns true on path complete return true; mMaxDist = 450; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 0341c9745..f28809d4f 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -260,15 +260,14 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) { - if(mPackages.front()->getPriority() <= package.getPriority()) + if((*it)->getPriority() <= package.getPriority()) { mPackages.insert(it,package.clone()); return; } } - if(mPackages.empty()) - mPackages.push_front (package.clone()); + mPackages.push_front (package.clone()); } AiPackage* MWMechanics::AiSequence::getActivePackage() diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 24e7b5aa1..90c180817 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -133,7 +133,7 @@ void MWMechanics::Alchemy::updateEffects() std::set effects (listEffects()); // general alchemy factor - float x = getChance(); + float x = getAlchemyFactor(); x *= mTools[ESM::Apparatus::MortarPestle].get()->mBase->mData.mQuality; x *= MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionStrengthMult")->getFloat(); @@ -306,18 +306,15 @@ void MWMechanics::Alchemy::increaseSkill() mAlchemist.getClass().skillUsageSucceeded (mAlchemist, ESM::Skill::Alchemy, 0); } -float MWMechanics::Alchemy::getChance() const +float MWMechanics::Alchemy::getAlchemyFactor() const { const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist); const NpcStats& npcStats = mAlchemist.getClass().getNpcStats (mAlchemist); - if (beginEffects() == endEffects()) - return 0.f; - return (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + - 0.1 * creatureStats.getAttribute (1).getModified() - + 0.1 * creatureStats.getAttribute (7).getModified()); + 0.1 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + + 0.1 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()); } int MWMechanics::Alchemy::countIngredients() const @@ -461,7 +458,14 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na if (listEffects().empty()) return Result_NoEffects; - if (getChance() (RAND_MAX)*100) + if (beginEffects() == endEffects()) + { + // all effects were nullified due to insufficient skill + removeIngredients(); + return Result_RandomFailure; + } + + if (getAlchemyFactor() (RAND_MAX)*100) { removeIngredients(); return Result_RandomFailure; diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index a2429ca8e..e6b8c6650 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -69,8 +69,7 @@ namespace MWMechanics void increaseSkill(); ///< Increase alchemist's skill. - float getChance() const; - ///< Return chance of success. + float getAlchemyFactor() const; int countIngredients() const; diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 255decdf7..7b8c43a06 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -72,7 +72,7 @@ namespace MWMechanics if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) continue; - if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) + if (race && race->mPowers.exists(spell->mId)) continue; if (!attrSkillCheck(spell, actorSkills, actorAttributes)) @@ -220,7 +220,7 @@ namespace MWMechanics if (spell->mData.mFlags & ESM::Spell::F_Always) return 100.f; - float skillTerm; + float skillTerm = 0; if (effectiveSchool != -1) skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)]; else diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8f1a8f718..7c7ea0c95 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1611,7 +1611,7 @@ void CharacterController::update(float duration) if(mMovementAnimationControlled && mPtr.getClass().isActor()) world->queueMovement(mPtr, moved); } - else if (mAnimation) + else mAnimation->updateEffects(duration); mSkipAnim = false; diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index fa97f1541..a973c0e35 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -25,6 +25,8 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find( "fDiseaseXferChance")->getFloat(); + MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects(); + Spells& spells = carrier.getClass().getCreatureStats(carrier).getSpells(); for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { @@ -33,26 +35,16 @@ namespace MWMechanics if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId)) continue; - bool hasCorprusEffect = false; - for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) - { - if (effectIt->mEffectID == ESM::MagicEffect::Corprus) - { - hasCorprusEffect = true; - break; - } - } - float resist = 0.f; - if (hasCorprusEffect) - resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCorprusDisease).getMagnitude() - - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCorprusDisease).getMagnitude()); + if (spells.hasCorprusEffect(spell)) + resist = 1.f - 0.01 * (actorEffects.get(ESM::MagicEffect::ResistCorprusDisease).getMagnitude() + - actorEffects.get(ESM::MagicEffect::WeaknessToCorprusDisease).getMagnitude()); else if (spell->mData.mType == ESM::Spell::ST_Disease) - resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCommonDisease).getMagnitude() - - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCommonDisease).getMagnitude()); + resist = 1.f - 0.01 * (actorEffects.get(ESM::MagicEffect::ResistCommonDisease).getMagnitude() + - actorEffects.get(ESM::MagicEffect::WeaknessToCommonDisease).getMagnitude()); else if (spell->mData.mType == ESM::Spell::ST_Blight) - resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistBlightDisease).getMagnitude() - - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToBlightDisease).getMagnitude()); + resist = 1.f - 0.01 * (actorEffects.get(ESM::MagicEffect::ResistBlightDisease).getMagnitude() + - actorEffects.get(ESM::MagicEffect::WeaknessToBlightDisease).getMagnitude()); else continue; diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 63ffbc7e8..c134942b8 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -53,6 +53,9 @@ namespace MWMechanics MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); ESM::Enchantment enchantment; enchantment.mData.mCharge = getGemCharge(); + enchantment.mData.mAutocalc = 0; + enchantment.mData.mType = mCastStyle; + enchantment.mData.mCost = getEnchantPoints(); store.remove(mSoulGemPtr, 1, player); @@ -72,8 +75,6 @@ namespace MWMechanics { enchantment.mData.mCharge=0; } - enchantment.mData.mType = mCastStyle; - enchantment.mData.mCost = getEnchantPoints(); enchantment.mEffects = mEffectList; // Apply the enchantment diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6ca4a4336..ef03d285c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -566,11 +566,14 @@ namespace MWMechanics MWWorld::LiveCellRef* player = playerPtr.get(); const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + static const float fDispRaceMod = gmst.find("fDispRaceMod")->getFloat(); if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace)) - x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispRaceMod")->getFloat(); + x += fDispRaceMod; - x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispPersonalityMult")->getFloat() - * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - MWBase::Environment::get().getWorld()->getStore().get().find("fDispPersonalityBase")->getFloat()); + static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->getFloat(); + static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->getFloat(); + x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase); float reaction = 0; int rank = 0; @@ -606,16 +609,23 @@ namespace MWMechanics reaction = 0; rank = 0; } - x += (MWBase::Environment::get().getWorld()->getStore().get().find("fDispFactionRankMult")->getFloat() * rank - + MWBase::Environment::get().getWorld()->getStore().get().find("fDispFactionRankBase")->getFloat()) - * MWBase::Environment::get().getWorld()->getStore().get().find("fDispFactionMod")->getFloat() * reaction; - x -= MWBase::Environment::get().getWorld()->getStore().get().find("fDispCrimeMod")->getFloat() * playerStats.getBounty(); + static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->getFloat(); + static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->getFloat(); + static const float fDispFactionMod = gmst.find("fDispFactionMod")->getFloat(); + x += (fDispFactionRankMult * rank + + fDispFactionRankBase) + * fDispFactionMod * reaction; + + static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->getFloat(); + static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->getFloat(); + x -= fDispCrimeMod * playerStats.getBounty(); if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) - x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispDiseaseMod")->getFloat(); + x += fDispDiseaseMod; + static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->getFloat(); if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) - x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispWeaponDrawn")->getFloat(); + x += fDispWeaponDrawn; x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).getMagnitude(); diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index 7caeba6e8..ab799ffe1 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -74,7 +74,7 @@ namespace MWMechanics TIterator end() const; - bool hasSpell(const std::string& spell) { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); } + bool hasSpell(const std::string& spell) const { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); } void add (const std::string& spell); ///< Adding a spell that is already listed in *this is a no-op. diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index bb196f282..1850df904 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -19,6 +19,7 @@ namespace MWRender Camera::Camera (Ogre::Camera *camera) : mCamera(camera), mCameraNode(NULL), + mCameraPosNode(NULL), mAnimation(NULL), mFirstPersonView(true), mPreviewMode(false), @@ -26,8 +27,8 @@ namespace MWRender mNearest(30.f), mFurthest(800.f), mIsNearest(false), - mHeight(128.f), - mCameraDistance(300.f), + mHeight(124.f), + mCameraDistance(192.f), mDistanceAdjusted(false), mVanityToggleQueued(false), mViewModeToggleQueued(false) @@ -66,12 +67,16 @@ namespace MWRender } Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); - if (!mVanity.enabled && !mPreviewMode) { - mCamera->getParentNode()->setOrientation(xr); - } else { + Ogre::Quaternion orient = xr; + if (mVanity.enabled || mPreviewMode) { Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); - mCamera->getParentNode()->setOrientation(zr * xr); + orient = zr * xr; } + + if (isFirstPerson()) + mCamera->getParentNode()->setOrientation(orient); + else + mCameraNode->setOrientation(orient); } const std::string &Camera::getHandle() const @@ -79,20 +84,40 @@ namespace MWRender return mTrackingPtr.getRefData().getHandle(); } - void Camera::attachTo(const MWWorld::Ptr &ptr) + Ogre::SceneNode* Camera::attachTo(const MWWorld::Ptr &ptr) { mTrackingPtr = ptr; Ogre::SceneNode *node = mTrackingPtr.getRefData().getBaseNode()->createChildSceneNode(Ogre::Vector3(0.0f, 0.0f, mHeight)); + node->setInheritScale(false); + Ogre::SceneNode *posNode = node->createChildSceneNode(); + posNode->setInheritScale(false); if(mCameraNode) { node->setOrientation(mCameraNode->getOrientation()); - node->setPosition(mCameraNode->getPosition()); - node->setScale(mCameraNode->getScale()); + posNode->setPosition(mCameraPosNode->getPosition()); mCameraNode->getCreator()->destroySceneNode(mCameraNode); + mCameraNode->getCreator()->destroySceneNode(mCameraPosNode); } mCameraNode = node; - if(!mCamera->isAttached()) - mCameraNode->attachObject(mCamera); + mCameraPosNode = posNode; + + if (!isFirstPerson()) + { + mCamera->detachFromParent(); + mCameraPosNode->attachObject(mCamera); + } + + return mCameraPosNode; + } + + void Camera::setPosition(const Ogre::Vector3& position) + { + mCameraPosNode->setPosition(position); + } + + void Camera::setPosition(float x, float y, float z) + { + setPosition(Ogre::Vector3(x,y,z)); } void Camera::updateListener() @@ -155,9 +180,9 @@ namespace MWRender processViewChange(); if (mFirstPersonView) { - mCamera->setPosition(0.f, 0.f, 0.f); + setPosition(0.f, 0.f, 0.f); } else { - mCamera->setPosition(0.f, 0.f, mCameraDistance); + setPosition(0.f, 0.f, mCameraDistance); } } @@ -172,7 +197,7 @@ namespace MWRender { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later - if (!mPreviewMode) + if (isFirstPerson() && !mAnimation->upperBodyReady()) { mVanityToggleQueued = true; return false; @@ -191,14 +216,14 @@ namespace MWRender Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { rot.x = Ogre::Degree(-30.f).valueRadians(); - mMainCam.offset = mCamera->getPosition().z; + mMainCam.offset = mCameraPosNode->getPosition().z; } else { rot.x = getPitch(); offset = mMainCam.offset; } rot.z = getYaw(); - mCamera->setPosition(0.f, 0.f, offset); + setPosition(0.f, 0.f, offset); rotateCamera(rot, false); return true; @@ -215,7 +240,7 @@ namespace MWRender mPreviewMode = enable; processViewChange(); - float offset = mCamera->getPosition().z; + float offset = mCameraPosNode->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; offset = mPreviewCam.offset; @@ -224,7 +249,7 @@ namespace MWRender offset = mMainCam.offset; } - mCamera->setPosition(0.f, 0.f, offset); + setPosition(0.f, 0.f, offset); } void Camera::setSneakOffset(float offset) @@ -283,7 +308,7 @@ namespace MWRender float Camera::getCameraDistance() const { - return mCamera->getPosition().z; + return mCameraPosNode->getPosition().z; } void Camera::setCameraDistance(float dist, bool adjust, bool override) @@ -295,7 +320,7 @@ namespace MWRender Ogre::Vector3 v(0.f, 0.f, dist); if (adjust) { - v += mCamera->getPosition(); + v += mCameraPosNode->getPosition(); } if (v.z >= mFurthest) { v.z = mFurthest; @@ -305,7 +330,7 @@ namespace MWRender v.z = mNearest; mIsNearest = true; } - mCamera->setPosition(v); + setPosition(v); if (override) { if (mVanity.enabled || mPreviewMode) { @@ -322,9 +347,9 @@ namespace MWRender { if (mDistanceAdjusted) { if (mVanity.enabled || mPreviewMode) { - mCamera->setPosition(0, 0, mPreviewCam.offset); + setPosition(0, 0, mPreviewCam.offset); } else if (!mFirstPersonView) { - mCamera->setPosition(0, 0, mCameraDistance); + setPosition(0, 0, mCameraDistance); } } mDistanceAdjusted = false; @@ -355,10 +380,10 @@ namespace MWRender Ogre::TagPoint *tag = mAnimation->attachObjectToBone("Head", mCamera); tag->setInheritOrientation(false); } - else + else { mAnimation->setViewMode(NpcAnimation::VM_Normal); - mCameraNode->attachObject(mCamera); + mCameraPosNode->attachObject(mCamera); } rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } @@ -368,8 +393,7 @@ namespace MWRender mCamera->getParentSceneNode()->needUpdate(true); camera = mCamera->getRealPosition(); - focal = Ogre::Vector3((mCamera->getParentNode()->_getFullTransform() * - Ogre::Vector4(0.0f, 0.0f, 0.0f, 1.0f)).ptr()); + focal = mCameraNode->_getDerivedPosition(); } void Camera::togglePlayerLooking(bool enable) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index f7b0ae6e8..c542dc96c 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -27,6 +27,7 @@ namespace MWRender Ogre::Camera *mCamera; Ogre::SceneNode *mCameraNode; + Ogre::SceneNode *mCameraPosNode; NpcAnimation *mAnimation; @@ -52,6 +53,9 @@ namespace MWRender /// Updates sound manager listener data void updateListener(); + void setPosition(const Ogre::Vector3& position); + void setPosition(float x, float y, float z); + public: Camera(Ogre::Camera *camera); ~Camera(); @@ -72,7 +76,7 @@ namespace MWRender const std::string &getHandle() const; /// Attach camera to object - void attachTo(const MWWorld::Ptr &); + Ogre::SceneNode* attachTo(const MWWorld::Ptr &); /// @param Force view mode switch, even if currently not allowed by the animation. void toggleViewMode(bool force=false); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 85f73ab7e..f4388eec5 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -25,7 +25,7 @@ using namespace MWRender; using namespace Ogre; LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering) : - mInterior(false), mCellX(0), mCellY(0) + mInterior(false) { mRendering = rend; mRenderingManager = rendering; @@ -522,10 +522,9 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni { x = std::ceil(pos.x / sSize)-1; y = std::ceil(pos.y / sSize)-1; - mCellX = x; - mCellY = y; } - MWBase::Environment::get().getWindowManager()->setActiveMap(x,y,mInterior); + else + MWBase::Environment::get().getWindowManager()->setActiveMap(x,y,mInterior); // convert from world coordinates to texture UV coordinates std::string texBaseName; @@ -540,7 +539,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni texBaseName = mInteriorName + "_"; } - MWBase::Environment::get().getWindowManager()->setPlayerPos(u, v); + MWBase::Environment::get().getWindowManager()->setPlayerPos(x, y, u, v); MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, playerdirection.y); // explore radius (squared) diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index b531c3e29..4c60cbb11 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -134,7 +134,6 @@ namespace MWRender Ogre::RenderTarget* mRenderTarget; bool mInterior; - int mCellX, mCellY; Ogre::AxisAlignedBox mBounds; std::string mInteriorName; }; diff --git a/apps/openmw/mwrender/refraction.cpp b/apps/openmw/mwrender/refraction.cpp index 7d728b721..164380866 100644 --- a/apps/openmw/mwrender/refraction.cpp +++ b/apps/openmw/mwrender/refraction.cpp @@ -50,7 +50,8 @@ namespace MWRender void Refraction::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) { - mParentCamera->getParentSceneNode ()->needUpdate (); + if (mParentCamera->isAttached()) + mParentCamera->getParentSceneNode ()->needUpdate (); mCamera->setOrientation(mParentCamera->getDerivedOrientation()); mCamera->setPosition(mParentCamera->getDerivedPosition()); mCamera->setNearClipDistance(mParentCamera->getNearClipDistance()); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4512bbbeb..6ed11b71f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -63,6 +63,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b , mPhysicsEngine(engine) , mTerrain(NULL) , mEffectManager(NULL) + , mRenderWorld(true) { mActors = new MWRender::Actors(mRendering, this); mObjects = new MWRender::Objects(mRendering); @@ -232,6 +233,15 @@ bool RenderingManager::toggleWater() return mWater->toggle(); } +bool RenderingManager::toggleWorld() +{ + mRenderWorld = !mRenderWorld; + + int visibilityMask = mRenderWorld ? ~int(0) : 0; + mRendering.getViewport()->setVisibilityMask(visibilityMask); + return mRenderWorld; +} + void RenderingManager::cellAdded (MWWorld::CellStore *store) { if (store->isExterior()) @@ -302,7 +312,7 @@ void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) if(mPlayerAnimation) mPlayerAnimation->updatePtr(ptr); if(mCamera->getHandle() == ptr.getRefData().getHandle()) - mCamera->attachTo(ptr); + attachCameraTo(ptr); } void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) @@ -317,7 +327,7 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) anim->rebuild(); if(mCamera->getHandle() == ptr.getRefData().getHandle()) { - mCamera->attachTo(ptr); + attachCameraTo(ptr); mCamera->setAnimation(anim); } } @@ -489,7 +499,7 @@ bool RenderingManager::toggleRenderMode(int mode) } } -void RenderingManager::configureFog(MWWorld::CellStore &mCell) +void RenderingManager::configureFog(const MWWorld::CellStore &mCell) { Ogre::ColourValue color; color.setAsABGR (mCell.getCell()->mAmbi.mFog); @@ -500,7 +510,7 @@ void RenderingManager::configureFog(MWWorld::CellStore &mCell) void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) { mFogColour = colour; - float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); + float max = Settings::Manager::getFloat("viewing distance", "Viewing distance"); if (density == 0) { @@ -732,7 +742,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec { setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } - else if (it->second == "max viewing distance" && it->first == "Viewing distance") + else if (it->second == "viewing distance" && it->first == "Viewing distance") { if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior() && MWBase::Environment::get().getWorld()->getPlayerPtr().mCell) @@ -859,7 +869,13 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr) { ptr.getRefData().setBaseNode(mRendering.getScene()->getSceneNode("player")); - mCamera->attachTo(ptr); + attachCameraTo(ptr); +} + +void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) +{ + Ogre::SceneNode* cameraNode = mCamera->attachTo(ptr); + mSkyManager->attachToNode(cameraNode); } void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 43d22d5ca..2da084074 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -123,6 +123,7 @@ public: void setWaterHeight(const float height); bool toggleWater(); + bool toggleWorld(); /// Updates object rendering after cell change /// \param old Object reference in previous cell @@ -182,7 +183,7 @@ public: ///< request the local map for a cell /// configure fog according to cell - void configureFog(MWWorld::CellStore &mCell); + void configureFog(const MWWorld::CellStore &mCell); /// configure fog manually void configureFog(const float density, const Ogre::ColourValue& colour); @@ -220,6 +221,8 @@ private: void setAmbientMode(); void applyFog(bool underwater); + void attachCameraTo(const MWWorld::Ptr& ptr); + void setMenuTransparency(float val); bool mSunEnabled; @@ -266,6 +269,8 @@ private: MWRender::LocalMap* mLocalMap; MWRender::Shadows* mShadows; + + bool mRenderWorld; }; } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 4620173b5..8e7bd3d89 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -382,9 +382,7 @@ void SkyManager::clearRain() for (std::map::iterator it = mRainModels.begin(); it != mRainModels.end();) { it->second.setNull(); - Ogre::SceneNode* parent = it->first->getParentSceneNode(); mSceneMgr->destroySceneNode(it->first); - mSceneMgr->destroySceneNode(parent); mRainModels.erase(it++); } } @@ -402,9 +400,7 @@ void SkyManager::updateRain(float dt) if (pos.z < -minHeight) { it->second.setNull(); - Ogre::SceneNode* parent = it->first->getParentSceneNode(); mSceneMgr->destroySceneNode(it->first); - mSceneMgr->destroySceneNode(parent); mRainModels.erase(it++); } else @@ -420,17 +416,20 @@ void SkyManager::updateRain(float dt) { mRainTimer = 0; + // TODO: handle rain settings from Morrowind.ini const float rangeRandom = 100; float xOffs = (std::rand()/(RAND_MAX+1.0)) * rangeRandom - (rangeRandom/2); float yOffs = (std::rand()/(RAND_MAX+1.0)) * rangeRandom - (rangeRandom/2); - Ogre::SceneNode* sceneNode = mCamera->getParentSceneNode()->createChildSceneNode(); - sceneNode->setInheritOrientation(false); // Create a separate node to control the offset, since a node with setInheritOrientation(false) will still // consider the orientation of the parent node for its position, just not for its orientation float startHeight = 700; - Ogre::SceneNode* offsetNode = sceneNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight)); + Ogre::SceneNode* offsetNode = mParticleNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight)); + // Spawn a new rain object for each instance. + // TODO: this is inefficient. We could try to use an Ogre::ParticleSystem instead, but then we would need to make assumptions + // about the rain meshes being Quads and their dimensions. + // Or we could clone meshes into one vertex buffer manually. NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(offsetNode, mRainEffect); for (unsigned int i=0; imEntities.size(); ++i) { @@ -562,12 +561,6 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) } else { - if (!mParticleNode) - { - mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); - mParticleNode->setInheritOrientation(false); - } - mParticle = NifOgre::Loader::createObjects(mParticleNode, mCurrentParticleEffect); for(size_t i = 0; i < mParticle->mParticles.size(); ++i) { @@ -783,3 +776,16 @@ void SkyManager::setGlareEnabled (bool enabled) return; mSunGlare->setVisible (mSunEnabled && enabled); } + +void SkyManager::attachToNode(SceneNode *sceneNode) +{ + if (!mParticleNode) + { + mParticleNode = sceneNode->createChildSceneNode(); + mParticleNode->setInheritOrientation(false); + } + else + { + sceneNode->addChild(mParticleNode); + } +} diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index e40eb1dce..0544f17ef 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -116,6 +116,9 @@ namespace MWRender SkyManager(Ogre::SceneNode* root, Ogre::Camera* pCamera); ~SkyManager(); + /// Attach weather particle effects to this scene node (should be the Camera's parent node) + void attachToNode(Ogre::SceneNode* sceneNode); + void update(float duration); void enable(); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 752da30df..fd790b363 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -74,7 +74,8 @@ CubeReflection::~CubeReflection () void CubeReflection::update () { - mParentCamera->getParentSceneNode ()->needUpdate (); + if (mParentCamera->isAttached()) + mParentCamera->getParentSceneNode ()->needUpdate (); mCamera->setPosition(mParentCamera->getDerivedPosition()); } @@ -133,7 +134,8 @@ void PlaneReflection::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::St void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) { - mParentCamera->getParentSceneNode ()->needUpdate (); + if (mParentCamera->isAttached()) + mParentCamera->getParentSceneNode ()->needUpdate (); mCamera->setOrientation(mParentCamera->getDerivedOrientation()); mCamera->setPosition(mParentCamera->getDerivedPosition()); mCamera->setNearClipDistance(mParentCamera->getNearClipDistance()); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 626332564..b80c84d67 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -432,5 +432,6 @@ op 0x20002ac-0x20002c3: SetMagicEffect, explicit op 0x20002c4-0x20002db: ModMagicEffect op 0x20002dc-0x20002f3: ModMagicEffect, explicit op 0x20002f4: ResetActors +op 0x20002f5: ToggleWorld -opcodes 0x20002f5-0x3ffffff unused +opcodes 0x20002f6-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index be8c85bbf..ef879a95a 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -290,6 +290,17 @@ namespace MWScript } }; + class OpToggleWorld : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWorld() ? "World -> On" + : "World -> Off"); + } + }; + class OpDontSaveObject : public Interpreter::Opcode0 { public: @@ -979,6 +990,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeFadeTo, new OpFadeTo); interpreter.installSegment5 (Compiler::Misc::opcodeTogglePathgrid, new OpTogglePathgrid); interpreter.installSegment5 (Compiler::Misc::opcodeToggleWater, new OpToggleWater); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleWorld, new OpToggleWorld); interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 31d52e39d..7ca3c10cf 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -40,7 +40,7 @@ using namespace Ogre; namespace { -void animateCollisionShapes (std::map& map) +void animateCollisionShapes (std::map& map, btDynamicsWorld* dynamicsWorld) { for (std::map::iterator it = map.begin(); it != map.end(); ++it) @@ -79,6 +79,9 @@ void animateCollisionShapes (std::mapgetChildShape(shapeIt->second)->setLocalScaling(BtOgre::Convert::toBullet(bone->_getDerivedScale())); compound->updateChildTransform(shapeIt->second, trans); } + + // needed because we used btDynamicsWorld::setForceUpdateAllAabbs(false) + dynamicsWorld->updateSingleAabb(it->first); } } @@ -283,8 +286,9 @@ namespace MWWorld */ OEngine::Physic::ActorTracer tracer; - bool wasOnGround = false; - bool isOnGround = false; + bool isOnGround = physicActor->getOnGround(); + if (movement.z > 0.f) + isOnGround = false; Ogre::Vector3 inertia(0.0f); Ogre::Vector3 velocity; @@ -317,23 +321,6 @@ namespace MWWorld velocity = newVelocity; } inertia = velocity; // NOTE: velocity is for z axis only in this code block - - if(!(movement.z > 0.0f)) // falling or moving horizontally (or stationary?) check if we're on ground now - { - wasOnGround = physicActor->getOnGround(); // store current state - tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); // check if down 2 possible - if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) - { - const btCollisionObject* standingOn = tracer.mHitObject; - if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) - { - standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName; - } - isOnGround = true; - // if we're on the ground, don't try to fall any more - velocity.z = std::max(0.0f, velocity.z); - } - } } ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; @@ -385,7 +372,6 @@ namespace MWWorld if(tracer.mFraction >= 1.0f) { newPosition = tracer.mEndPos; // ok to move, so set newPosition - remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it? break; } else @@ -406,7 +392,6 @@ namespace MWWorld // precision can be lost due to any math Bullet does internally). Since we // aren't performing any collision detection, we want to reject the next // position, so that we don't slowly move inside another object. - remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it? break; } @@ -438,12 +423,22 @@ namespace MWWorld } } - if(isOnGround || wasOnGround) + if (!(inertia.z > 0.f) && !(newPosition.z < waterlevel || isFlying)) { - tracer.doTrace(colobj, newPosition, newPosition - Ogre::Vector3(0,0,sStepSize+2.0f), engine); + Ogre::Vector3 from = newPosition; + Ogre::Vector3 to = newPosition - (isOnGround ? + Ogre::Vector3(0,0,sStepSize+2.f) : Ogre::Vector3(0,0,2.f)); + tracer.doTrace(colobj, from, to, engine); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) { + const btCollisionObject* standingOn = tracer.mHitObject; + if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) + { + standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName; + } + newPosition.z = tracer.mEndPos.z + 1.0f; + isOnGround = true; } else @@ -672,11 +667,18 @@ namespace MWWorld const Ogre::Vector3 &position = node->getPosition(); if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle)) + { body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z)); + mEngine->mDynamicsWorld->updateSingleAabb(body); + } if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle, true)) + { body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z)); + mEngine->mDynamicsWorld->updateSingleAabb(body); + } + // Actors update their AABBs every frame (DISABLE_DEACTIVATION), so no need to do it manually if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle)) physact->setPosition(position); } @@ -698,6 +700,7 @@ namespace MWWorld body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); else mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation); + mEngine->mDynamicsWorld->updateSingleAabb(body); } if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle, true)) { @@ -705,6 +708,7 @@ namespace MWWorld body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); else mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation); + mEngine->mDynamicsWorld->updateSingleAabb(body); } } @@ -866,8 +870,8 @@ namespace MWWorld void PhysicsSystem::stepSimulation(float dt) { - animateCollisionShapes(mEngine->mAnimatedShapes); - animateCollisionShapes(mEngine->mAnimatedRaycastingShapes); + animateCollisionShapes(mEngine->mAnimatedShapes, mEngine->mDynamicsWorld); + animateCollisionShapes(mEngine->mAnimatedRaycastingShapes, mEngine->mDynamicsWorld); mEngine->stepSimulation(dt); } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a316f0bb2..c98485dc9 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -5,7 +5,7 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" /// FIXME +#include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" @@ -117,6 +117,28 @@ namespace MWWorld } } + void Scene::getGridCenter(int &cellX, int &cellY) + { + int maxX = std::numeric_limits().min(); + int maxY = std::numeric_limits().min(); + int minX = std::numeric_limits().max(); + int minY = std::numeric_limits().max(); + CellStoreCollection::iterator iter = mActiveCells.begin(); + while (iter!=mActiveCells.end()) + { + assert ((*iter)->getCell()->isExterior()); + int x = (*iter)->getCell()->getGridX(); + int y = (*iter)->getCell()->getGridY(); + maxX = std::max(x, maxX); + maxY = std::max(y, maxY); + minX = std::min(x, minX); + minY = std::min(y, minY); + ++iter; + } + cellX = (minX + maxX) / 2; + cellY = (minY + maxY) / 2; + } + void Scene::update (float duration, bool paused) { if (mNeedMapUpdate) @@ -126,6 +148,13 @@ namespace MWWorld for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active) mRendering.requestMap(*active); mNeedMapUpdate = false; + + if (mCurrentCell->isExterior()) + { + int cellX, cellY; + getGridCenter(cellX, cellY); + MWBase::Environment::get().getWindowManager()->setActiveMap(cellX,cellY,false); + } } mRendering.update (duration, paused); @@ -213,41 +242,6 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); } - void Scene::playerCellChange(CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) - { - MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr old = world->getPlayerPtr(); - world->getPlayer().setCell(cell); - - MWWorld::Ptr player = world->getPlayerPtr(); - mRendering.updatePlayerPtr(player); - - if (adjustPlayerPos) { - world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); - - float x = Ogre::Radian(pos.rot[0]).valueDegrees(); - float y = Ogre::Radian(pos.rot[1]).valueDegrees(); - float z = Ogre::Radian(pos.rot[2]).valueDegrees(); - world->rotateObject(player, x, y, z); - - player.getClass().adjustPosition(player, true); - } - - MWBase::MechanicsManager *mechMgr = - MWBase::Environment::get().getMechanicsManager(); - - mechMgr->updateCell(old, player); - mechMgr->watchActor(player); - - mRendering.updateTerrain(); - - // Delay the map update until scripts have been given a chance to run. - // If we don't do this, objects that should be disabled will still appear on the map. - mNeedMapUpdate = true; - - MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); - } - void Scene::changeToVoid() { CellStoreCollection::iterator active = mActiveCells.begin(); @@ -257,7 +251,28 @@ namespace MWWorld mCurrentCell = NULL; } - void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) + void Scene::playerMoved(const Ogre::Vector3 &pos) + { + if (!mCurrentCell || !mCurrentCell->isExterior()) + return; + + // figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in) + int cellX, cellY; + getGridCenter(cellX, cellY); + float centerX, centerY; + MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); + const float maxDistance = 8192/2 + 1024; // 1/2 cell size + threshold + float distance = std::max(std::abs(centerX-pos.x), std::abs(centerY-pos.y)); + if (distance > maxDistance) + { + int newX, newY; + MWBase::Environment::get().getWorld()->positionToIndex(pos.x, pos.y, newX, newY); + changeCellGrid(newX, newY); + mRendering.updateTerrain(); + } + } + + void Scene::changeCellGrid (int X, int Y) { Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); @@ -286,6 +301,7 @@ namespace MWWorld int refsToLoad = 0; // get the number of refs to load for (int x=X-1; x<=X+1; ++x) + { for (int y=Y-1; y<=Y+1; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); @@ -304,11 +320,13 @@ namespace MWWorld if (iter==mActiveCells.end()) refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count(); } + } loadingListener->setProgressRange(refsToLoad); // Load cells for (int x=X-1; x<=X+1; ++x) + { for (int y=Y-1; y<=Y+1; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); @@ -331,32 +349,47 @@ namespace MWWorld loadCell (cell, loadingListener); } } + } - // find current cell - CellStoreCollection::iterator iter = mActiveCells.begin(); + CellStore* current = MWBase::Environment::get().getWorld()->getExterior(X,Y); + MWBase::Environment::get().getWindowManager()->changeCell(current); - while (iter!=mActiveCells.end()) - { - assert ((*iter)->getCell()->isExterior()); + mCellChanged = true; - if (X==(*iter)->getCell()->getGridX() && - Y==(*iter)->getCell()->getGridY()) - break; + // Delay the map update until scripts have been given a chance to run. + // If we don't do this, objects that should be disabled will still appear on the map. + mNeedMapUpdate = true; + } - ++iter; - } + void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) + { + mCurrentCell = cell; - assert (iter!=mActiveCells.end()); + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr old = world->getPlayerPtr(); + world->getPlayer().setCell(cell); - mCurrentCell = *iter; + MWWorld::Ptr player = world->getPlayerPtr(); + mRendering.updatePlayerPtr(player); - // adjust player - playerCellChange (mCurrentCell, position, adjustPlayerPos); + if (adjustPlayerPos) { + world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); - // Sky system - MWBase::Environment::get().getWorld()->adjustSky(); + float x = Ogre::Radian(pos.rot[0]).valueDegrees(); + float y = Ogre::Radian(pos.rot[1]).valueDegrees(); + float z = Ogre::Radian(pos.rot[2]).valueDegrees(); + world->rotateObject(player, x, y, z); - mCellChanged = true; + player.getClass().adjustPosition(player, true); + } + + MWBase::MechanicsManager *mechMgr = + MWBase::Environment::get().getMechanicsManager(); + + mechMgr->updateCell(old, player); + mechMgr->watchActor(player); + + MWBase::Environment::get().getWorld()->adjustSky(); } //We need the ogre renderer and a scene node. @@ -427,33 +460,39 @@ namespace MWWorld // Load cell. std::cout << "cellName: " << cell->getCell()->mName << std::endl; - //Loading Interior loading text - loadCell (cell, loadingListener); - mCurrentCell = cell; + changePlayerCell(cell, position, true); // adjust fog mRendering.configureFog(*mCurrentCell); - // adjust player - playerCellChange (mCurrentCell, position); - // Sky system MWBase::Environment::get().getWorld()->adjustSky(); mCellChanged = true; MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); + + MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + + // Delay the map update until scripts have been given a chance to run. + // If we don't do this, objects that should be disabled will still appear on the map. + mNeedMapUpdate = true; } - void Scene::changeToExteriorCell (const ESM::Position& position) + void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) { int x = 0; int y = 0; MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); - changeCell (x, y, position, true); + changeCellGrid(x, y); + + CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); + changePlayerCell(current, position, adjustPlayerPos); + + mRendering.updateTerrain(); } CellStore* Scene::getCurrentCell () diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index e0eeee187..a9d80bf17 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -60,11 +60,13 @@ namespace MWWorld bool mNeedMapUpdate; - void playerCellChange (CellStore *cell, const ESM::Position& position, - bool adjustPlayerPos = true); - void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); + // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center + void changeCellGrid (int X, int Y); + + void getGridCenter(int& cellX, int& cellY); + public: Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics); @@ -75,19 +77,21 @@ namespace MWWorld void loadCell (CellStore *cell, Loading::Listener* loadingListener); - void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos); + void playerMoved (const Ogre::Vector3& pos); + + void changePlayerCell (CellStore* newCell, const ESM::Position& position, bool adjustPlayerPos); - CellStore* getCurrentCell (); + CellStore *getCurrentCell(); const CellStoreCollection& getActiveCells () const; bool hasCellChanged() const; - ///< Has the player moved to a different cell, since the last frame? + ///< Has the set of active cells changed, since the last frame? void changeToInteriorCell (const std::string& cellName, const ESM::Position& position); ///< Move to interior cell. - void changeToExteriorCell (const ESM::Position& position); + void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos); ///< Move to exterior cell. void changeToVoid(); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 613fd712f..525352e7f 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -498,7 +498,8 @@ void WeatherManager::update(float duration) else if (sound == 1) soundName = &mThunderSoundID1; else if (sound == 2) soundName = &mThunderSoundID2; else if (sound == 3) soundName = &mThunderSoundID3; - MWBase::Environment::get().getSoundManager()->playSound(*soundName, 1.0, 1.0); + if (soundName) + MWBase::Environment::get().getSoundManager()->playSound(*soundName, 1.0, 1.0); mThunderSoundDelay = 1000; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c49f1f483..cf8c11574 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -241,7 +241,7 @@ namespace MWWorld pos.rot[0] = 0; pos.rot[1] = 0; pos.rot[2] = 0; - mWorldScene->changeToExteriorCell(pos); + mWorldScene->changeToExteriorCell(pos, true); } } @@ -920,7 +920,7 @@ namespace MWWorld mRendering->notifyWorldSpaceChanged(); } removeContainerScripts(getPlayerPtr()); - mWorldScene->changeToExteriorCell(position); + mWorldScene->changeToExteriorCell(position, true); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } @@ -1045,7 +1045,7 @@ namespace MWWorld CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); - bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); + bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell)); if (currCell != newCell) { @@ -1057,9 +1057,10 @@ namespace MWWorld changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos); else { - int cellX = newCell->getCell()->getGridX(); - int cellY = newCell->getCell()->getGridY(); - mWorldScene->changeCell(cellX, cellY, pos, false); + if (mWorldScene->isCellActive(*newCell)) + mWorldScene->changePlayerCell(newCell, pos, false); + else + mWorldScene->changeToExteriorCell(pos, false); } addContainerScripts (getPlayerPtr(), newCell); } @@ -1120,6 +1121,10 @@ namespace MWWorld mRendering->moveObject(ptr, vec); mPhysics->moveObject (ptr); } + if (isPlayer) + { + mWorldScene->playerMoved (vec); + } } bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z) @@ -1309,6 +1314,8 @@ namespace MWWorld void World::doPhysics(float duration) { + mPhysics->stepSimulation(duration); + processDoors(duration); mProjectileManager->update(duration); @@ -1327,8 +1334,6 @@ namespace MWWorld } if(player != results.end()) moveObjectImp(player->first, player->second.x, player->second.y, player->second.z); - - mPhysics->stepSimulation(duration); } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) @@ -1565,7 +1570,7 @@ namespace MWWorld bool World::isCellExterior() const { - CellStore *currentCell = mWorldScene->getCurrentCell(); + const CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { return currentCell->getCell()->isExterior(); @@ -1575,7 +1580,7 @@ namespace MWWorld bool World::isCellQuasiExterior() const { - CellStore *currentCell = mWorldScene->getCurrentCell(); + const CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { if (!(currentCell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) @@ -1663,6 +1668,11 @@ namespace MWWorld return mRendering->toggleWater(); } + bool World::toggleWorld() + { + return mRendering->toggleWorld(); + } + void World::PCDropped (const Ptr& item) { std::string script = item.getClass().getScript(item); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d4f11f5b6..1a25edbe5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -180,6 +180,7 @@ namespace MWWorld virtual void setWaterHeight(const float height); virtual bool toggleWater(); + virtual bool toggleWorld(); virtual void adjustSky(); @@ -197,7 +198,7 @@ namespace MWWorld virtual LocalScripts& getLocalScripts(); virtual bool hasCellChanged() const; - ///< Has the player moved to a different cell, since the last frame? + ///< Has the set of active cells changed, since the last frame? virtual bool isCellExterior() const; diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index e12ab65eb..25ee6fdb6 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -256,6 +256,8 @@ namespace Compiler extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo); extensions.registerInstruction ("togglewater", "", opcodeToggleWater); extensions.registerInstruction ("twa", "", opcodeToggleWater); + extensions.registerInstruction ("toggleworld", "", opcodeToggleWorld); + extensions.registerInstruction ("tw", "", opcodeToggleWorld); extensions.registerInstruction ("togglepathgrid", "", opcodeTogglePathgrid); extensions.registerInstruction ("tpg", "", opcodeTogglePathgrid); extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 440475773..5063397e1 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -212,6 +212,7 @@ namespace Compiler const int opcodeFadeOut = 0x200013d; const int opcodeFadeTo = 0x200013e; const int opcodeToggleWater = 0x2000144; + const int opcodeToggleWorld = 0x20002f5; const int opcodeTogglePathgrid = 0x2000146; const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; diff --git a/components/esm/spelllist.cpp b/components/esm/spelllist.cpp index aeface617..8ec386db4 100644 --- a/components/esm/spelllist.cpp +++ b/components/esm/spelllist.cpp @@ -20,4 +20,12 @@ void SpellList::save(ESMWriter &esm) const } } +bool SpellList::exists(const std::string &spell) const +{ + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) + if (Misc::StringUtils::ciEqual(*it, spell)) + return true; + return false; +} + } diff --git a/components/esm/spelllist.hpp b/components/esm/spelllist.hpp index 934bdda7a..bcd6ba798 100644 --- a/components/esm/spelllist.hpp +++ b/components/esm/spelllist.hpp @@ -16,6 +16,9 @@ namespace ESM { std::vector mList; + /// Is this spell ID in mList? + bool exists(const std::string& spell) const; + void load(ESMReader &esm); void save(ESMWriter &esm) const; }; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 585c1495d..c4152533e 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -147,8 +147,8 @@ namespace ESMTerrain Ogre::Vector3 normal; Ogre::ColourValue color; - float vertY; - float vertX; + float vertY = 0; + float vertX = 0; float vertY_ = 0; // of current cell corner for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 44974eeb1..57379a334 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -73,38 +73,6 @@ namespace return NULL; } - - // Ogre::AxisAlignedBox::distance is broken in 1.8. - Ogre::Real distance(const Ogre::AxisAlignedBox& box, const Ogre::Vector3& v) - { - - if (box.contains(v)) - return 0; - else - { - Ogre::Vector3 maxDist(0,0,0); - const Ogre::Vector3& minimum = box.getMinimum(); - const Ogre::Vector3& maximum = box.getMaximum(); - - if (v.x < minimum.x) - maxDist.x = minimum.x - v.x; - else if (v.x > maximum.x) - maxDist.x = v.x - maximum.x; - - if (v.y < minimum.y) - maxDist.y = minimum.y - v.y; - else if (v.y > maximum.y) - maxDist.y = v.y - maximum.y; - - if (v.z < minimum.z) - maxDist.z = minimum.z - v.z; - else if (v.z > maximum.z) - maxDist.z = v.z - maximum.z; - - return maxDist.length(); - } - } - // Create a 2D quad void makeQuad(Ogre::SceneManager* sceneMgr, float left, float top, float right, float bottom, Ogre::MaterialPtr material) { @@ -270,7 +238,7 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) if (mBounds.isNull()) return true; - float dist = distance(mWorldBounds, cameraPos); + float dist = mWorldBounds.distance(cameraPos); // Make sure our scene node is attached if (!mSceneNode->isInSceneGraph()) diff --git a/components/widgets/box.cpp b/components/widgets/box.cpp index e1c148271..0ac2ff7fd 100644 --- a/components/widgets/box.cpp +++ b/components/widgets/box.cpp @@ -197,8 +197,17 @@ namespace Gui MyGUI::IntCoord widgetCoord; widgetCoord.left = curX; widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; - int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count - : sizes[i].first.width; + + int width = 0; + if (sizes[i].second) + { + if (h_stretched_count == 0) + throw std::logic_error("unexpected"); + width = sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count; + } + else + width = sizes[i].first.width; + widgetCoord.width = width; widgetCoord.height = height; w->setCoord(widgetCoord); @@ -334,8 +343,17 @@ namespace Gui MyGUI::IntCoord widgetCoord; widgetCoord.top = curY; widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; - int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count - : sizes[i].first.height; + + int height = 0; + if (sizes[i].second) + { + if (v_stretched_count == 0) + throw std::logic_error("unexpected"); + height = sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count; + } + else + height = sizes[i].first.height; + widgetCoord.height = height; widgetCoord.width = width; w->setCoord(widgetCoord); diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index f874abaf8..6a6a6bf09 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -119,9 +119,11 @@ namespace ICS //bool sliderMoved(const OIS::JoyStickEvent &evt, int index); void addKeyBinding(Control* control, SDL_Scancode key, Control::ControlChangingDirection direction); + bool isKeyBound(SDL_Scancode key) const; void addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction); void addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction); - void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction); + bool isMouseButtonBound(unsigned int button) const; + void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction); void addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction); void addJoystickPOVBinding(Control* control, int deviceId, int index, POVAxis axis, Control::ControlChangingDirection direction); void addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction); diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp index af0eec82a..ed35d9870 100644 --- a/extern/oics/ICSInputControlSystem_keyboard.cpp +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -61,6 +61,11 @@ namespace ICS mControlsKeyBinderMap[ key ] = controlKeyBinderItem; } + bool InputControlSystem::isKeyBound(SDL_Scancode key) const + { + return mControlsKeyBinderMap.find(key) != mControlsKeyBinderMap.end(); + } + void InputControlSystem::removeKeyBinding(SDL_Scancode key) { ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key); diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp index d1ef721b2..9742d389c 100644 --- a/extern/oics/ICSInputControlSystem_mouse.cpp +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -139,6 +139,11 @@ namespace ICS mControlsMouseButtonBinderMap[ button ] = controlMouseButtonBinderItem; } + bool InputControlSystem::isMouseButtonBound(unsigned int button) const + { + return mControlsMouseButtonBinderMap.find(button) != mControlsMouseButtonBinderMap.end(); + } + // get bindings InputControlSystem::NamedAxis InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) { diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index c8a8e0220..5040052fc 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -12,13 +12,13 @@ { float4x4 viewFixed = view; #if !SH_GLSL - viewFixed[0][3] = 0; - viewFixed[1][3] = 0; - viewFixed[2][3] = 0; + viewFixed[0][3] = 0.0; + viewFixed[1][3] = 0.0; + viewFixed[2][3] = 0.0; #else - viewFixed[3][0] = 0; - viewFixed[3][1] = 0; - viewFixed[3][2] = 0; + viewFixed[3][0] = 0.0; + viewFixed[3][1] = 0.0; + viewFixed[3][2] = 0.0; #endif shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); alphaFade = shInputPosition.z < 150.0 ? 0.0 : 1.0; diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index 9e9b10256..90c4d0f84 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -14,13 +14,13 @@ float4x4 worldviewFixed = worldview; #if !SH_GLSL - worldviewFixed[0][3] = 0; - worldviewFixed[1][3] = 0; - worldviewFixed[2][3] = 0; + worldviewFixed[0][3] = 0.0; + worldviewFixed[1][3] = 0.0; + worldviewFixed[2][3] = 0.0; #else - worldviewFixed[3][0] = 0; - worldviewFixed[3][1] = 0; - worldviewFixed[3][2] = 0; + worldviewFixed[3][0] = 0.0; + worldviewFixed[3][1] = 0.0; + worldviewFixed[3][2] = 0.0; #endif shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition)); diff --git a/files/materials/moon.shader b/files/materials/moon.shader index a7d183d10..d369a1c68 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -13,13 +13,13 @@ shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) { float4x4 viewFixed = view; #if !SH_GLSL - viewFixed[0][3] = 0; - viewFixed[1][3] = 0; - viewFixed[2][3] = 0; + viewFixed[0][3] = 0.0; + viewFixed[1][3] = 0.0; + viewFixed[2][3] = 0.0; #else - viewFixed[3][0] = 0; - viewFixed[3][1] = 0; - viewFixed[3][2] = 0; + viewFixed[3][0] = 0.0; + viewFixed[3][1] = 0.0; + viewFixed[3][2] = 0.0; #endif shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shMatrixMult(world, shInputPosition))); UV = uv0; diff --git a/files/materials/sun.shader b/files/materials/sun.shader index abe4c97f1..fc747b522 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -13,13 +13,13 @@ shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) { float4x4 viewFixed = view; #if !SH_GLSL - viewFixed[0][3] = 0; - viewFixed[1][3] = 0; - viewFixed[2][3] = 0; + viewFixed[0][3] = 0.0; + viewFixed[1][3] = 0.0; + viewFixed[2][3] = 0.0; #else - viewFixed[3][0] = 0; - viewFixed[3][1] = 0; - viewFixed[3][2] = 0; + viewFixed[3][0] = 0.0; + viewFixed[3][1] = 0.0; + viewFixed[3][2] = 0.0; #endif shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shMatrixMult(world, shInputPosition))); UV = uv0; diff --git a/files/materials/water.shader b/files/materials/water.shader index 701154ffa..418b13715 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -40,7 +40,7 @@ SH_START_PROGRAM { - shOutputColour(0).xyz = shSample(animatedTexture, UV * 15).xyz * float3(1.0, 1.0, 1.0); + shOutputColour(0).xyz = shSample(animatedTexture, UV * float2(15.0, 15.0)).xyz * float3(1.0, 1.0, 1.0); shOutputColour(0).w = 0.7; #if FOG diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 87e750cc9..0099f9a46 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -28,7 +28,7 @@ set(MYGUI_FILES openmw_container_window.layout openmw_count_window.layout openmw_dialogue_window.layout - openmw_dialogue_window_skin.xml + openmw_dialogue_window.skin.xml openmw_edit.skin.xml openmw_font.xml openmw_hud_box.skin.xml @@ -38,19 +38,19 @@ set(MYGUI_FILES openmw_interactive_messagebox.layout openmw_inventory_window.layout openmw_journal.layout - openmw_journal_skin.xml + openmw_journal.skin.xml openmw_layers.xml openmw_list.skin.xml openmw_mainmenu.layout - openmw_mainmenu_skin.xml + openmw_mainmenu.skin.xml openmw_map_window.layout - openmw_map_window_skin.xml + openmw_map_window.skin.xml openmw_messagebox.layout openmw_pointer.xml openmw_progress.skin.xml openmw_resources.xml openmw_scroll.layout - openmw_scroll_skin.xml + openmw_scroll.skin.xml openmw_settings_window.layout openmw_settings.xml openmw_spell_window.layout @@ -83,6 +83,8 @@ set(MYGUI_FILES openmw_recharge_dialog.layout openmw_screen_fader.layout openmw_edit_note.layout + openmw_debug_window.layout + openmw_debug_window.skin.xml DejaVuLGCSansMono.ttf ../launcher/images/openmw.png OpenMWResourcePlugin.xml diff --git a/files/mygui/core.xml b/files/mygui/core.xml index ea1627875..f3b482d50 100644 --- a/files/mygui/core.xml +++ b/files/mygui/core.xml @@ -15,13 +15,14 @@ - + - - - - + + + + + diff --git a/files/mygui/openmw_button.skin.xml b/files/mygui/openmw_button.skin.xml index 9193e3874..73c266818 100644 --- a/files/mygui/openmw_button.skin.xml +++ b/files/mygui/openmw_button.skin.xml @@ -73,6 +73,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index c88c1955d..854568f9b 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -2,8 +2,7 @@ - - + diff --git a/files/mygui/openmw_debug_window.layout b/files/mygui/openmw_debug_window.layout new file mode 100644 index 000000000..9af646c6e --- /dev/null +++ b/files/mygui/openmw_debug_window.layout @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_debug_window.skin.xml b/files/mygui/openmw_debug_window.skin.xml new file mode 100644 index 000000000..587101b7f --- /dev/null +++ b/files/mygui/openmw_debug_window.skin.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_dialogue_window_skin.xml b/files/mygui/openmw_dialogue_window.skin.xml similarity index 100% rename from files/mygui/openmw_dialogue_window_skin.xml rename to files/mygui/openmw_dialogue_window.skin.xml diff --git a/files/mygui/openmw_journal_skin.xml b/files/mygui/openmw_journal.skin.xml similarity index 100% rename from files/mygui/openmw_journal_skin.xml rename to files/mygui/openmw_journal.skin.xml diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 1df4841af..38a98d133 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -6,6 +6,7 @@ + diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index eda002040..49ee0a1de 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -4,7 +4,7 @@ - + diff --git a/files/mygui/openmw_mainmenu_skin.xml b/files/mygui/openmw_mainmenu.skin.xml similarity index 100% rename from files/mygui/openmw_mainmenu_skin.xml rename to files/mygui/openmw_mainmenu.skin.xml diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window.skin.xml similarity index 100% rename from files/mygui/openmw_map_window_skin.xml rename to files/mygui/openmw_map_window.skin.xml diff --git a/files/mygui/openmw_messagebox.layout b/files/mygui/openmw_messagebox.layout index 8264d64b9..a70e14503 100644 --- a/files/mygui/openmw_messagebox.layout +++ b/files/mygui/openmw_messagebox.layout @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index ad5ac84bb..7d7ba07b6 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -33,7 +33,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/files/mygui/openmw_scroll_skin.xml b/files/mygui/openmw_scroll.skin.xml similarity index 100% rename from files/mygui/openmw_scroll_skin.xml rename to files/mygui/openmw_scroll.skin.xml diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 5945c68d7..fa6485688 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -346,10 +346,10 @@ - + - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 67fd5ee8c..06d999189 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -122,8 +122,12 @@ small object size = 250 # Rendering distance for small objects small object distance = 3500 -# Max viewing distance at clear weather conditions -max viewing distance = 5600 +# Viewing distance at normal weather conditions +# The maximum distance with no pop-in will be: (see RenderingManager::configureFog) +# viewing distance / minimum weather fog depth (.69) * view frustum factor <= cell size (8192) - loading threshold (1024) +# view frustum factor takes into account that the view frustum end is a plane, so at the edges of the screen you can see further than you should be able to. +# exact factor would depend on FOV +viewing distance = 4600 # Distance at which fog starts (proportional to viewing distance) fog start factor = 0.5 diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index f2be11386..b4d5e49a5 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -97,6 +97,8 @@ namespace Physic (0,0, mShape.get()); mBody = new RigidBody(CI, name); mBody->mPlaceable = false; + mBody->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); + mBody->setActivationState(DISABLE_DEACTIVATION); setPosition(position); setRotation(rotation); @@ -224,15 +226,15 @@ namespace Physic // The actual physics solver solver = new btSequentialImpulseConstraintSolver; - //btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); - pairCache = new btSortedOverlappingPairCache(); - - //pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); - broadphase = new btDbvtBroadphase(); // The world. mDynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); + + // Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this. + // Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb. + mDynamicsWorld->setForceUpdateAllAabbs(false); + mDynamicsWorld->setGravity(btVector3(0,0,-10)); if(BulletShapeManager::getSingletonPtr() == NULL) @@ -334,7 +336,6 @@ namespace Physic delete collisionConfiguration; delete dispatcher; delete broadphase; - delete pairCache; delete mShapeLoader; delete BulletShapeManager::getSingletonPtr(); @@ -380,7 +381,7 @@ namespace Physic mHeightFieldMap [name] = hf; mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap, - CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); + CollisionType_Actor|CollisionType_Raycasting); } void PhysicEngine::removeHeightField(int x, int y) @@ -487,7 +488,7 @@ namespace Physic { assert (mCollisionObjectMap.find(name) == mCollisionObjectMap.end()); mCollisionObjectMap[name] = body; - mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); + mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_Actor|CollisionType_HeightMap); } else { diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index e330a7621..0d280c4fe 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -49,21 +49,6 @@ namespace Physic CollisionType_Raycasting = 1<<3 //Still used? }; - /** - *This is just used to be able to name objects. - */ - class PairCachingGhostObject : public btPairCachingGhostObject - { - public: - PairCachingGhostObject(std::string name) - :btPairCachingGhostObject(),mName(name) - { - } - virtual ~PairCachingGhostObject(){} - - std::string mName; - }; - /** *This class is just an extension of normal btRigidBody in order to add extra info. *When bullet give back a btRigidBody, you can just do a static_cast to RigidBody, @@ -317,7 +302,6 @@ namespace Physic btCollisionObject *object); //Bullet Stuff - btOverlappingPairCache* pairCache; btBroadphaseInterface* broadphase; btDefaultCollisionConfiguration* collisionConfiguration; btSequentialImpulseConstraintSolver* solver; diff --git a/libs/openengine/gui/layout.hpp b/libs/openengine/gui/layout.hpp index 26a3fdab8..cd530a1d9 100644 --- a/libs/openengine/gui/layout.hpp +++ b/libs/openengine/gui/layout.hpp @@ -42,6 +42,7 @@ namespace GUI MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' in layout '" << mLayoutName << "' not found."); } + private: void initialise(const std::string & _layout, MyGUI::Widget* _parent = nullptr) { @@ -74,6 +75,7 @@ namespace GUI mListWindowRoot.clear(); } + public: void setCoord(int x, int y, int w, int h) { mMainWidget->setCoord(x,y,w,h); @@ -121,29 +123,6 @@ namespace GUI adjustWindowCaption(); } - void setState(const std::string& widget, const std::string& state) - { - MyGUI::Widget* pt; - getWidget(pt, widget); - pt->_setWidgetState(state); - } - - void setTextColor(const std::string& name, float r, float g, float b) - { - MyGUI::Widget* pt; - getWidget(pt, name); - MyGUI::TextBox *st = dynamic_cast(pt); - if(st != NULL) - st->setTextColour(MyGUI::Colour(b,g,r)); - } - - void setImage(const std::string& name, const std::string& imgName) - { - MyGUI::ImageBox* pt; - getWidget(pt, name); - pt->setImageTexture(imgName); - } - void adjustButtonSize(MyGUI::Button* button) { // adjust size of button to fit its text