Merge remote-tracking branch 'upstream/master' into wizard

loadfix
pvdk 11 years ago
commit 8fe837ae6e

@ -7,7 +7,6 @@ branches:
- /openmw-.*$/ - /openmw-.*$/
before_install: before_install:
- pwd - pwd
- git fetch --tags
- echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
- echo "yes" | sudo apt-add-repository ppa:openmw/openmw - echo "yes" | sudo apt-add-repository ppa:openmw/openmw
- sudo apt-get update -qq - sudo apt-get update -qq

@ -6,34 +6,63 @@ if (APPLE)
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
endif (APPLE) endif (APPLE)
# Macros
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
include(OpenMWMacros)
# Version # Version
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 29)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
find_package(Git)
if(GIT_FOUND)
include(GetGitRevisionDescription)
get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if(MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}")
set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}")
if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION})
message(FATAL_ERROR "Silly Zini forgot to update the version again...")
else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION})
set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR})
set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR})
set(OPENMW_VERSION_RELEASE ${GIT_VERSION_RELEASE})
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
endif(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION})
message(STATUS "OpenMW version ${OPENMW_VERSION}")
else(MATCH)
message(WARNING "Failed to get valid version information from Git")
endif(MATCH)
else(GIT_FOUND)
message(WARNING "Git executable not found")
endif(GIT_FOUND)
else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
message(STATUS "Shallow Git clone detected, not attempting to retrieve version info")
endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
include(GetGitRevisionDescription) # Macros
include(OpenMWMacros)
get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...")
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if (MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...")
else (MATCH)
message(FATAL_ERROR "Failed to get valid version information from Git")
endif (MATCH)
# doxygen main page # doxygen main page
@ -210,6 +239,14 @@ if (UNIX AND NOT APPLE)
find_package (Threads) find_package (Threads)
endif() endif()
# Look for stdint.h
include(CheckIncludeFile)
check_include_file(stdint.h HAVE_STDINT_H)
if(NOT HAVE_STDINT_H)
unset(HAVE_STDINT_H CACHE)
message(FATAL_ERROR "stdint.h was not found" )
endif()
include (CheckIncludeFileCXX) include (CheckIncludeFileCXX)
check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP) check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP)
if (HAVE_UNORDERED_MAP) if (HAVE_UNORDERED_MAP)

@ -79,17 +79,20 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
QString revision(OPENMW_VERSION_COMMITHASH); QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH); QString tag(OPENMW_VERSION_TAGHASH);
if (revision == tag) { if (!revision.isEmpty() && !tag.isEmpty())
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); {
} else { if (revision == tag) {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
} } else {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
}
// Add the compile date and time // Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
}
createIcons(); createIcons();
} }

@ -64,8 +64,12 @@ opencs_units (view/world
) )
opencs_units (view/render opencs_units (view/render
scenewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
) )
opencs_units_noqt (view/render
navigation navigation1st navigationfree navigationorbit
)
opencs_units_noqt (view/world opencs_units_noqt (view/world
dialoguesubview subviews dialoguesubview subviews

@ -9,15 +9,22 @@
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
#include <components/ogreinit/ogreinit.hpp>
#include "model/doc/document.hpp" #include "model/doc/document.hpp"
#include "model/world/data.hpp" #include "model/world/data.hpp"
CS::Editor::Editor() CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS")
{ {
mIpcServerName = "org.openmw.OpenCS"; Files::PathContainer dataDirs = readConfig();
setupDataFiles (dataDirs);
CSMSettings::UserSettings::instance().loadSettings ("opencs.cfg");
setupDataFiles(); ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string());
mNewGame.setLocalData (mLocal); mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal); mFileDialog.setLocalData (mLocal);
@ -42,7 +49,16 @@ CS::Editor::Editor()
this, SLOT (createNewGame (const boost::filesystem::path&))); this, SLOT (createNewGame (const boost::filesystem::path&)));
} }
void CS::Editor::setupDataFiles() void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs)
{
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path);
}
}
Files::PathContainer CS::Editor::readConfig()
{ {
boost::program_options::variables_map variables; boost::program_options::variables_map variables;
boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options"); boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options");
@ -58,6 +74,8 @@ void CS::Editor::setupDataFiles()
mCfgMgr.readConfiguration(variables, desc); mCfgMgr.readConfiguration(variables, desc);
mDocumentManager.setResourceDir (variables["resources"].as<std::string>());
Files::PathContainer dataDirs, dataLocal; Files::PathContainer dataDirs, dataLocal;
if (!variables["data"].empty()) { if (!variables["data"].empty()) {
dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>()); dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
@ -83,23 +101,11 @@ void CS::Editor::setupDataFiles()
messageBox.exec(); messageBox.exec();
QApplication::exit (1); QApplication::exit (1);
return;
} }
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
mDocumentManager.setResourceDir (variables["resources"].as<std::string>()); return dataDirs;
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path);
}
//load the settings into the userSettings instance.
const QString settingFileName = "opencs.cfg";
CSMSettings::UserSettings::instance().loadSettings(settingFileName);
} }
void CS::Editor::createGame() void CS::Editor::createGame()
@ -210,8 +216,6 @@ int CS::Editor::run()
if (mLocal.empty()) if (mLocal.empty())
return 1; return 1;
// temporarily disable OGRE-integration (need to fix path problem first)
#if 0
// TODO: setting // TODO: setting
Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem"));
@ -228,7 +232,6 @@ int CS::Editor::run()
#endif #endif
Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, &params); Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, &params);
hiddenWindow->setActive(false); hiddenWindow->setActive(false);
#endif
mStartup.show(); mStartup.show();

@ -10,6 +10,8 @@
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#endif #endif
#include <components/files/multidircollection.hpp>
#include "model/settings/usersettings.hpp" #include "model/settings/usersettings.hpp"
#include "model/doc/documentmanager.hpp" #include "model/doc/documentmanager.hpp"
@ -20,6 +22,11 @@
#include "view/settings/usersettingsdialog.hpp" #include "view/settings/usersettingsdialog.hpp"
namespace OgreInit
{
class OgreInit;
}
namespace CS namespace CS
{ {
class Editor : public QObject class Editor : public QObject
@ -37,7 +44,10 @@ namespace CS
boost::filesystem::path mLocal; boost::filesystem::path mLocal;
void setupDataFiles(); void setupDataFiles (const Files::PathContainer& dataDirs);
Files::PathContainer readConfig();
///< \return data paths
// not implemented // not implemented
Editor (const Editor&); Editor (const Editor&);
@ -45,7 +55,7 @@ namespace CS
public: public:
Editor(); Editor (OgreInit::OgreInit& ogreInit);
bool makeIPCServer(); bool makeIPCServer();
void connectToIPCServer(); void connectToIPCServer();

@ -40,15 +40,9 @@ int main(int argc, char *argv[])
{ {
Q_INIT_RESOURCE (resources); Q_INIT_RESOURCE (resources);
// TODO: Ogre startup shouldn't be here, but it currently has to:
// SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :(
Application mApplication (argc, argv);
// temporarily disable OGRE-integration (need to fix path problem first)
#if 0
OgreInit::OgreInit ogreInit; OgreInit::OgreInit ogreInit;
ogreInit.init("./opencsOgre.log"); // TODO log path?
#endif Application application (argc, argv);
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
@ -66,12 +60,12 @@ int main(int argc, char *argv[])
QStringList libraryPaths; QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
mApplication.setLibraryPaths(libraryPaths); application.setLibraryPaths(libraryPaths);
#endif #endif
mApplication.setWindowIcon (QIcon (":./opencs.png")); application.setWindowIcon (QIcon (":./opencs.png"));
CS::Editor editor; CS::Editor editor (ogreInit);
if(!editor.makeIPCServer()) if(!editor.makeIPCServer())
{ {

@ -98,7 +98,7 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
virtual void cloneRecord(const std::string& origin, virtual void cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
const UniversalId::Type type); const UniversalId::Type type);
@ -198,7 +198,7 @@ namespace CSMWorld
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin, void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
const UniversalId::Type type) const UniversalId::Type type)
{ {

@ -74,7 +74,7 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None) = 0; UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
virtual void cloneRecord(const std::string& origin, virtual void cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
const UniversalId::Type type) = 0; const UniversalId::Type type) = 0;

@ -247,12 +247,12 @@ CSMWorld::Data::Data() : mRefs (mCells)
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal);
addModel (new IdTable (&mTopicInfos), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); addModel (new IdTable (&mTopicInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo);
addModel (new IdTable (&mJournalInfos), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); addModel (new IdTable (&mJournalInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo);
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mCells, IdTable::Reordering_None, IdTable::Viewing_Id), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference, false); addModel (new IdTable (&mRefs, IdTable::Reordering_None, IdTable::Viewing_Cell), UniversalId::Type_References, UniversalId::Type_Reference, false);
addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false);
} }

@ -4,8 +4,9 @@
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering) CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering,
: mIdCollection (idCollection), mReordering (reordering) Viewing viewing)
: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing)
{} {}
CSMWorld::IdTable::~IdTable() CSMWorld::IdTable::~IdTable()
@ -188,4 +189,45 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector<int>& newO
CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const
{ {
return mReordering; return mReordering;
}
CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const
{
return mViewing;
}
std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row) const
{
std::string id;
std::string hint;
if (mViewing==Viewing_Cell)
{
int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell);
int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);
if (cellColumn!=-1 && idColumn!=-1)
{
id = mIdCollection->getData (row, cellColumn).toString().toUtf8().constData();
hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData());
}
}
else if (mViewing==Viewing_Id)
{
int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);
if (column!=-1)
{
id = mIdCollection->getData (row, column).toString().toUtf8().constData();
hint = "c:" + id;
}
}
if (id.empty())
return std::make_pair (UniversalId::Type_None, "");
if (id[0]=='#')
id = "sys::default";
return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint);
} }

@ -25,10 +25,20 @@ namespace CSMWorld
Reordering_WithinTopic Reordering_WithinTopic
}; };
enum Viewing
{
Viewing_None,
Viewing_Id, // use ID column to generate view request (ID is transformed into
// worldspace and original ID is passed as hint with c: prefix)
Viewing_Cell // use cell column to generate view request (cell ID is transformed
// into worldspace and record ID is passed as hint with r: prefix)
};
private: private:
CollectionBase *mIdCollection; CollectionBase *mIdCollection;
Reordering mReordering; Reordering mReordering;
Viewing mViewing;
// not implemented // not implemented
IdTable (const IdTable&); IdTable (const IdTable&);
@ -36,7 +46,8 @@ namespace CSMWorld
public: public:
IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic); IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_None,
Viewing viewing = Viewing_None);
///< The ownership of \a idCollection is not transferred. ///< The ownership of \a idCollection is not transferred.
virtual ~IdTable(); virtual ~IdTable();
@ -63,8 +74,8 @@ namespace CSMWorld
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
void cloneRecord(const std::string& origin, void cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
QModelIndex getModelIndex (const std::string& id, int column) const; QModelIndex getModelIndex (const std::string& id, int column) const;
@ -86,6 +97,12 @@ namespace CSMWorld
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
Reordering getReordering() const; Reordering getReordering() const;
Viewing getViewing() const;
std::pair<UniversalId, std::string> view (int row) const;
///< Return the UniversalId and the hint for viewing \a row. If viewing is not
/// supported by this table, return (UniversalId::Type_None, "").
}; };
} }

@ -90,14 +90,14 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
static const TypeData sIndexArg[] = static const TypeData sIndexArg[] =
{ {
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
} }

@ -16,4 +16,6 @@ void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QSt
{ {
} }
void CSVDoc::SubView::setStatusBar (bool show) {} void CSVDoc::SubView::setStatusBar (bool show) {}
void CSVDoc::SubView::useHint (const std::string& hint) {}

@ -40,9 +40,12 @@ namespace CSVDoc
virtual void setStatusBar (bool show); virtual void setStatusBar (bool show);
///< Default implementation: ignored ///< Default implementation: ignored
virtual void useHint (const std::string& hint);
///< Default implementation: ignored
signals: signals:
void focusId (const CSMWorld::UniversalId& universalId); void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint);
}; };
} }

@ -115,10 +115,6 @@ void CSVDoc::View::setupWorldMenu()
world->addSeparator(); // items that don't represent single record lists follow here world->addSeparator(); // items that don't represent single record lists follow here
QAction *scene = new QAction (tr ("Scene"), this);
connect (scene, SIGNAL (triggered()), this, SLOT (addSceneSubView()));
world->addAction (scene);
QAction *regionMap = new QAction (tr ("Region Map"), this); QAction *regionMap = new QAction (tr ("Region Map"), this);
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
world->addAction (regionMap); world->addAction (regionMap);
@ -310,7 +306,7 @@ void CSVDoc::View::updateProgress (int current, int max, int type, int threads)
mOperations->setProgress (current, max, type, threads); mOperations->setProgress (current, max, type, threads);
} }
void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) 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 /// \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 /// number is exceeded
@ -322,12 +318,15 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id)
SubView *view = mSubViewFactory.makeSubView (id, *mDocument); SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
if (!hint.empty())
view->useHint (hint);
view->setStatusBar (mShowStatusBar->isChecked()); view->setStatusBar (mShowStatusBar->isChecked());
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this,
SLOT (addSubView (const CSMWorld::UniversalId&))); SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&)));
CSMSettings::UserSettings::instance().updateSettings("Display Format"); CSMSettings::UserSettings::instance().updateSettings("Display Format");
@ -429,11 +428,6 @@ void CSVDoc::View::addFiltersSubView()
addSubView (CSMWorld::UniversalId::Type_Filters); addSubView (CSMWorld::UniversalId::Type_Filters);
} }
void CSVDoc::View::addSceneSubView()
{
addSubView (CSMWorld::UniversalId::Type_Scene);
}
void CSVDoc::View::addTopicsSubView() void CSVDoc::View::addTopicsSubView()
{ {
addSubView (CSMWorld::UniversalId::Type_Topics); addSubView (CSMWorld::UniversalId::Type_Topics);

@ -120,7 +120,9 @@ namespace CSVDoc
public slots: public slots:
void addSubView (const CSMWorld::UniversalId& id); void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = "");
///< \param hint Suggested view point (e.g. coordinates in a 3D scene or a line number
/// in a script).
void abortOperation (int type); void abortOperation (int type);
@ -166,8 +168,6 @@ namespace CSVDoc
void addFiltersSubView(); void addFiltersSubView();
void addSceneSubView();
void addTopicsSubView(); void addTopicsSubView();
void addJournalsSubView(); void addJournalsSubView();

@ -0,0 +1,19 @@
#include "navigation.hpp"
float CSVRender::Navigation::getFactor (bool mouse) const
{
float factor = mFastModeFactor;
if (mouse)
factor /= 2; /// \todo make this configurable
return factor;
}
CSVRender::Navigation::~Navigation() {}
void CSVRender::Navigation::setFastModeFactor (float factor)
{
mFastModeFactor = factor;
}

@ -0,0 +1,46 @@
#ifndef OPENCS_VIEW_NAVIGATION_H
#define OPENCS_VIEW_NAVIGATION_H
class QPoint;
namespace Ogre
{
class Camera;
}
namespace CSVRender
{
class Navigation
{
float mFastModeFactor;
protected:
float getFactor (bool mouse) const;
public:
virtual ~Navigation();
void setFastModeFactor (float factor);
///< Set currently applying fast mode factor.
virtual bool activate (Ogre::Camera *camera) = 0;
///< \return Update required?
virtual bool wheelMoved (int delta) = 0;
///< \return Update required?
virtual bool mouseMoved (const QPoint& delta, int mode) = 0;
///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1
/// \return Update required?
virtual bool handleMovementKeys (int vertical, int horizontal) = 0;
///< \return Update required?
virtual bool handleRollKeys (int delta) = 0;
///< \return Update required?
};
}
#endif

@ -0,0 +1,85 @@
#include "navigation1st.hpp"
#include <OgreCamera.h>
#include <QPoint>
CSVRender::Navigation1st::Navigation1st() : mCamera (0) {}
bool CSVRender::Navigation1st::activate (Ogre::Camera *camera)
{
mCamera = camera;
mCamera->setFixedYawAxis (true);
Ogre::Radian pitch = mCamera->getOrientation().getPitch();
Ogre::Radian limit (Ogre::Math::PI/2-0.5);
if (pitch>limit)
mCamera->pitch (-(pitch-limit));
else if (pitch<-limit)
mCamera->pitch (pitch-limit);
return true;
}
bool CSVRender::Navigation1st::wheelMoved (int delta)
{
mCamera->move (getFactor (true) * mCamera->getDirection() * delta);
return true;
}
bool CSVRender::Navigation1st::mouseMoved (const QPoint& delta, int mode)
{
if (mode==0)
{
// turn camera
if (delta.x())
mCamera->yaw (Ogre::Degree (getFactor (true) * delta.x()));
if (delta.y())
{
Ogre::Radian oldPitch = mCamera->getOrientation().getPitch();
float deltaPitch = getFactor (true) * delta.y();
Ogre::Radian newPitch = oldPitch + Ogre::Degree (deltaPitch);
Ogre::Radian limit (Ogre::Math::PI/2-0.5);
if ((deltaPitch>0 && newPitch<limit) || (deltaPitch<0 && newPitch>-limit))
mCamera->pitch (Ogre::Degree (deltaPitch));
}
return true;
}
else if (mode==1)
{
// pan camera
if (delta.x())
mCamera->move (getFactor (true) * mCamera->getDerivedRight() * delta.x());
if (delta.y())
mCamera->move (getFactor (true) * -mCamera->getDerivedUp() * delta.y());
return true;
}
return false;
}
bool CSVRender::Navigation1st::handleMovementKeys (int vertical, int horizontal)
{
if (vertical)
mCamera->move (getFactor (false) * mCamera->getDirection() * vertical);
if (horizontal)
mCamera->move (getFactor (true) * mCamera->getDerivedRight() * horizontal);
return true;
}
bool CSVRender::Navigation1st::handleRollKeys (int delta)
{
// we don't roll this way in 1st person mode
return false;
}

@ -0,0 +1,35 @@
#ifndef OPENCS_VIEW_NAVIGATION1ST_H
#define OPENCS_VIEW_NAVIGATION1ST_H
#include "navigation.hpp"
namespace CSVRender
{
/// \brief First person-like camera controls
class Navigation1st : public Navigation
{
Ogre::Camera *mCamera;
public:
Navigation1st();
virtual bool activate (Ogre::Camera *camera);
///< \return Update required?
virtual bool wheelMoved (int delta);
///< \return Update required?
virtual bool mouseMoved (const QPoint& delta, int mode);
///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1
/// \return Update required?
virtual bool handleMovementKeys (int vertical, int horizontal);
///< \return Update required?
virtual bool handleRollKeys (int delta);
///< \return Update required?
};
}
#endif

@ -0,0 +1,66 @@
#include "navigationfree.hpp"
#include <OgreCamera.h>
#include <QPoint>
CSVRender::NavigationFree::NavigationFree() : mCamera (0) {}
bool CSVRender::NavigationFree::activate (Ogre::Camera *camera)
{
mCamera = camera;
mCamera->setFixedYawAxis (false);
return false;
}
bool CSVRender::NavigationFree::wheelMoved (int delta)
{
mCamera->move (getFactor (true) * mCamera->getDirection() * delta);
return true;
}
bool CSVRender::NavigationFree::mouseMoved (const QPoint& delta, int mode)
{
if (mode==0)
{
// turn camera
if (delta.x())
mCamera->yaw (Ogre::Degree (getFactor (true) * delta.x()));
if (delta.y())
mCamera->pitch (Ogre::Degree (getFactor (true) * delta.y()));
return true;
}
else if (mode==1)
{
// pan camera
if (delta.x())
mCamera->move (getFactor (true) * mCamera->getDerivedRight() * delta.x());
if (delta.y())
mCamera->move (getFactor (true) * -mCamera->getDerivedUp() * delta.y());
return true;
}
return false;
}
bool CSVRender::NavigationFree::handleMovementKeys (int vertical, int horizontal)
{
if (vertical)
mCamera->move (getFactor (false) * mCamera->getDerivedUp() * vertical);
if (horizontal)
mCamera->move (getFactor (true) * mCamera->getDerivedRight() * horizontal);
return true;
}
bool CSVRender::NavigationFree::handleRollKeys (int delta)
{
mCamera->roll (Ogre::Degree (getFactor (false) * delta));
return true;
}

@ -0,0 +1,35 @@
#ifndef OPENCS_VIEW_NAVIGATIONFREE_H
#define OPENCS_VIEW_NAVIGATIONFREE_H
#include "navigation.hpp"
namespace CSVRender
{
/// \brief Free camera controls
class NavigationFree : public Navigation
{
Ogre::Camera *mCamera;
public:
NavigationFree();
virtual bool activate (Ogre::Camera *camera);
///< \return Update required?
virtual bool wheelMoved (int delta);
///< \return Update required?
virtual bool mouseMoved (const QPoint& delta, int mode);
///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1
/// \return Update required?
virtual bool handleMovementKeys (int vertical, int horizontal);
///< \return Update required?
virtual bool handleRollKeys (int delta);
///< \return Update required?
};
}
#endif

@ -0,0 +1,100 @@
#include "navigationorbit.hpp"
#include <OgreCamera.h>
#include <QPoint>
void CSVRender::NavigationOrbit::rotateCamera (const Ogre::Vector3& diff)
{
Ogre::Vector3 pos = mCamera->getPosition();
float distance = (pos-mCentre).length();
Ogre::Vector3 direction = (pos+diff)-mCentre;
direction.normalise();
mCamera->setPosition (mCentre + direction*distance);
mCamera->lookAt (mCentre);
}
CSVRender::NavigationOrbit::NavigationOrbit() : mCamera (0), mCentre (0, 0, 0), mDistance (100)
{}
bool CSVRender::NavigationOrbit::activate (Ogre::Camera *camera)
{
mCamera = camera;
mCamera->setFixedYawAxis (false);
if ((mCamera->getPosition()-mCentre).length()<mDistance)
{
// move camera out of the centre area
Ogre::Vector3 direction = mCentre-mCamera->getPosition();
direction.normalise();
if (direction.length()==0)
direction = Ogre::Vector3 (1, 0, 0);
mCamera->setPosition (mCentre - direction * mDistance);
}
mCamera->lookAt (mCentre);
return true;
}
bool CSVRender::NavigationOrbit::wheelMoved (int delta)
{
Ogre::Vector3 diff = getFactor (true) * mCamera->getDirection() * delta;
Ogre::Vector3 pos = mCamera->getPosition();
if (delta>0 && diff.length()>=(pos-mCentre).length()-mDistance)
{
pos = mCentre-(mCamera->getDirection() * mDistance);
}
else
{
pos += diff;
}
mCamera->setPosition (pos);
return true;
}
bool CSVRender::NavigationOrbit::mouseMoved (const QPoint& delta, int mode)
{
Ogre::Vector3 diff =
getFactor (true) * -mCamera->getDerivedRight() * delta.x()
+ getFactor (true) * mCamera->getDerivedUp() * delta.y();
if (mode==0)
{
rotateCamera (diff);
return true;
}
else if (mode==1)
{
mCamera->move (diff);
mCentre += diff;
return true;
}
return false;
}
bool CSVRender::NavigationOrbit::handleMovementKeys (int vertical, int horizontal)
{
rotateCamera (
- getFactor (false) * -mCamera->getDerivedRight() * horizontal
+ getFactor (false) * mCamera->getDerivedUp() * vertical);
return true;
}
bool CSVRender::NavigationOrbit::handleRollKeys (int delta)
{
mCamera->roll (Ogre::Degree (getFactor (false) * delta));
return true;
}

@ -0,0 +1,42 @@
#ifndef OPENCS_VIEW_NAVIGATIONORBIT_H
#define OPENCS_VIEW_NAVIGATIONORBIT_H
#include "navigation.hpp"
#include <OgreVector3.h>
namespace CSVRender
{
/// \brief Orbiting camera controls
class NavigationOrbit : public Navigation
{
Ogre::Camera *mCamera;
Ogre::Vector3 mCentre;
int mDistance;
void rotateCamera (const Ogre::Vector3& diff);
///< Rotate camera around centre.
public:
NavigationOrbit();
virtual bool activate (Ogre::Camera *camera);
///< \return Update required?
virtual bool wheelMoved (int delta);
///< \return Update required?
virtual bool mouseMoved (const QPoint& delta, int mode);
///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1
/// \return Update required?
virtual bool handleMovementKeys (int vertical, int horizontal);
///< \return Update required?
virtual bool handleRollKeys (int delta);
///< \return Update required?
};
}
#endif

@ -0,0 +1,6 @@
#include "pagedworldspacewidget.hpp"
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget *parent)
: WorldspaceWidget (parent)
{}

@ -0,0 +1,18 @@
#ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H
#define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H
#include "worldspacewidget.hpp"
namespace CSVRender
{
class PagedWorldspaceWidget : public WorldspaceWidget
{
Q_OBJECT
public:
PagedWorldspaceWidget (QWidget *parent);
};
}
#endif

@ -2,24 +2,34 @@
#include <QEvent> #include <QEvent>
#include <QResizeEvent> #include <QResizeEvent>
#include <QTimer>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreSceneNode.h>
#include <OgreViewport.h>
#include "navigation.hpp"
namespace CSVRender namespace CSVRender
{ {
SceneWidget::SceneWidget(QWidget *parent) SceneWidget::SceneWidget(QWidget *parent)
: QWidget(parent) : QWidget(parent)
, mWindow(NULL) , mWindow(NULL)
, mCamera(NULL) , mCamera(NULL)
, mSceneMgr(NULL) , mSceneMgr(NULL), mNavigation (0), mUpdate (false)
, mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false)
, mKeyRollLeft (false), mKeyRollRight (false)
, mFast (false), mDragging (false), mMod1 (false)
, mFastFactor (4) /// \todo make this configurable
{ {
setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_NoSystemBackground);
setFocusPolicy (Qt::StrongFocus);
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
// Throw in a random color just to make sure multiple scenes work // Throw in a random color just to make sure multiple scenes work
@ -44,6 +54,16 @@ namespace CSVRender
mCamera->lookAt(0,0,0); mCamera->lookAt(0,0,0);
mCamera->setNearClipDistance(0.1); mCamera->setNearClipDistance(0.1);
mCamera->setFarClipDistance(3000); mCamera->setFarClipDistance(3000);
QTimer *timer = new QTimer (this);
connect (timer, SIGNAL (timeout()), this, SLOT (update()));
timer->start (20); /// \todo make this configurable
}
void SceneWidget::setAmbient (const Ogre::ColourValue& colour)
{
mSceneMgr->setAmbientLight (colour);
} }
void SceneWidget::updateOgreWindow() void SceneWidget::updateOgreWindow()
@ -81,7 +101,21 @@ namespace CSVRender
SceneWidget::~SceneWidget() SceneWidget::~SceneWidget()
{ {
Ogre::Root::getSingleton().destroyRenderTarget(mWindow); if (mWindow)
Ogre::Root::getSingleton().destroyRenderTarget (mWindow);
if (mSceneMgr)
Ogre::Root::getSingleton().destroySceneManager (mSceneMgr);
}
void SceneWidget::setNavigation (Navigation *navigation)
{
if ((mNavigation = navigation))
{
mNavigation->setFastModeFactor (mFast ? mFastFactor : 1);
if (mNavigation->activate (mCamera))
mUpdate = true;
}
} }
void SceneWidget::paintEvent(QPaintEvent* e) void SceneWidget::paintEvent(QPaintEvent* e)
@ -93,7 +127,6 @@ namespace CSVRender
e->accept(); e->accept();
} }
QPaintEngine* SceneWidget::paintEngine() const QPaintEngine* SceneWidget::paintEngine() const
{ {
// We don't want another paint engine to get in the way. // We don't want another paint engine to get in the way.
@ -130,4 +163,151 @@ namespace CSVRender
return QWidget::event(e); return QWidget::event(e);
} }
void SceneWidget::keyPressEvent (QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_W: mKeyForward = true; break;
case Qt::Key_S: mKeyBackward = true; break;
case Qt::Key_A: mKeyLeft = true; break;
case Qt::Key_D: mKeyRight = true; break;
case Qt::Key_Q: mKeyRollLeft = true; break;
case Qt::Key_E: mKeyRollRight = true; break;
case Qt::Key_Control: mMod1 = true; break;
case Qt::Key_Shift:
mFast = true;
if (mNavigation)
mNavigation->setFastModeFactor (mFastFactor);
break;
default: QWidget::keyPressEvent (event);
}
}
void SceneWidget::keyReleaseEvent (QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_W: mKeyForward = false; break;
case Qt::Key_S: mKeyBackward = false; break;
case Qt::Key_A: mKeyLeft = false; break;
case Qt::Key_D: mKeyRight = false; break;
case Qt::Key_Q: mKeyRollLeft = false; break;
case Qt::Key_E: mKeyRollRight = false; break;
case Qt::Key_Control: mMod1 = false; break;
case Qt::Key_Shift:
mFast = false;
if (mNavigation)
mNavigation->setFastModeFactor (1);
break;
default: QWidget::keyReleaseEvent (event);
}
}
void SceneWidget::wheelEvent (QWheelEvent *event)
{
if (mNavigation)
if (event->delta())
if (mNavigation->wheelMoved (event->delta()))
mUpdate = true;
}
void SceneWidget::leaveEvent (QEvent *event)
{
mDragging = false;
}
void SceneWidget::mouseMoveEvent (QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
if (mDragging)
{
QPoint diff = mOldPos-event->pos();
mOldPos = event->pos();
if (mNavigation)
if (mNavigation->mouseMoved (diff, mMod1 ? 1 : 0))
mUpdate = true;
}
else
{
mDragging = true;
mOldPos = event->pos();
}
}
}
void SceneWidget::mouseReleaseEvent (QMouseEvent *event)
{
if (!(event->buttons() & Qt::LeftButton))
mDragging = false;
}
void SceneWidget::focusOutEvent (QFocusEvent *event)
{
mKeyForward = false;
mKeyBackward = false;
mKeyLeft = false;
mKeyRight = false;
mFast = false;
mMod1 = false;
QWidget::focusOutEvent (event);
}
void SceneWidget::update()
{
if (mNavigation)
{
int horizontal = 0;
int vertical = 0;
if (mKeyForward && !mKeyBackward)
vertical = 1;
else if (!mKeyForward && mKeyBackward)
vertical = -1;
if (mKeyLeft && !mKeyRight)
horizontal = -1;
else if (!mKeyLeft && mKeyRight)
horizontal = 1;
if (horizontal || vertical)
if (mNavigation->handleMovementKeys (vertical, horizontal))
mUpdate = true;
int roll = 0;
if (mKeyRollLeft && !mKeyRollRight)
roll = 1;
else if (!mKeyRollLeft && mKeyRollRight)
roll = -1;
if (roll)
if (mNavigation->handleRollKeys (roll))
mUpdate = true;
}
if (mUpdate)
{
mUpdate = false;
mWindow->update();
}
}
int SceneWidget::getFastFactor() const
{
return mFast ? mFastFactor : 1;
}
} }

@ -8,33 +8,77 @@ namespace Ogre
class Camera; class Camera;
class SceneManager; class SceneManager;
class RenderWindow; class RenderWindow;
class ColourValue;
} }
namespace CSVRender namespace CSVRender
{ {
class Navigation;
class SceneWidget : public QWidget class SceneWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
SceneWidget(QWidget *parent);
virtual ~SceneWidget(void);
QPaintEngine* paintEngine() const; SceneWidget(QWidget *parent);
virtual ~SceneWidget();
private: QPaintEngine* paintEngine() const;
void paintEvent(QPaintEvent* e);
void resizeEvent(QResizeEvent* e);
bool event(QEvent* e);
void updateOgreWindow(); void setAmbient (const Ogre::ColourValue& colour);
///< \note The actual ambient colour may differ based on lighting settings.
Ogre::Camera* mCamera; protected:
Ogre::SceneManager* mSceneMgr;
Ogre::RenderWindow* mWindow; void setNavigation (Navigation *navigation);
}; ///< \attention The ownership of \a navigation is not transferred to *this.
private:
void paintEvent(QPaintEvent* e);
void resizeEvent(QResizeEvent* e);
bool event(QEvent* e);
void keyPressEvent (QKeyEvent *event);
void keyReleaseEvent (QKeyEvent *event);
void focusOutEvent (QFocusEvent *event);
void wheelEvent (QWheelEvent *event);
void leaveEvent (QEvent *event);
void mouseMoveEvent (QMouseEvent *event);
void mouseReleaseEvent (QMouseEvent *event);
void updateOgreWindow();
int getFastFactor() const;
Ogre::Camera* mCamera;
Ogre::SceneManager* mSceneMgr;
Ogre::RenderWindow* mWindow;
Navigation *mNavigation;
bool mUpdate;
bool mKeyForward;
bool mKeyBackward;
bool mKeyLeft;
bool mKeyRight;
bool mKeyRollLeft;
bool mKeyRollRight;
bool mFast;
bool mDragging;
bool mMod1;
QPoint mOldPos;
int mFastFactor;
private slots:
void update();
};
} }
#endif #endif

@ -0,0 +1,66 @@
#include "unpagedworldspacewidget.hpp"
#include <OgreColourValue.h>
#include "../../model/doc/document.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp"
void CSVRender::UnpagedWorldspaceWidget::update()
{
const CSMWorld::Record<CSMWorld::Cell>& record =
dynamic_cast<const CSMWorld::Record<CSMWorld::Cell>&> (mCellsModel->getRecord (mCellId));
Ogre::ColourValue colour;
colour.setAsABGR (record.get().mAmbi.mAmbient);
setAmbient (colour);
/// \todo deal with mSunlight and mFog/mForDensity
}
CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId,
CSMDoc::Document& document, QWidget *parent)
: WorldspaceWidget (parent), mCellId (cellId)
{
mCellsModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));
connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&)));
connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int)));
update();
}
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
int index = mCellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, index);
if (cellIndex.row()>=topLeft.row() && cellIndex.row()<=bottomRight.row())
{
if (mCellsModel->data (cellIndex).toInt()==CSMWorld::RecordBase::State_Deleted)
{
emit closeRequest();
}
else
{
/// \todo possible optimisation: check columns and update only if relevant columns have
/// changed
update();
}
}
}
void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelIndex& parent,
int start, int end)
{
QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, 0);
if (cellIndex.row()>=start && cellIndex.row()<=end)
emit closeRequest();
}

@ -0,0 +1,44 @@
#ifndef OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H
#define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H
#include <string>
#include "worldspacewidget.hpp"
class QModelIndex;
namespace CSMDoc
{
class Document;
}
namespace CSMWorld
{
class IdTable;
}
namespace CSVRender
{
class UnpagedWorldspaceWidget : public WorldspaceWidget
{
Q_OBJECT
std::string mCellId;
CSMWorld::IdTable *mCellsModel;
void update();
public:
UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document,
QWidget *parent);
private slots:
void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
};
}
#endif

@ -0,0 +1,38 @@
#include "worldspacewidget.hpp"
#include "../world/scenetoolmode.hpp"
CSVRender::WorldspaceWidget::WorldspaceWidget (QWidget *parent)
: SceneWidget (parent)
{}
void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode)
{
if (mode=="1st")
setNavigation (&m1st);
else if (mode=="free")
setNavigation (&mFree);
else if (mode=="orbit")
setNavigation (&mOrbit);
}
void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
{
setNavigation (&m1st);
}
CSVWorld::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (
CSVWorld::SceneToolbar *parent)
{
CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent);
tool->addButton (":door.png", "1st"); /// \todo replace icons
tool->addButton (":GMST.png", "free");
tool->addButton (":Info.png", "orbit");
connect (tool, SIGNAL (modeChanged (const std::string&)),
this, SLOT (selectNavigationMode (const std::string&)));
return tool;
}

@ -0,0 +1,46 @@
#ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H
#define OPENCS_VIEW_WORLDSPACEWIDGET_H
#include "scenewidget.hpp"
#include "navigation1st.hpp"
#include "navigationfree.hpp"
#include "navigationorbit.hpp"
namespace CSVWorld
{
class SceneToolMode;
class SceneToolbar;
}
namespace CSVRender
{
class WorldspaceWidget : public SceneWidget
{
Q_OBJECT
CSVRender::Navigation1st m1st;
CSVRender::NavigationFree mFree;
CSVRender::NavigationOrbit mOrbit;
public:
WorldspaceWidget (QWidget *parent = 0);
CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent);
///< \important The created tool is not added to the toolbar (via addTool). Doing that
/// is the responsibility of the calling function.
void selectDefaultNavigationMode();
private slots:
void selectNavigationMode (const std::string& mode);
signals:
void closeRequest();
};
}
#endif

@ -40,5 +40,5 @@ void CSVTools::ReportSubView::updateEditorSetting (const QString& key, const QSt
void CSVTools::ReportSubView::show (const QModelIndex& index) void CSVTools::ReportSubView::show (const QModelIndex& index)
{ {
focusId (mModel->getUniversalId (index.row())); focusId (mModel->getUniversalId (index.row()), "");
} }

@ -9,7 +9,8 @@
#include "../filter/filterbox.hpp" #include "../filter/filterbox.hpp"
#include "../render/scenewidget.hpp" #include "../render/pagedworldspacewidget.hpp"
#include "../render/unpagedworldspacewidget.hpp"
#include "tablebottombox.hpp" #include "tablebottombox.hpp"
#include "creator.hpp" #include "creator.hpp"
@ -32,38 +33,21 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
layout2->setContentsMargins (QMargins (0, 0, 0, 0)); layout2->setContentsMargins (QMargins (0, 0, 0, 0));
SceneToolbar *toolbar = new SceneToolbar (48, this); SceneToolbar *toolbar = new SceneToolbar (48, this);
// test
SceneToolMode *tool = new SceneToolMode (toolbar);
tool->addButton (":door.png", "a");
tool->addButton (":GMST.png", "b");
tool->addButton (":Info.png", "c");
toolbar->addTool (tool);
toolbar->addTool (new SceneToolMode (toolbar));
toolbar->addTool (new SceneToolMode (toolbar));
toolbar->addTool (new SceneToolMode (toolbar));
layout2->addWidget (toolbar, 0);
// temporarily disable OGRE-integration (need to fix path problem first) if (id.getId()[0]=='#')
#if 0 mScene = new CSVRender::PagedWorldspaceWidget (this);
CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); else
mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this);
layout2->addWidget (sceneWidget, 1); SceneToolMode *tool = mScene->makeNavigationSelector (toolbar);
toolbar->addTool (tool);
layout->insertLayout (0, layout2, 1); layout2->addWidget (toolbar, 0);
#endif
/// \todo replace with rendering widget
QPalette palette2 (palette());
palette2.setColor (QPalette::Background, Qt::white);
QLabel *placeholder = new QLabel ("Here goes the 3D scene", this);
placeholder->setAutoFillBackground (true);
placeholder->setPalette (palette2);
placeholder->setAlignment (Qt::AlignHCenter);
layout2->addWidget (placeholder, 1); layout2->addWidget (mScene, 1);
layout->insertLayout (0, layout2, 1); layout->insertLayout (0, layout2, 1);
CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);
layout->insertWidget (0, filterBox); layout->insertWidget (0, filterBox);
@ -73,6 +57,10 @@ toolbar->addTool (new SceneToolMode (toolbar));
widget->setLayout (layout); widget->setLayout (layout);
setWidget (widget); setWidget (widget);
mScene->selectDefaultNavigationMode();
connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest()));
} }
void CSVWorld::SceneSubView::setEditLock (bool locked) void CSVWorld::SceneSubView::setEditLock (bool locked)
@ -91,3 +79,8 @@ void CSVWorld::SceneSubView::setStatusBar (bool show)
{ {
mBottom->setStatusBar (show); mBottom->setStatusBar (show);
} }
void CSVWorld::SceneSubView::closeRequest()
{
deleteLater();
}

@ -10,6 +10,11 @@ namespace CSMDoc
class Document; class Document;
} }
namespace CSVRender
{
class WorldspaceWidget;
}
namespace CSVWorld namespace CSVWorld
{ {
class Table; class Table;
@ -21,6 +26,7 @@ namespace CSVWorld
Q_OBJECT Q_OBJECT
TableBottomBox *mBottom; TableBottomBox *mBottom;
CSVRender::WorldspaceWidget *mScene;
public: public:
@ -31,6 +37,10 @@ namespace CSVWorld
virtual void updateEditorSetting (const QString& key, const QString& value); virtual void updateEditorSetting (const QString& key, const QString& value);
virtual void setStatusBar (bool show); virtual void setStatusBar (bool show);
private slots:
void closeRequest();
}; };
} }

@ -2,7 +2,6 @@
#include "table.hpp" #include "table.hpp"
#include <QHeaderView> #include <QHeaderView>
#include <QAction> #include <QAction>
#include <QApplication> #include <QApplication>
#include <QMenu> #include <QMenu>
@ -10,6 +9,8 @@
#include <QString> #include <QString>
#include <QtCore/qnamespace.h> #include <QtCore/qnamespace.h>
#include "../../model/doc/document.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
@ -35,8 +36,21 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (selectedRows.size()==1) if (selectedRows.size()==1)
{ {
menu.addAction (mEditAction); menu.addAction (mEditAction);
if (mCreateAction) if (mCreateAction)
menu.addAction(mCloneAction); menu.addAction(mCloneAction);
if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None)
{
int row = selectedRows.begin()->row();
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
CSMWorld::UniversalId id = mModel->view (row).first;
if (!mDocument.getData().getCells().getRecord (id.getId()).isDeleted())
menu.addAction (mViewAction);
}
} }
if (mCreateAction) if (mCreateAction)
@ -162,11 +176,12 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
return deletableIds; return deletableIds;
} }
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, const CSMDoc::Document& document) bool createAndDelete, bool sorting, CSMDoc::Document& document)
: mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) : mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0),
mDocument (document)
{ {
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTable&> (*mDocument.getData().getTableModel (id));
mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
mProxyModel->setSourceModel (mModel); mProxyModel->setSourceModel (mModel);
@ -190,7 +205,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display,
undoStack, this); mDocument.getUndoStack(), this);
mDelegates.push_back (delegate); mDelegates.push_back (delegate);
setItemDelegateForColumn (i, delegate); setItemDelegateForColumn (i, delegate);
@ -230,6 +245,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord()));
addAction (mMoveDownAction); addAction (mMoveDownAction);
mViewAction = new QAction (tr ("View"), this);
connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord()));
addAction (mViewAction);
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate())); this, SLOT (tableSizeUpdate()));
@ -268,13 +287,13 @@ void CSVWorld::Table::revertRecord()
if (revertableIds.size()>0) if (revertableIds.size()>0)
{ {
if (revertableIds.size()>1) if (revertableIds.size()>1)
mUndoStack.beginMacro (tr ("Revert multiple records")); mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
for (std::vector<std::string>::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) for (std::vector<std::string>::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter)
mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); mDocument.getUndoStack().push (new CSMWorld::RevertCommand (*mModel, *iter));
if (revertableIds.size()>1) if (revertableIds.size()>1)
mUndoStack.endMacro(); mDocument.getUndoStack().endMacro();
} }
} }
} }
@ -288,13 +307,13 @@ void CSVWorld::Table::deleteRecord()
if (deletableIds.size()>0) if (deletableIds.size()>0)
{ {
if (deletableIds.size()>1) if (deletableIds.size()>1)
mUndoStack.beginMacro (tr ("Delete multiple records")); mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
for (std::vector<std::string>::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) for (std::vector<std::string>::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter)
mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (*mModel, *iter));
if (deletableIds.size()>1) if (deletableIds.size()>1)
mUndoStack.endMacro(); mDocument.getUndoStack().endMacro();
} }
} }
} }
@ -306,7 +325,7 @@ void CSVWorld::Table::editRecord()
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
if (selectedRows.size()==1) if (selectedRows.size()==1)
emit editRequest (selectedRows.begin()->row()); emit editRequest (getUniversalId (selectedRows.begin()->row()), "");
} }
} }
@ -347,7 +366,7 @@ void CSVWorld::Table::moveUpRecord()
for (int i=1; i<row2-row; ++i) for (int i=1; i<row2-row; ++i)
newOrder[i] = i; newOrder[i] = i;
mUndoStack.push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder)); mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder));
} }
} }
} }
@ -376,11 +395,28 @@ void CSVWorld::Table::moveDownRecord()
for (int i=1; i<row2-row; ++i) for (int i=1; i<row2-row; ++i)
newOrder[i] = i; newOrder[i] = i;
mUndoStack.push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder)); mDocument.getUndoStack().push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder));
} }
} }
} }
void CSVWorld::Table::viewRecord()
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
if (selectedRows.size()==1)
{
int row =selectedRows.begin()->row();
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
std::pair<CSMWorld::UniversalId, std::string> params = mModel->view (row);
if (params.first.getType()!=CSMWorld::UniversalId::Type_None)
emit editRequest (params.first, params.second);
}
}
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
{ {
int columns = mModel->columnCount(); int columns = mModel->columnCount();
@ -517,7 +553,7 @@ void CSVWorld::Table::dropEvent(QDropEvent *event)
std::auto_ptr<CSMWorld::ModifyCommand> command (new CSMWorld::ModifyCommand std::auto_ptr<CSMWorld::ModifyCommand> command (new CSMWorld::ModifyCommand
(*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str()))));
mUndoStack.push (command.release()); mDocument.getUndoStack().push (command.release());
} }
} //TODO handle drops from different document } //TODO handle drops from different document
} }

@ -10,13 +10,14 @@
#include "../../model/filter/node.hpp" #include "../../model/filter/node.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
namespace CSMDoc {
class Document;
}
class QUndoStack; class QUndoStack;
class QAction; class QAction;
namespace CSMDoc
{
class Document;
}
namespace CSMWorld namespace CSMWorld
{ {
class Data; class Data;
@ -35,7 +36,6 @@ namespace CSVWorld
Q_OBJECT Q_OBJECT
std::vector<CommandDelegate *> mDelegates; std::vector<CommandDelegate *> mDelegates;
QUndoStack& mUndoStack;
QAction *mEditAction; QAction *mEditAction;
QAction *mCreateAction; QAction *mCreateAction;
QAction *mCloneAction; QAction *mCloneAction;
@ -43,14 +43,12 @@ namespace CSVWorld
QAction *mDeleteAction; QAction *mDeleteAction;
QAction *mMoveUpAction; QAction *mMoveUpAction;
QAction *mMoveDownAction; QAction *mMoveDownAction;
QAction *mViewAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
bool mEditLock; bool mEditLock;
int mRecordStatusDisplay; int mRecordStatusDisplay;
CSMDoc::Document& mDocument;
/// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you
/// should NOT use it for anything else.
const CSMDoc::Document& mDocument;
private: private:
@ -70,9 +68,8 @@ namespace CSVWorld
public: public:
Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, Table (const CSMWorld::UniversalId& id, bool createAndDelete,
bool sorting, const CSMDoc::Document& document); bool sorting, CSMDoc::Document& document);
///< \param createAndDelete Allow creation and deletion of records. ///< \param createAndDelete Allow creation and deletion of records.
/// \param sorting Allow changing order of rows in the view via column headers. /// \param sorting Allow changing order of rows in the view via column headers.
@ -86,7 +83,7 @@ namespace CSVWorld
signals: signals:
void editRequest (int row); void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);
void selectionSizeChanged (int size); void selectionSizeChanged (int size);
@ -112,6 +109,8 @@ namespace CSVWorld
void moveDownRecord(); void moveDownRecord();
void viewRecord();
public slots: public slots:
void tableSizeUpdate(); void tableSizeUpdate();

@ -24,7 +24,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0);
layout->insertWidget (0, mTable = layout->insertWidget (0, mTable =
new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2);
CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);
@ -36,7 +36,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
setWidget (widget); setWidget (widget);
connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),
this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&)));
connect (mTable, SIGNAL (selectionSizeChanged (int)), connect (mTable, SIGNAL (selectionSizeChanged (int)),
mBottom, SLOT (selectionSizeChanged (int))); mBottom, SLOT (selectionSizeChanged (int)));
@ -81,9 +82,9 @@ void CSVWorld::TableSubView::setEditLock (bool locked)
mBottom->setEditLock (locked); mBottom->setEditLock (locked);
} }
void CSVWorld::TableSubView::editRequest (int row) void CSVWorld::TableSubView::editRequest (const CSMWorld::UniversalId& id, const std::string& hint)
{ {
focusId (mTable->getUniversalId (row)); focusId (id, hint);
} }
void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue)

@ -53,7 +53,7 @@ namespace CSVWorld
private slots: private slots:
void editRequest (int row); void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);
void cloneRequest (const CSMWorld::UniversalId& toClone); void cloneRequest (const CSMWorld::UniversalId& toClone);
void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, void createFilterRequest(std::vector< CSMWorld::UniversalId >& types,
Qt::DropAction action); Qt::DropAction action);

@ -15,7 +15,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst effectmanager terrainstorage renderconst effectmanager weaponanimation
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -159,3 +159,10 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw gcov) target_link_libraries(openmw gcov)
endif() endif()
if (MSVC)
# Debug version needs increased number of sections beyond 2^16
if (CMAKE_CL_64)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
endif (CMAKE_CL_64)
endif (MSVC)

@ -1,6 +1,7 @@
#include "engine.hpp" #include "engine.hpp"
#include <stdexcept> #include <stdexcept>
#include <iomanip>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
@ -403,7 +404,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create the world // Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles,
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
mActivationDistanceOverride)); mActivationDistanceOverride, mCellName));
MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer()); input->setPlayer(&mEnvironment.getWorld()->getPlayer());
@ -439,31 +440,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mechanics->buildPlayer(); mechanics->buildPlayer();
window->updatePlayer(); window->updatePlayer();
// load cell
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
if (!mCellName.empty())
{
if (world->findExteriorPosition(mCellName, pos)) {
world->changeToExteriorCell (pos);
}
else {
world->findInteriorPosition(mCellName, pos);
world->changeToInteriorCell (mCellName, pos);
}
}
else
{
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.pos[2] = 0;
world->changeToExteriorCell (pos);
}
Ogre::FrameEvent event;
event.timeSinceLastEvent = 0;
event.timeSinceLastFrame = 0;
frameRenderingQueued(event);
mOgre->getRoot()->addFrameListener (this); mOgre->getRoot()->addFrameListener (this);
// scripts // scripts
@ -501,15 +477,15 @@ void OMW::Engine::go()
// Play some good 'ol tunes // Play some good 'ol tunes
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// start in main menu // start in main menu
if (!mSkipMenu) if (!mSkipMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
else else
MWBase::Environment::get().getStateManager()->newGame (true); MWBase::Environment::get().getStateManager()->newGame (true);
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// Start the main rendering loop // Start the main rendering loop
while (!mEnvironment.get().getStateManager()->hasQuitRequest()) while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame(); Ogre::Root::getSingleton().renderOneFrame();
@ -621,4 +597,4 @@ void OMW::Engine::setActivationDistanceOverride (int distance)
void OMW::Engine::setWarningsMode (int mode) void OMW::Engine::setWarningsMode (int mode)
{ {
mWarningsMode = mode; mWarningsMode = mode;
} }

@ -3,7 +3,7 @@
#include <string> #include <string>
#include <libs/platform/stdint.h> #include <stdint.h>
namespace ESM namespace ESM
{ {

@ -20,6 +20,9 @@ namespace MWBase
InputManager() {} InputManager() {}
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual ~InputManager() {} virtual ~InputManager() {}
virtual void update(float dt, bool loading) = 0; virtual void update(float dt, bool loading) = 0;

@ -5,7 +5,7 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <libs/platform/stdint.h> #include <stdint.h>
#include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/journalentry.hpp"
#include "../mwdialogue/topic.hpp" #include "../mwdialogue/topic.hpp"

@ -101,7 +101,8 @@ namespace MWBase
virtual ~World() {} virtual ~World() {}
virtual void startNewGame() = 0; virtual void startNewGame (bool bypass) = 0;
///< \param bypass Bypass regular game start.
virtual void clear() = 0; virtual void clear() = 0;
@ -274,7 +275,7 @@ namespace MWBase
virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
virtual void virtual void
moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore &newCell, float x, float y, float z) = 0; moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0;
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
@ -282,7 +283,7 @@ namespace MWBase
virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
///< place an object in a "safe" location (ie not in the void, etc). ///< place an object in a "safe" location (ie not in the void, etc).
virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)
@ -464,8 +465,10 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void castSpell (const MWWorld::Ptr& actor) = 0;
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0; const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0; virtual const std::vector<std::string>& getContentFiles() const = 0;

@ -7,6 +7,7 @@
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/magiceffects.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/disease.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -243,15 +244,7 @@ namespace MWClass
Ogre::Vector3 hitPosition = result.second; Ogre::Vector3 hitPosition = result.second;
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat);
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = ref->mBase->mData.mCombat +
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= stats.getFatigueTerm();
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
hitchance -= otherstats.getEvasion();
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{ {
@ -334,6 +327,8 @@ namespace MWClass
if (damage > 0) if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
MWMechanics::diseaseContact(victim, ptr);
victim.getClass().onHit(victim, damage, true, weapon, ptr, true); victim.getClass().onHit(victim, damage, true, weapon, ptr, true);
} }
@ -530,7 +525,7 @@ namespace MWClass
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f; moveSpeed = 0.0f;
else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
world->isLevitationEnabled())) world->isLevitationEnabled()))
{ {
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
@ -683,7 +678,15 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Creature>().insert(*ref), &cell); return MWWorld::Ptr(&cell.get<ESM::Creature>().insert(*ref), &cell);
} }
bool Creature::isFlying(const MWWorld::Ptr &ptr) const bool Creature::isBipedal(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Bipedal;
}
bool Creature::canFly(const MWWorld::Ptr &ptr) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();
@ -691,6 +694,22 @@ namespace MWClass
return ref->mBase->mFlags & ESM::Creature::Flies; return ref->mBase->mFlags & ESM::Creature::Flies;
} }
bool Creature::canSwim(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Swims;
}
bool Creature::canWalk(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Walks;
}
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
{ {
if(name == "left") if(name == "left")

@ -124,7 +124,10 @@ namespace MWClass
return true; return true;
} }
virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual bool isBipedal (const MWWorld::Ptr &ptr) const;
virtual bool canFly (const MWWorld::Ptr &ptr) const;
virtual bool canSwim (const MWWorld::Ptr &ptr) const;
virtual bool canWalk (const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;

@ -57,7 +57,7 @@ namespace MWClass
MWWorld::ManualRef ref(store, id); MWWorld::ManualRef ref(store, id);
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
// TODO: hold on to this for respawn purposes later // TODO: hold on to this for respawn purposes later
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos);
} }
ptr.getRefData().setCustomData(data.release()); ptr.getRefData().setCustomData(data.release());

@ -498,15 +498,7 @@ namespace MWClass
if(!weapon.isEmpty()) if(!weapon.isEmpty())
weapskill = get(weapon).getEquipmentSkill(weapon); weapskill = get(weapon).getEquipmentSkill(weapon);
MWMechanics::NpcStats &stats = getNpcStats(ptr); float hitchance = MWMechanics::getHitChance(ptr, victim, ptr.getClass().getSkill(ptr, weapskill));
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = stats.getSkill(weapskill).getModified() +
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= stats.getFatigueTerm();
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
hitchance -= otherstats.getEvasion();
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{ {
@ -516,6 +508,7 @@ namespace MWClass
bool healthdmg; bool healthdmg;
float damage = 0.0f; float damage = 0.0f;
MWMechanics::NpcStats &stats = getNpcStats(ptr);
if(!weapon.isEmpty()) if(!weapon.isEmpty())
{ {
const bool weaphashealth = get(weapon).hasItemHealth(weapon); const bool weaphashealth = get(weapon).hasItemHealth(weapon);
@ -615,6 +608,8 @@ namespace MWClass
if (healthdmg && damage > 0) if (healthdmg && damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
MWMechanics::diseaseContact(victim, ptr);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
} }
@ -648,9 +643,6 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
} }
if (!attacker.isEmpty())
MWMechanics::diseaseContact(ptr, attacker);
if (damage > 0.0f && !object.isEmpty()) if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
@ -681,12 +673,7 @@ namespace MWClass
else else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(object.isEmpty()) if(ishealth)
{
if(ishealth)
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
}
else if(ishealth)
{ {
// Hit percentages: // Hit percentages:
// cuirass = 30% // cuirass = 30%

@ -6,7 +6,7 @@
#include "MyGUI_TextureUtility.h" #include "MyGUI_TextureUtility.h"
#include "MyGUI_FactoryManager.h" #include "MyGUI_FactoryManager.h"
#include <platform/stdint.h> #include <stdint.h>
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>

@ -5,7 +5,7 @@
#include "MyGUI_Widget.h" #include "MyGUI_Widget.h"
#include <functional> #include <functional>
#include <platform/stdint.h> #include <stdint.h>
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>

@ -1,5 +1,7 @@
#include "enchantingdialog.hpp" #include "enchantingdialog.hpp"
#include <iomanip>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"

@ -604,15 +604,22 @@ namespace MWGui
mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop());
} }
void HUD::updateEnemyHealthBar()
{
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
mEnemyHealth->setProgressRange(100);
// Health is usually cast to int before displaying. Actors die whenever they are < 1 health.
// Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :)
mEnemyHealth->setProgressPosition(int(stats.getHealth().getCurrent()) / stats.getHealth().getModified() * 100);
}
void HUD::update() void HUD::update()
{ {
mSpellIcons->updateWidgets(mEffectBox, true); mSpellIcons->updateWidgets(mEffectBox, true);
if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) if (!mEnemy.isEmpty() && mEnemyHealth->getVisible())
{ {
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); updateEnemyHealthBar();
mEnemyHealth->setProgressRange(100);
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
} }
if (mIsDrowning) if (mIsDrowning)
@ -629,9 +636,7 @@ namespace MWGui
if (!mEnemyHealth->getVisible()) if (!mEnemyHealth->getVisible())
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
mEnemyHealth->setVisible(true); mEnemyHealth->setVisible(true);
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); updateEnemyHealthBar();
mEnemyHealth->setProgressRange(100);
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
} }
} }

@ -112,6 +112,8 @@ namespace MWGui
void onMagicClicked(MyGUI::Widget* _sender); void onMagicClicked(MyGUI::Widget* _sender);
void onMapClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender);
void updateEnemyHealthBar();
void updatePositions(); void updatePositions();
}; };
} }

@ -514,6 +514,9 @@ namespace MWGui
void InventoryWindow::pickUpObject (MWWorld::Ptr object) void InventoryWindow::pickUpObject (MWWorld::Ptr object)
{ {
// If the inventory is not yet enabled, don't pick anything up
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
return;
// make sure the object is of a type that can be picked up // make sure the object is of a type that can be picked up
std::string type = object.getTypeName(); std::string type = object.getTypeName();
if ( (type != typeid(ESM::Apparatus).name()) if ( (type != typeid(ESM::Apparatus).name())

@ -206,9 +206,9 @@ struct JournalViewModelImpl : JournalViewModel
const MWDialogue::Quest& quest = i->second; const MWDialogue::Quest& quest = i->second;
// Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal.
if (quest.getName().empty()) // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (i->first)); // to appear in the quest book.
else if (!quest.getName().empty())
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (quest.getName())); visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (quest.getName()));
} }
} }

@ -4,7 +4,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <platform/stdint.h> #include <stdint.h>
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>

@ -1,6 +1,13 @@
#include "loadingscreen.hpp" #include "loadingscreen.hpp"
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
#include <OgreMaterialManager.h>
#include <OgreTechnique.h>
#include <OgreRectangle2D.h>
#include <OgreSceneNode.h>
#include <OgreTextureManager.h>
#include <OgreViewport.h>
#include <OgreHardwarePixelBuffer.h>
#include <openengine/ogre/fader.hpp> #include <openengine/ogre/fader.hpp>
@ -66,6 +73,10 @@ namespace MWGui
void LoadingScreen::loadingOn() void LoadingScreen::loadingOn()
{ {
// Early-out if already on
if (mRectangle->getVisible())
return;
// Temporarily turn off VSync, we want to do actual loading rather than waiting for the screen to sync. // Temporarily turn off VSync, we want to do actual loading rather than waiting for the screen to sync.
// Threaded loading would be even better, of course - especially because some drivers force VSync to on and we can't change it. // Threaded loading would be even better, of course - especially because some drivers force VSync to on and we can't change it.
// In Ogre 1.8, the swapBuffers argument is useless and setVSyncEnabled is bugged with GLX, nothing we can do :/ // In Ogre 1.8, the swapBuffers argument is useless and setVSyncEnabled is bugged with GLX, nothing we can do :/
@ -74,16 +85,36 @@ namespace MWGui
mWindow->setVSyncEnabled(false); mWindow->setVSyncEnabled(false);
#endif #endif
if (!mFirstLoad)
{
mBackgroundImage->setImageTexture("");
int width = mWindow->getWidth();
int height = mWindow->getHeight();
const std::string textureName = "@loading_background";
Ogre::TexturePtr texture;
texture = Ogre::TextureManager::getSingleton().getByName(textureName);
if (texture.isNull())
{
texture = Ogre::TextureManager::getSingleton().createManual(textureName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
width, height, 0, mWindow->suggestPixelFormat(), Ogre::TU_DYNAMIC_WRITE_ONLY);
}
texture->unload();
texture->setWidth(width);
texture->setHeight(height);
texture->createInternalResources();
mWindow->copyContentsToMemory(texture->getBuffer()->lock(Ogre::Image::Box(0,0,width,height), Ogre::HardwareBuffer::HBL_DISCARD));
texture->getBuffer()->unlock();
mBackgroundImage->setImageTexture(texture->getName());
}
setVisible(true); setVisible(true);
if (mFirstLoad) if (mFirstLoad)
{ {
changeWallpaper(); changeWallpaper();
} }
else
{
mBackgroundImage->setImageTexture("");
}
MWBase::Environment::get().getWindowManager()->pushGuiMode(mFirstLoad ? GM_LoadingWallpaper : GM_Loading); MWBase::Environment::get().getWindowManager()->pushGuiMode(mFirstLoad ? GM_LoadingWallpaper : GM_Loading);
} }
@ -197,8 +228,6 @@ namespace MWGui
MWBase::Environment::get().getInputManager()->update(0, true); MWBase::Environment::get().getInputManager()->update(0, true);
mWindow->getViewport(0)->setClearEveryFrame(false);
// First, swap buffers from last draw, then, queue an update of the // First, swap buffers from last draw, then, queue an update of the
// window contents, but don't swap buffers (which would have // window contents, but don't swap buffers (which would have
// caused a sync / flush and would be expensive). // caused a sync / flush and would be expensive).
@ -208,9 +237,6 @@ namespace MWGui
mWindow->update(false); mWindow->update(false);
mWindow->getViewport(0)->setClearEveryFrame(true);
mRectangle->setVisible(false); mRectangle->setVisible(false);
// resume 3d rendering // resume 3d rendering

@ -2,6 +2,7 @@
#define MWGUI_LOADINGSCREEN_H #define MWGUI_LOADINGSCREEN_H
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreTimer.h>
#include "windowbase.hpp" #include "windowbase.hpp"

@ -1,5 +1,7 @@
#include "mainmenu.hpp" #include "mainmenu.hpp"
#include <components/version/version.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -20,6 +22,22 @@ namespace MWGui
, mButtonBox(0), mWidth (w), mHeight (h) , mButtonBox(0), mWidth (w), mHeight (h)
, mSaveGameDialog(NULL) , mSaveGameDialog(NULL)
{ {
getWidget(mVersionText, "VersionText");
std::stringstream sstream;
sstream << "OpenMW version: " << OPENMW_VERSION;
// adding info about git hash if availible
std::string rev = OPENMW_VERSION_COMMITHASH;
std::string tag = OPENMW_VERSION_TAGHASH;
if (!rev.empty() && !tag.empty())
{
rev = rev.substr(0,10);
sstream << "\nrevision: " << rev;
}
std::string output = sstream.str();
mVersionText->setCaption(output);
updateMenu(); updateMenu();
} }

@ -24,6 +24,7 @@ namespace MWGui
private: private:
MyGUI::Widget* mButtonBox; MyGUI::Widget* mButtonBox;
MyGUI::TextBox* mVersionText;
std::map<std::string, MWGui::ImageButton*> mButtons; std::map<std::string, MWGui::ImageButton*> mButtons;

@ -3,6 +3,7 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreVector2.h>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

@ -1,7 +1,7 @@
#ifndef MWGUI_MAPWINDOW_H #ifndef MWGUI_MAPWINDOW_H
#define MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H
#include <libs/platform/stdint.h> #include <stdint.h>
#include "windowpinnablebase.hpp" #include "windowpinnablebase.hpp"

@ -41,9 +41,13 @@ namespace MWGui
getWidget(mPreviewImage, "PreviewImage"); getWidget(mPreviewImage, "PreviewImage");
getWidget(mHeadRotate, "HeadRotate"); getWidget(mHeadRotate, "HeadRotate");
mHeadRotate->setScrollRange(50);
mHeadRotate->setScrollPosition(25); // Mouse wheel step is hardcoded to 50 in MyGUI 3.2 ("FIXME").
mHeadRotate->setScrollViewPage(10); // Give other steps the same value to accomodate.
mHeadRotate->setScrollRange(1000);
mHeadRotate->setScrollPosition(500);
mHeadRotate->setScrollViewPage(50);
mHeadRotate->setScrollPage(50);
mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
// Set up next/previous buttons // Set up next/previous buttons
@ -171,9 +175,9 @@ namespace MWGui
eventBack(); eventBack();
} }
void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position)
{ {
float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5) * 3.14 * 2;
float diff = angle - mCurrentAngle; float diff = angle - mCurrentAngle;
mPreview->update (diff); mPreview->update (diff);
mPreviewDirty = true; mPreviewDirty = true;

@ -1,5 +1,7 @@
#include "repair.hpp" #include "repair.hpp"
#include <iomanip>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

@ -1,5 +1,7 @@
#include "tooltips.hpp" #include "tooltips.hpp"
#include <iomanip>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

@ -3,6 +3,8 @@
#include <cassert> #include <cassert>
#include <iterator> #include <iterator>
#include <OgreTextureManager.h>
#include "MyGUI_UString.h" #include "MyGUI_UString.h"
#include "MyGUI_IPointer.h" #include "MyGUI_IPointer.h"
#include "MyGUI_ResourceImageSetPointer.h" #include "MyGUI_ResourceImageSetPointer.h"

@ -143,6 +143,13 @@ namespace MWInput
mControlSwitch["vanitymode"] = true; mControlSwitch["vanitymode"] = true;
} }
void InputManager::clear()
{
// Enable all controls
for (std::map<std::string, bool>::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it)
it->second = true;
}
InputManager::~InputManager() InputManager::~InputManager()
{ {
mInputBinder->save (mUserFile); mInputBinder->save (mUserFile);
@ -455,7 +462,7 @@ namespace MWInput
mInputBinder->adjustMouseRegion(width, height); mInputBinder->adjustMouseRegion(width, height);
} }
bool InputManager::keyPressed( const SDL_KeyboardEvent &arg ) void InputManager::keyPressed( const SDL_KeyboardEvent &arg )
{ {
// Cut, copy & paste // Cut, copy & paste
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
@ -501,7 +508,6 @@ namespace MWInput
if (kc != OIS::KC_UNASSIGNED) if (kc != OIS::KC_UNASSIGNED)
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0);
return true;
} }
void InputManager::textInput(const SDL_TextInputEvent &arg) void InputManager::textInput(const SDL_TextInputEvent &arg)
@ -512,23 +518,21 @@ namespace MWInput
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it);
} }
bool InputManager::keyReleased(const SDL_KeyboardEvent &arg ) void InputManager::keyReleased(const SDL_KeyboardEvent &arg )
{ {
mInputBinder->keyReleased (arg); mInputBinder->keyReleased (arg);
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc));
return true;
} }
bool InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id )
{ {
mInputBinder->mousePressed (arg, id); mInputBinder->mousePressed (arg, id);
if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT) if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT)
return true; // MyGUI has no use for these events return; // MyGUI has no use for these events
MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id));
if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0)
@ -539,20 +543,16 @@ namespace MWInput
MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f);
} }
} }
return true;
} }
bool InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id )
{ {
mInputBinder->mouseReleased (arg, id); mInputBinder->mouseReleased (arg, id);
MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id));
return true;
} }
bool InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) void InputManager::mouseMoved(const SFO::MouseMotionEvent &arg )
{ {
mInputBinder->mouseMoved (arg); mInputBinder->mouseMoved (arg);
@ -585,13 +585,13 @@ namespace MWInput
float rot[3]; float rot[3];
rot[0] = -y; rot[0] = -y;
rot[1] = 0.0f; rot[1] = 0.0f;
rot[2] = x; rot[2] = -x;
// Only actually turn player when we're not in vanity mode // Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot))
{ {
mPlayer->yaw(x); mPlayer->yaw(x);
mPlayer->pitch(-y); mPlayer->pitch(y);
} }
if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change
@ -600,8 +600,6 @@ namespace MWInput
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true); MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
} }
} }
return true;
} }
void InputManager::windowFocusChange(bool have_focus) void InputManager::windowFocusChange(bool have_focus)

@ -65,6 +65,9 @@ namespace MWInput
virtual ~InputManager(); virtual ~InputManager();
/// Clear all savegame-specific data
virtual void clear();
virtual void update(float dt, bool loading); virtual void update(float dt, bool loading);
void setPlayer (MWWorld::Player* player) { mPlayer = player; } void setPlayer (MWWorld::Player* player) { mPlayer = player; }
@ -86,13 +89,13 @@ namespace MWInput
virtual void resetToDefaultBindings(); virtual void resetToDefaultBindings();
public: public:
virtual bool keyPressed(const SDL_KeyboardEvent &arg ); virtual void keyPressed(const SDL_KeyboardEvent &arg );
virtual bool keyReleased( const SDL_KeyboardEvent &arg ); virtual void keyReleased( const SDL_KeyboardEvent &arg );
virtual void textInput (const SDL_TextInputEvent &arg); virtual void textInput (const SDL_TextInputEvent &arg);
virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); virtual void mouseMoved( const SFO::MouseMotionEvent &arg );
virtual void windowVisibilityChange( bool visible ); virtual void windowVisibilityChange( bool visible );
virtual void windowFocusChange( bool have_focus ); virtual void windowFocusChange( bool have_focus );

@ -210,7 +210,7 @@ namespace MWMechanics
&& LOS && LOS
) )
{ {
creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()));
creatureStats.setHostile(true); creatureStats.setHostile(true);
} }
} }
@ -541,7 +541,7 @@ namespace MWMechanics
// TODO: Add AI to follow player and fight for him // TODO: Add AI to follow player and fight for him
// TODO: VFX_SummonStart, VFX_SummonEnd // TODO: VFX_SummonStart, VFX_SummonEnd
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
} }
} }
else else

@ -15,7 +15,7 @@
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include "npcstats.hpp" #include "creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "character.hpp" // fixme: for getActiveWeapon #include "character.hpp" // fixme: for getActiveWeapon
@ -140,11 +140,12 @@ namespace MWMechanics
{ {
MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
//Get weapon speed and range //Get weapon speed and range
MWWorld::ContainerStoreIterator weaponSlot = MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype);
if (weaptype == WeapType_HandToHand) if (weaptype == WeapType_HandToHand)
{ {
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
@ -242,17 +243,21 @@ namespace MWMechanics
//target is at far distance: build path to target OR follow target (if previously actor had reached it once) //target is at far distance: build path to target OR follow target (if previously actor had reached it once)
mFollowTarget = false; mFollowTarget = false;
buildNewPath(actor); buildNewPath(actor); //may fail to build a path, check before use
//delete visited path node //delete visited path node
mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]);
//try shortcut //if no new path leave mTargetAngle unchanged
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) if(!mPathFinder.getPath().empty())
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); {
else //try shortcut
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
mRotate = true; mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
else
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
mRotate = true;
}
mMovement.mPosition[1] = 1; mMovement.mPosition[1] = 1;
mReadyToAttack = false; mReadyToAttack = false;
@ -302,9 +307,13 @@ namespace MWMechanics
dest.mZ = mTarget.getRefData().getPosition().pos[2]; dest.mZ = mTarget.getRefData().getPosition().pos[2];
Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ);
ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); float dist = -1; //hack to indicate first time, to construct a new path
Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); if(!mPathFinder.getPath().empty())
float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); {
ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length());
}
float targetPosThreshold; float targetPosThreshold;
bool isOutside = actor.getCell()->getCell()->isExterior(); bool isOutside = actor.getCell()->getCell()->isExterior();
@ -313,7 +322,7 @@ namespace MWMechanics
else else
targetPosThreshold = 100; targetPosThreshold = 100;
if(dist > targetPosThreshold) if((dist < 0) || (dist > targetPosThreshold))
{ {
//construct new path only if target has moved away more than on <targetPosThreshold> //construct new path only if target has moved away more than on <targetPosThreshold>
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
@ -334,8 +343,11 @@ namespace MWMechanics
//maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
//not the actual path length. Here we should know if the new path is actually more effective. //not the actual path length. Here we should know if the new path is actually more effective.
//if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) //if(pathFinder2.getPathSize() < mPathFinder.getPathSize())
newPathFinder.syncStart(mPathFinder.getPath()); if(!mPathFinder.getPath().empty())
mPathFinder = newPathFinder; {
newPathFinder.syncStart(mPathFinder.getPath());
mPathFinder = newPathFinder;
}
} }
} }
} }

@ -27,6 +27,11 @@ namespace
namespace MWMechanics namespace MWMechanics
{ {
// NOTE: determined empirically but probably need further tweaking
static const int COUNT_BEFORE_STUCK = 20;
static const int COUNT_BEFORE_RESET = 200;
static const int COUNT_EVADE = 7;
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
@ -36,6 +41,11 @@ namespace MWMechanics
, mX(0) , mX(0)
, mY(0) , mY(0)
, mZ(0) , mZ(0)
, mPrevX(0)
, mPrevY(0)
, mWalkState(State_Norm)
, mStuckCount(0)
, mEvadeCount(0)
, mSaidGreeting(false) , mSaidGreeting(false)
{ {
for(unsigned short counter = 0; counter < mIdle.size(); counter++) for(unsigned short counter = 0; counter < mIdle.size(); counter++)
@ -298,9 +308,91 @@ namespace MWMechanics
} }
else else
{ {
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); /* 1 n
* State_Norm <---> State_CheckStuck --> State_Evade
* ^ ^ | ^ | ^ | |
* | | | | | | | |
* | +---+ +---+ +---+ | m
* | any < n < m |
* +--------------------------------------------+
*/
bool samePosition = (abs(pos.pos[0] - mPrevX) < 1) && (abs(pos.pos[1] - mPrevY) < 1);
switch(mWalkState)
{
case State_Norm:
{
if(!samePosition)
break;
else
mWalkState = State_CheckStuck;
}
/* FALL THROUGH */
case State_CheckStuck:
{
if(!samePosition)
{
mWalkState = State_Norm;
// to do this properly need yet another variable, simply don't clear for now
//mStuckCount = 0;
break;
}
else
{
// consider stuck only if position unchanges consecutively
if((mStuckCount++ % COUNT_BEFORE_STUCK) == 0)
mWalkState = State_Evade;
// NOTE: mStuckCount is purposely not cleared here
else
break; // still in the same state, but counter got incremented
}
}
/* FALL THROUGH */
case State_Evade:
{
if(mEvadeCount++ < COUNT_EVADE)
break;
else
{
mWalkState = State_Norm; // tried to evade, assume all is ok and start again
// NOTE: mStuckCount is purposely not cleared here
mEvadeCount = 0;
}
}
/* NO DEFAULT CASE */
}
if(mWalkState == State_Evade)
{
//std::cout << "Stuck \""<<actor.getClass().getName(actor)<<"\"" << std::endl;
// diagonal should have same animation as walk forward
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.01f;
// change the angle a bit, too
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
}
else
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
}
if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
{
//std::cout << "Reset \""<<actor.getClass().getName(actor)<<"\"" << std::endl;
mWalkState = State_Norm;
mStuckCount = 0;
stopWalking(actor);
mMoveNow = false;
mWalking = false;
mChooseAction = true;
}
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; // update position
ESM::Position updatedPos = actor.getRefData().getPosition();
mPrevX = updatedPos.pos[0];
mPrevY = updatedPos.pos[1];
} }
} }

@ -43,6 +43,21 @@ namespace MWMechanics
float mXCell; float mXCell;
float mYCell; float mYCell;
// for checking if we're stuck (but don't check Z axis)
float mPrevX;
float mPrevY;
enum WalkState
{
State_Norm,
State_CheckStuck,
State_Evade
};
WalkState mWalkState;
int mStuckCount;
int mEvadeCount;
bool mStoredAvailableNodes; bool mStoredAvailableNodes;
bool mChooseAction; bool mChooseAction;
bool mIdleNow; bool mIdleNow;

@ -313,6 +313,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
mAnimation->disable(mCurrentMovement); mAnimation->disable(mCurrentMovement);
mCurrentMovement = movement; mCurrentMovement = movement;
mMovementAnimVelocity = 0.0f;
if(!mCurrentMovement.empty()) if(!mCurrentMovement.empty())
{ {
float vel, speedmult = 1.0f; float vel, speedmult = 1.0f;
@ -320,7 +321,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f)
{
mMovementAnimVelocity = vel;
speedmult = mMovementSpeed / vel; speedmult = mMovementSpeed / vel;
}
else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
else if (mMovementSpeed > 0.0f) else if (mMovementSpeed > 0.0f)
@ -330,10 +334,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f); speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
mMovementAnimVelocity = vel;
} }
else mMovementAnimVelocity = 0.0f;
} }
} }
@ -1194,7 +1195,10 @@ void CharacterController::update(float duration)
: (sneak ? CharState_SneakBack : (sneak ? CharState_SneakBack
: (isrunning ? CharState_RunBack : CharState_WalkBack))); : (isrunning ? CharState_RunBack : CharState_WalkBack)));
} }
else if(rot.z != 0.0f && !inwater && !sneak) // Don't play turning animations during attack. It would break positioning of the arrow bone when releasing a shot.
// Actually, in vanilla the turning animation is not even played when merely having equipped the weapon,
// but I don't think we need to go as far as that.
else if(rot.z != 0.0f && !inwater && !sneak && mUpperBodyState < UpperCharState_StartToMinAttack)
{ {
if(rot.z > 0.0f) if(rot.z > 0.0f)
movestate = CharState_TurnRight; movestate = CharState_TurnRight;

@ -4,9 +4,12 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -17,14 +20,30 @@
namespace namespace
{ {
Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 n) Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 normal)
{ {
return Ogre::Math::ATan2( return Ogre::Math::ATan2(
n.dotProduct( v1.crossProduct(v2) ), normal.dotProduct( v1.crossProduct(v2) ),
v1.dotProduct(v2) v1.dotProduct(v2)
); );
} }
void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition)
{
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
if (!enchantmentName.empty())
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
enchantmentName);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{
MWMechanics::CastSpell cast(attacker, victim);
cast.mHitPosition = hitPosition;
cast.cast(object);
}
}
}
} }
namespace MWMechanics namespace MWMechanics
@ -135,4 +154,94 @@ namespace MWMechanics
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}");
} }
void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile,
const Ogre::Vector3& hitPosition)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
const MWWorld::Class &othercls = victim.getClass();
if(!othercls.isActor()) // Can't hit non-actors
return;
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
if(otherstats.isDead()) // Can't hit dead actors
return;
if(attacker.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
int weapskill = ESM::Skill::Marksman;
if(!weapon.isEmpty())
weapskill = weapon.getClass().getEquipmentSkill(weapon);
float skillValue = attacker.getClass().getSkill(attacker,
weapon.getClass().getEquipmentSkill(weapon));
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
return;
}
float damage = 0.0f;
float fDamageStrengthBase = gmst.find("fDamageStrengthBase")->getFloat();
float fDamageStrengthMult = gmst.find("fDamageStrengthMult")->getFloat();
const unsigned char* attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
damage = attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength()); // Bow/crossbow damage
if (weapon != projectile)
{
// Arrow/bolt damage
attack = projectile.get<ESM::Weapon>()->mBase->mData.mChop;
damage += attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength());
}
damage *= fDamageStrengthBase +
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
if(attacker.getRefData().getHandle() == "player")
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);
if(!detected)
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
if (victim.getClass().getCreatureStats(victim).getKnockedDown())
damage *= gmst.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" effect of the weapon
applyEnchantment(attacker, victim, weapon, hitPosition);
if (weapon != projectile)
applyEnchantment(attacker, victim, projectile, hitPosition);
if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat();
if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f)
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
victim.getClass().onHit(victim, damage, true, projectile, attacker, true);
}
float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue)
{
MWMechanics::CreatureStats &stats = attacker.getClass().getCreatureStats(attacker);
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = skillValue +
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= stats.getFatigueTerm();
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
hitchance -= victim.getClass().getCreatureStats(victim).getEvasion();
return hitchance;
}
} }

@ -2,6 +2,7 @@
#define OPENMW_MECHANICS_COMBAT_H #define OPENMW_MECHANICS_COMBAT_H
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include <OgreVector3.h>
namespace MWMechanics namespace MWMechanics
{ {
@ -11,6 +12,13 @@ bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker
void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage); void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);
/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt
void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
const Ogre::Vector3& hitPosition);
/// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value
float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue);
} }
#endif #endif

@ -6,6 +6,8 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <iomanip>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
@ -509,4 +511,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
mTimeToStartDrowning = state.mTimeToStartDrowning; mTimeToStartDrowning = state.mTimeToStartDrowning;
mLastDrowningHit = state.mLastDrowningHit; mLastDrowningHit = state.mLastDrowningHit;
mLevelHealthBonus = state.mLevelHealthBonus; mLevelHealthBonus = state.mLevelHealthBonus;
} }

@ -393,6 +393,8 @@ namespace MWMechanics
void PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path) void PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path)
{ {
if (mPath.size() < 2)
return; //nothing to pop
std::list<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin(); std::list<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin();
std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin(); std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();

@ -587,7 +587,7 @@ namespace MWMechanics
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
} }
MWBase::Environment::get().getWorld()->launchProjectile(mId, false, enchantment->mEffects, mCaster, mSourceName); MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName);
return true; return true;
} }
@ -666,7 +666,7 @@ namespace MWMechanics
} }
} }
MWBase::Environment::get().getWorld()->launchProjectile(mId, false, spell->mEffects, mCaster, mSourceName); MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName);
return true; return true;
} }

@ -10,6 +10,8 @@
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreControllerManager.h> #include <OgreControllerManager.h>
#include <OgreStaticGeometry.h> #include <OgreStaticGeometry.h>
#include <OgreSceneNode.h>
#include <OgreTechnique.h>
#include <components/esm/loadligh.hpp> #include <components/esm/loadligh.hpp>
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
@ -293,6 +295,17 @@ void Animation::addAnimSource(const std::string &model)
} }
} }
if (grp == 0 && dstval->getNode()->getName() == "Bip01")
{
mNonAccumRoot = dstval->getNode();
mAccumRoot = mNonAccumRoot->getParent();
if(!mAccumRoot)
{
std::cerr<< "Non-Accum root for "<<mPtr.getCellRef().mRefID<<" is skeleton root??" <<std::endl;
mNonAccumRoot = NULL;
}
}
ctrls[i].setSource(mAnimationTimePtr[grp]); ctrls[i].setSource(mAnimationTimePtr[grp]);
grpctrls[grp].push_back(ctrls[i]); grpctrls[grp].push_back(ctrls[i]);
} }

@ -70,7 +70,7 @@ namespace MWRender
if (!mVanity.enabled && !mPreviewMode) { if (!mVanity.enabled && !mPreviewMode) {
mCamera->getParentNode()->setOrientation(xr); mCamera->getParentNode()->setOrientation(xr);
} else { } else {
Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z);
mCamera->getParentNode()->setOrientation(zr * xr); mCamera->getParentNode()->setOrientation(zr * xr);
} }
} }

@ -1,10 +1,13 @@
#include "characterpreview.hpp" #include "characterpreview.hpp"
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreHardwarePixelBuffer.h> #include <OgreHardwarePixelBuffer.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreSceneNode.h>
#include <OgreTextureManager.h>
#include <OgreViewport.h>
#include <OgreRenderTexture.h>
#include <libs/openengine/ogre/selectionbuffer.hpp> #include <libs/openengine/ogre/selectionbuffer.hpp>

@ -3,6 +3,7 @@
#include <OgreRenderTarget.h> #include <OgreRenderTarget.h>
#include <OgreMaterialManager.h> #include <OgreMaterialManager.h>
#include <OgreVector3.h>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>

@ -55,6 +55,8 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr)
updateParts(); updateParts();
} }
mWeaponAnimationTime = Ogre::SharedPtr<WeaponAnimationTime>(new WeaponAnimationTime(this));
} }
void CreatureWeaponAnimation::showWeapons(bool showWeapon) void CreatureWeaponAnimation::showWeapons(bool showWeapon)
@ -110,6 +112,20 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0, setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0,
!item.getClass().getEnchantment(item).empty(), &glowColor); !item.getClass().getEnchantment(item).empty(), &glowColor);
// Crossbows start out with a bolt attached
if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&
item.getTypeName() == typeid(ESM::Weapon).name() &&
item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
attachArrow();
else
mAmmunition.setNull();
}
else
mAmmunition.setNull();
if(scene->mSkelBase) if(scene->mSkelBase)
{ {
Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton(); Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton();
@ -133,15 +149,42 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
updateSkeletonInstance(mSkelBase->getSkeleton(), skel); updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
} }
// TODO:
// type == ESM::PRT_Weapon should get an animation source based on the current offset
// of the weapon attack animation (from its beginning, or start marker?)
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin()); std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin());
for(;ctrl != scene->mControllers.end();ctrl++) for(;ctrl != scene->mControllers.end();ctrl++)
{ {
if(ctrl->getSource().isNull()) if(ctrl->getSource().isNull())
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime())); {
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
ctrl->setSource(mWeaponAnimationTime);
else
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
}
} }
} }
void CreatureWeaponAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
{
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
setRenderProperties(object, RV_Actors, RQG_Main, RQG_Alpha, 0,
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
}
void CreatureWeaponAnimation::attachArrow()
{
WeaponAnimation::attachArrow(mPtr);
}
void CreatureWeaponAnimation::releaseArrow()
{
WeaponAnimation::releaseArrow(mPtr);
}
Ogre::Vector3 CreatureWeaponAnimation::runAnimation(float duration)
{
Ogre::Vector3 ret = Animation::runAnimation(duration);
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton());
return ret;
}
} }

@ -2,6 +2,7 @@
#define GAME_RENDER_CREATUREANIMATION_H #define GAME_RENDER_CREATUREANIMATION_H
#include "animation.hpp" #include "animation.hpp"
#include "weaponanimation.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
namespace MWWorld namespace MWWorld
@ -21,7 +22,7 @@ namespace MWRender
// For creatures with weapons and shields // For creatures with weapons and shields
// Animation is already virtual anyway, so might as well make a separate class. // Animation is already virtual anyway, so might as well make a separate class.
// Most creatures don't need weapons/shields, so this will save some memory. // Most creatures don't need weapons/shields, so this will save some memory.
class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
{ {
public: public:
CreatureWeaponAnimation(const MWWorld::Ptr& ptr); CreatureWeaponAnimation(const MWWorld::Ptr& ptr);
@ -36,11 +37,29 @@ namespace MWRender
void updatePart(NifOgre::ObjectScenePtr& scene, int slot); void updatePart(NifOgre::ObjectScenePtr& scene, int slot);
virtual void attachArrow();
virtual void releaseArrow();
virtual Ogre::Vector3 runAnimation(float duration);
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
/// to indicate the facing orientation of the character.
virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
// WeaponAnimation
virtual NifOgre::ObjectScenePtr getWeapon() { return mWeapon; }
virtual void showWeapon(bool show) { showWeapons(show); }
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
private: private:
NifOgre::ObjectScenePtr mWeapon; NifOgre::ObjectScenePtr mWeapon;
NifOgre::ObjectScenePtr mShield; NifOgre::ObjectScenePtr mShield;
bool mShowWeapons; bool mShowWeapons;
bool mShowCarriedLeft; bool mShowCarriedLeft;
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
}; };
} }

@ -7,6 +7,8 @@
#include <OgreMaterial.h> #include <OgreMaterial.h>
#include <OgreMaterialManager.h> #include <OgreMaterialManager.h>
#include <OgreManualObject.h> #include <OgreManualObject.h>
#include <OgreTechnique.h>
#include <OgreSceneNode.h>
#include <openengine/bullet/physic.hpp> #include <openengine/bullet/physic.hpp>

@ -2,6 +2,8 @@
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreParticleSystem.h> #include <OgreParticleSystem.h>
#include <OgreSceneNode.h>
#include <OgreTechnique.h>
#include "animation.hpp" #include "animation.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"

@ -75,10 +75,9 @@ namespace MWRender
if (land) if (land)
{ {
if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
{ if (!land->isDataLoaded(mask))
land->loadData(ESM::Land::DATA_VHGT); land->loadData(mask);
}
} }
for (int cellY=0; cellY<cellSize; ++cellY) for (int cellY=0; cellY<cellSize; ++cellY)

@ -6,6 +6,8 @@
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreTextureManager.h> #include <OgreTextureManager.h>
#include <OgreRenderTexture.h>
#include <OgreViewport.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

@ -4,6 +4,11 @@
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreParticleSystem.h> #include <OgreParticleSystem.h>
#include <OgreSubEntity.h> #include <OgreSubEntity.h>
#include <OgreSkeleton.h>
#include <OgreSkeletonInstance.h>
#include <OgreSceneNode.h>
#include <OgreBone.h>
#include <OgreTechnique.h>
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>
@ -71,27 +76,6 @@ float HeadAnimationTime::getValue() const
return 1; return 1;
} }
float WeaponAnimationTime::getValue() const
{
if (mWeaponGroup.empty())
return 0;
float current = mAnimation->getCurrentTime(mWeaponGroup);
if (current == -1)
return 0;
return current - mStartTime;
}
void WeaponAnimationTime::setGroup(const std::string &group)
{
mWeaponGroup = group;
mStartTime = mAnimation->getStartTime(mWeaponGroup);
}
void WeaponAnimationTime::updateStartTime()
{
setGroup(mWeaponGroup);
}
static NpcAnimation::PartBoneMap createPartListMap() static NpcAnimation::PartBoneMap createPartListMap()
{ {
NpcAnimation::PartBoneMap result; NpcAnimation::PartBoneMap result;
@ -142,8 +126,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
mShowCarriedLeft(true), mShowCarriedLeft(true),
mFirstPersonOffset(0.f, 0.f, 0.f), mFirstPersonOffset(0.f, 0.f, 0.f),
mAlpha(1.f), mAlpha(1.f),
mNpcType(Type_Normal), mNpcType(Type_Normal)
mPitchFactor(0)
{ {
mNpc = mPtr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
@ -527,20 +510,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
{ {
float pitch = mPtr.getRefData().getPosition().rot[0]; float pitch = mPtr.getRefData().getPosition().rot[0];
Ogre::Node *node = baseinst->getBone("Bip01 Neck"); Ogre::Node *node = baseinst->getBone("Bip01 Neck");
node->pitch(Ogre::Radian(pitch), Ogre::Node::TS_WORLD); node->pitch(Ogre::Radian(-pitch), Ogre::Node::TS_WORLD);
// This has to be done before this function ends; // This has to be done before this function ends;
// updateSkeletonInstance, below, touches the hands. // updateSkeletonInstance, below, touches the hands.
node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD); node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD);
} }
else if (mPitchFactor > 0) else
{ {
// In third person mode we may still need pitch for ranged weapon targeting // In third person mode we may still need pitch for ranged weapon targeting
float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
Ogre::Node *node = baseinst->getBone("Bip01 Spine2");
node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD);
node = baseinst->getBone("Bip01 Spine1");
node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD);
} }
mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame.
@ -690,13 +669,14 @@ void NpcAnimation::showWeapons(bool showWeapon)
{ {
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end()) // special case for weapons if(weapon != inv.end())
{ {
Ogre::Vector3 glowColor = getEnchantmentColor(*weapon); Ogre::Vector3 glowColor = getEnchantmentColor(*weapon);
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon); std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
// Crossbows start out with a bolt attached
if (weapon->getTypeName() == typeid(ESM::Weapon).name() && if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{ {
@ -738,50 +718,24 @@ void NpcAnimation::showCarriedLeft(bool show)
removeIndividualPart(ESM::PRT_Shield); removeIndividualPart(ESM::PRT_Shield);
} }
void NpcAnimation::attachArrow() void NpcAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
{ {
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); setRenderProperties(object, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
if (weaponSlot != inv.end() && weaponSlot->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) !ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
showWeapons(true);
else
{
NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon];
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo == inv.end())
return;
std::string model = ammo->getClass().getModel(*ammo);
mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", mInsert, model); std::for_each(object->mEntities.begin(), object->mEntities.end(), SetObjectGroup(slot));
Ogre::Vector3 glowColor = getEnchantmentColor(*ammo); std::for_each(object->mParticles.begin(), object->mParticles.end(), SetObjectGroup(slot));
setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, }
!ammo->getClass().getEnchantment(*ammo).empty(), &glowColor);
std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); void NpcAnimation::attachArrow()
std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); {
} WeaponAnimation::attachArrow(mPtr);
} }
void NpcAnimation::releaseArrow() void NpcAnimation::releaseArrow()
{ {
// Thrown weapons get detached now WeaponAnimation::releaseArrow(mPtr);
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weapon != inv.end() && weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
{
showWeapons(false);
inv.remove(*weapon, 1, mPtr);
}
else
{
// With bows and crossbows only the used arrow/bolt gets detached
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo == inv.end())
return;
inv.remove(*ammo, 1, mPtr);
mAmmunition.setNull();
}
} }
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)

@ -5,6 +5,8 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "weaponanimation.hpp"
namespace ESM namespace ESM
{ {
struct NPC; struct NPC;
@ -25,24 +27,7 @@ public:
{ } { }
}; };
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real> class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
{
private:
Animation* mAnimation;
std::string mWeaponGroup;
float mStartTime;
public:
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
void setGroup(const std::string& group);
void updateStartTime();
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value)
{ }
};
class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener
{ {
public: public:
virtual void equipmentChanged() { updateParts(); } virtual void equipmentChanged() { updateParts(); }
@ -91,7 +76,6 @@ private:
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime; Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
float mAlpha; float mAlpha;
float mPitchFactor;
void updateNpcBase(); void updateNpcBase();
@ -138,7 +122,10 @@ public:
virtual void attachArrow(); virtual void attachArrow();
virtual void releaseArrow(); virtual void releaseArrow();
NifOgre::ObjectScenePtr mAmmunition; // WeaponAnimation
virtual NifOgre::ObjectScenePtr getWeapon() { return mObjectParts[ESM::PRT_Weapon]; }
virtual void showWeapon(bool show) { showWeapons(show); }
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
void setViewMode(ViewMode viewMode); void setViewMode(ViewMode viewMode);

@ -109,6 +109,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh)
{ {
uniqueID = uniqueID+1; uniqueID = uniqueID+1;
sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID));
sg->setOrigin(ptr.getRefData().getBaseNode()->getPosition());
mStaticGeometrySmall[ptr.getCell()] = sg; mStaticGeometrySmall[ptr.getCell()] = sg;
sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance"));
@ -122,6 +123,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh)
{ {
uniqueID = uniqueID+1; uniqueID = uniqueID+1;
sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID));
sg->setOrigin(ptr.getRefData().getBaseNode()->getPosition());
mStaticGeometry[ptr.getCell()] = sg; mStaticGeometry[ptr.getCell()] = sg;
} }
else else

@ -9,6 +9,8 @@
#include <OgreMeshManager.h> #include <OgreMeshManager.h>
#include <OgreMaterialManager.h> #include <OgreMaterialManager.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreSceneNode.h>
#include <OgreMesh.h>
#include "renderconst.hpp" #include "renderconst.hpp"

@ -7,6 +7,8 @@
#include <OgreRenderTarget.h> #include <OgreRenderTarget.h>
#include <OgreViewport.h> #include <OgreViewport.h>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderTexture.h>
#include <OgreSceneNode.h>
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>

@ -5,12 +5,14 @@
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <OgreViewport.h> #include <OgreViewport.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreTextureManager.h> #include <OgreTextureManager.h>
#include <OgreHardwarePixelBuffer.h> #include <OgreHardwarePixelBuffer.h>
#include <OgreControllerManager.h> #include <OgreControllerManager.h>
#include <OgreMeshManager.h> #include <OgreMeshManager.h>
#include <OgreRenderTexture.h>
#include <SDL_video.h> #include <SDL_video.h>
@ -278,13 +280,12 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr)
if(ptr.getRefData().getHandle() == mCamera->getHandle() && if(ptr.getRefData().getHandle() == mCamera->getHandle() &&
!mCamera->isVanityOrPreviewModeEnabled()) !mCamera->isVanityOrPreviewModeEnabled())
mCamera->rotateCamera(rot, false); mCamera->rotateCamera(-rot, false);
Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(rot.z), Ogre::Vector3::NEGATIVE_UNIT_Z);
if(!MWWorld::Class::get(ptr).isActor()) if(!MWWorld::Class::get(ptr).isActor())
newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * newo = Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::NEGATIVE_UNIT_X) *
Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo; Ogre::Quaternion(Ogre::Radian(rot.y), Ogre::Vector3::NEGATIVE_UNIT_Y) * newo;
ptr.getRefData().getBaseNode()->setOrientation(newo); ptr.getRefData().getBaseNode()->setOrientation(newo);
} }
@ -649,6 +650,18 @@ void RenderingManager::setGlare(bool glare)
mSkyManager->setGlare(glare); mSkyManager->setGlare(glare);
} }
void RenderingManager::updateTerrain()
{
if (mTerrain)
{
// Avoid updating with dims.getCenter for each cell. Player position should be good enough
mTerrain->update(mRendering.getCamera()->getRealPosition());
mTerrain->syncLoad();
// need to update again so the chunks that were just loaded can be made visible
mTerrain->update(mRendering.getCamera()->getRealPosition());
}
}
void RenderingManager::requestMap(MWWorld::CellStore* cell) void RenderingManager::requestMap(MWWorld::CellStore* cell)
{ {
if (cell->getCell()->isExterior()) if (cell->getCell()->isExterior())
@ -659,9 +672,6 @@ void RenderingManager::requestMap(MWWorld::CellStore* cell)
Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5); Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5);
dims.merge(mTerrain->getWorldBoundingBox(center)); dims.merge(mTerrain->getWorldBoundingBox(center));
if (dims.isFinite())
mTerrain->update(dims.getCenter());
mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z); mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z);
} }
else else
@ -981,13 +991,11 @@ void RenderingManager::screenshot(Image &image, int w, int h)
Ogre::PixelFormat pf = rt->suggestPixelFormat(); Ogre::PixelFormat pf = rt->suggestPixelFormat();
std::vector<Ogre::uchar> data; image.loadDynamicImage(
data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf)); OGRE_ALLOC_T(Ogre::uchar, w * h * Ogre::PixelUtil::getNumElemBytes(pf), Ogre::MEMCATEGORY_GENERAL),
w, h, 1, pf, true // autoDelete=true, frees memory we allocate
Ogre::PixelBox pb(w, h, 1, pf, &data[0]); );
rt->copyContentsToMemory(pb); rt->copyContentsToMemory(image.getPixelBox()); // getPixelBox returns a box sharing the same memory as the image
image.loadDynamicImage(&data[0], w, h, pf);
Ogre::TextureManager::getSingleton().remove(tempName); Ogre::TextureManager::getSingleton().remove(tempName);
mRendering.getCamera()->setAspectRatio(oldAspect); mRendering.getCamera()->setAspectRatio(oldAspect);
@ -1045,20 +1053,16 @@ void RenderingManager::enableTerrain(bool enable)
{ {
if (!mTerrain) if (!mTerrain)
{ {
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); mTerrain = new Terrain::World(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
Loading::ScopedLoad load(listener);
mTerrain = new Terrain::World(listener, mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
Settings::Manager::getBool("distant land", "Terrain"), Settings::Manager::getBool("distant land", "Terrain"),
Settings::Manager::getBool("shader", "Terrain")); Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
Settings::Manager::getBool("split", "Shadows")); Settings::Manager::getBool("split", "Shadows"));
mTerrain->update(mRendering.getCamera()->getRealPosition()); mTerrain->update(mRendering.getCamera()->getRealPosition());
mTerrain->setLoadingListener(NULL);
} }
mTerrain->setVisible(true); mTerrain->setVisible(true);
} }
else else if (mTerrain)
if (mTerrain)
mTerrain->setVisible(false); mTerrain->setVisible(false);
} }
@ -1069,7 +1073,7 @@ float RenderingManager::getCameraDistance() const
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale)
{ {
mEffectManager->addEffect(model, "", worldPosition, scale); mEffectManager->addEffect(model, texture, worldPosition, scale);
} }
} // namespace } // namespace

@ -180,6 +180,10 @@ public:
void removeWaterRippleEmitter (const MWWorld::Ptr& ptr); void removeWaterRippleEmitter (const MWWorld::Ptr& ptr);
void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
void updateTerrain ();
///< update the terrain according to the player position. Usually done automatically, but should be done manually
/// before calling requestMap
void requestMap (MWWorld::CellStore* cell); void requestMap (MWWorld::CellStore* cell);
///< request the local map for a cell ///< request the local map for a cell

@ -4,6 +4,10 @@
#include <OgreStringConverter.h> #include <OgreStringConverter.h>
#include <OgreHardwarePixelBuffer.h> #include <OgreHardwarePixelBuffer.h>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRectangle2D.h>
#include <OgreSceneNode.h>
#include <OgreRenderTexture.h>
#include <OgreViewport.h>
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>

@ -9,6 +9,8 @@
#include <OgreShadowCameraSetupPSSM.h> #include <OgreShadowCameraSetupPSSM.h>
#include <OgreHardwarePixelBuffer.h> #include <OgreHardwarePixelBuffer.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreRenderTexture.h>
#include <OgreViewport.h>
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>

@ -11,6 +11,7 @@
#include <OgreBillboardSet.h> #include <OgreBillboardSet.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreSubEntity.h> #include <OgreSubEntity.h>
#include <OgreTechnique.h>
#include <OgreMeshManager.h> #include <OgreMeshManager.h>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save