diff --git a/.travis.yml b/.travis.yml index 39a02de63..5d0326a07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ branches: - /openmw-.*$/ before_install: - 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 apt-add-repository ppa:openmw/openmw - sudo apt-get update -qq diff --git a/CMakeLists.txt b/CMakeLists.txt index a9d1f2017..954e161a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,34 +6,63 @@ if (APPLE) set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") endif (APPLE) -# Macros - set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -include(OpenMWMacros) - # Version +message(STATUS "Configuring OpenMW...") -include(GetGitRevisionDescription) +set(OPENMW_VERSION_MAJOR 0) +set(OPENMW_VERSION_MINOR 29) +set(OPENMW_VERSION_RELEASE 0) -get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...") -get_git_head_revision(REFSPEC COMMITHASH) -git_describe(VERSION --tags ${TAGHASH}) +set(OPENMW_VERSION_COMMITHASH "") +set(OPENMW_VERSION_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 "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") - set(OPENMW_VERSION_TAGHASH "${TAGHASH}") +if(EXISTS ${PROJECT_SOURCE_DIR}/.git) + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + find_package(Git) - message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") -else (MATCH) - message(FATAL_ERROR "Failed to get valid version information from Git") -endif (MATCH) + 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) + +# Macros +include(OpenMWMacros) # doxygen main page @@ -209,6 +238,14 @@ if (UNIX AND NOT APPLE) find_package (Threads) 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) check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP) if (HAVE_UNORDERED_MAP) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 56b3186ff..5cf8f8a89 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -76,17 +76,20 @@ Launcher::MainDialog::MainDialog(QWidget *parent) QString revision(OPENMW_VERSION_COMMITHASH); QString tag(OPENMW_VERSION_TAGHASH); - if (revision == tag) { - versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); - } else { - versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); - } + if (!revision.isEmpty() && !tag.isEmpty()) + { + if (revision == tag) { + 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 - versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), - QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), - QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), - QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + // Add the compile date and time + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), + QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), + QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), + QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + } createIcons(); } diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a7b39feb8..5034d9753 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -64,8 +64,12 @@ opencs_units (view/world ) opencs_units (view/render - scenewidget - ) + scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget + ) + +opencs_units_noqt (view/render + navigation navigation1st navigationfree navigationorbit + ) opencs_units_noqt (view/world subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 5d5ac4c55..2b2f41754 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -9,15 +9,22 @@ #include #include +#include + #include "model/doc/document.hpp" #include "model/world/data.hpp" -CS::Editor::Editor() - : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) +CS::Editor::Editor (OgreInit::OgreInit& ogreInit) +: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), + mIpcServerName ("org.openmw.OpenCS") { - mIpcServerName = "org.openmw.OpenCS"; + Files::PathContainer dataDirs = readConfig(); - setupDataFiles(); + setupDataFiles (dataDirs); + + CSMSettings::UserSettings::instance().loadSettings ("opencs.cfg"); + + ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string()); mNewGame.setLocalData (mLocal); mFileDialog.setLocalData (mLocal); @@ -42,7 +49,16 @@ CS::Editor::Editor() 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::options_description desc("Syntax: opencs \nAllowed options"); @@ -58,6 +74,8 @@ void CS::Editor::setupDataFiles() mCfgMgr.readConfiguration(variables, desc); + mDocumentManager.setResourceDir (variables["resources"].as()); + Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); @@ -83,23 +101,11 @@ void CS::Editor::setupDataFiles() messageBox.exec(); QApplication::exit (1); - return; } dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); - mDocumentManager.setResourceDir (variables["resources"].as()); - - 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); + return dataDirs; } void CS::Editor::createGame() @@ -210,8 +216,6 @@ int CS::Editor::run() if (mLocal.empty()) return 1; -// temporarily disable OGRE-integration (need to fix path problem first) -#if 0 // TODO: setting Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); @@ -228,7 +232,6 @@ int CS::Editor::run() #endif Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); -#endif mStartup.show(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 930aa9d64..0f1c7a682 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -10,6 +10,8 @@ #include #endif +#include + #include "model/settings/usersettings.hpp" #include "model/doc/documentmanager.hpp" @@ -20,6 +22,11 @@ #include "view/settings/usersettingsdialog.hpp" +namespace OgreInit +{ + class OgreInit; +} + namespace CS { class Editor : public QObject @@ -37,7 +44,10 @@ namespace CS boost::filesystem::path mLocal; - void setupDataFiles(); + void setupDataFiles (const Files::PathContainer& dataDirs); + + Files::PathContainer readConfig(); + ///< \return data paths // not implemented Editor (const Editor&); @@ -45,7 +55,7 @@ namespace CS public: - Editor(); + Editor (OgreInit::OgreInit& ogreInit); bool makeIPCServer(); void connectToIPCServer(); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 931d63312..212ed0836 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -40,15 +40,9 @@ int main(int argc, char *argv[]) { 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.init("./opencsOgre.log"); // TODO log path? -#endif + + Application application (argc, argv); #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); @@ -66,12 +60,12 @@ int main(int argc, char *argv[]) QStringList libraryPaths; libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); - mApplication.setLibraryPaths(libraryPaths); + application.setLibraryPaths(libraryPaths); #endif - mApplication.setWindowIcon (QIcon (":./opencs.png")); + application.setWindowIcon (QIcon (":./opencs.png")); - CS::Editor editor; + CS::Editor editor (ogreInit); if(!editor.makeIPCServer()) { diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index d342e88a4..9d97e36c7 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -98,7 +98,7 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None); ///< \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 UniversalId::Type type); @@ -198,7 +198,7 @@ namespace CSMWorld } template - void Collection::cloneRecord(const std::string& origin, + void Collection::cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) { diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 408a89d92..442055d5f 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -74,7 +74,7 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None) = 0; ///< 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 UniversalId::Type type) = 0; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 8d53c4e53..d68b79ff0 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -247,12 +247,12 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); - addModel (new IdTable (&mTopicInfos), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); - addModel (new IdTable (&mJournalInfos), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); - addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); + addModel (new IdTable (&mTopicInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); + addModel (new IdTable (&mJournalInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); + addModel (new IdTable (&mCells, IdTable::Reordering_None, IdTable::Viewing_Id), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, 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); } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index bea307aa2..453a7da6a 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -4,8 +4,9 @@ #include "collectionbase.hpp" #include "columnbase.hpp" -CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering) -: mIdCollection (idCollection), mReordering (reordering) +CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering, + Viewing viewing) +: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing) {} CSMWorld::IdTable::~IdTable() @@ -188,4 +189,45 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newO CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const { return mReordering; +} + +CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const +{ + return mViewing; +} + +std::pair 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); } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 74923867d..5a271de44 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -25,10 +25,20 @@ namespace CSMWorld 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: CollectionBase *mIdCollection; Reordering mReordering; + Viewing mViewing; // not implemented IdTable (const IdTable&); @@ -36,7 +46,8 @@ namespace CSMWorld 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. virtual ~IdTable(); @@ -63,8 +74,8 @@ namespace CSMWorld void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - void cloneRecord(const std::string& origin, - const std::string& destination, + void cloneRecord(const std::string& origin, + const std::string& destination, UniversalId::Type type = UniversalId::Type_None); 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). Reordering getReordering() const; + + Viewing getViewing() const; + + std::pair 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, ""). }; } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 8301ebfd3..1d1b3c960 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -90,14 +90,14 @@ namespace { 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_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 }; static const TypeData sIndexArg[] = { { 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 }; } diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 09361a1c0..6160f2673 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -16,4 +16,6 @@ void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QSt { } -void CSVDoc::SubView::setStatusBar (bool show) {} \ No newline at end of file +void CSVDoc::SubView::setStatusBar (bool show) {} + +void CSVDoc::SubView::useHint (const std::string& hint) {} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index aa073f81d..59781f869 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -40,9 +40,12 @@ namespace CSVDoc virtual void setStatusBar (bool show); ///< Default implementation: ignored + virtual void useHint (const std::string& hint); + ///< Default implementation: ignored + signals: - void focusId (const CSMWorld::UniversalId& universalId); + void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 533ca7f57..de3f476af 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -115,10 +115,6 @@ void CSVDoc::View::setupWorldMenu() 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); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); 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); } -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 /// number is exceeded @@ -322,12 +318,15 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); + if (!hint.empty()) + view->useHint (hint); + view->setStatusBar (mShowStatusBar->isChecked()); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); - connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, - SLOT (addSubView (const CSMWorld::UniversalId&))); + connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this, + SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&))); CSMSettings::UserSettings::instance().updateSettings("Display Format"); @@ -429,11 +428,6 @@ void CSVDoc::View::addFiltersSubView() addSubView (CSMWorld::UniversalId::Type_Filters); } -void CSVDoc::View::addSceneSubView() -{ - addSubView (CSMWorld::UniversalId::Type_Scene); -} - void CSVDoc::View::addTopicsSubView() { addSubView (CSMWorld::UniversalId::Type_Topics); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 13c15ec9b..ee7380e2b 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -120,7 +120,9 @@ namespace CSVDoc 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); @@ -166,8 +168,6 @@ namespace CSVDoc void addFiltersSubView(); - void addSceneSubView(); - void addTopicsSubView(); void addJournalsSubView(); diff --git a/apps/opencs/view/render/navigation.cpp b/apps/opencs/view/render/navigation.cpp new file mode 100644 index 000000000..14ae7f0b7 --- /dev/null +++ b/apps/opencs/view/render/navigation.cpp @@ -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; +} \ No newline at end of file diff --git a/apps/opencs/view/render/navigation.hpp b/apps/opencs/view/render/navigation.hpp new file mode 100644 index 000000000..873519609 --- /dev/null +++ b/apps/opencs/view/render/navigation.hpp @@ -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 diff --git a/apps/opencs/view/render/navigation1st.cpp b/apps/opencs/view/render/navigation1st.cpp new file mode 100644 index 000000000..b892d3e3e --- /dev/null +++ b/apps/opencs/view/render/navigation1st.cpp @@ -0,0 +1,85 @@ + +#include "navigation1st.hpp" + +#include + +#include + +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)) + 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; +} diff --git a/apps/opencs/view/render/navigation1st.hpp b/apps/opencs/view/render/navigation1st.hpp new file mode 100644 index 000000000..d1e09d236 --- /dev/null +++ b/apps/opencs/view/render/navigation1st.hpp @@ -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 diff --git a/apps/opencs/view/render/navigationfree.cpp b/apps/opencs/view/render/navigationfree.cpp new file mode 100644 index 000000000..950e137a7 --- /dev/null +++ b/apps/opencs/view/render/navigationfree.cpp @@ -0,0 +1,66 @@ + +#include "navigationfree.hpp" + +#include + +#include + +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; +} diff --git a/apps/opencs/view/render/navigationfree.hpp b/apps/opencs/view/render/navigationfree.hpp new file mode 100644 index 000000000..e30722f75 --- /dev/null +++ b/apps/opencs/view/render/navigationfree.hpp @@ -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 diff --git a/apps/opencs/view/render/navigationorbit.cpp b/apps/opencs/view/render/navigationorbit.cpp new file mode 100644 index 000000000..c6e729e96 --- /dev/null +++ b/apps/opencs/view/render/navigationorbit.cpp @@ -0,0 +1,100 @@ + +#include "navigationorbit.hpp" + +#include + +#include + +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()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; +} \ No newline at end of file diff --git a/apps/opencs/view/render/navigationorbit.hpp b/apps/opencs/view/render/navigationorbit.hpp new file mode 100644 index 000000000..7796eb9e7 --- /dev/null +++ b/apps/opencs/view/render/navigationorbit.hpp @@ -0,0 +1,42 @@ +#ifndef OPENCS_VIEW_NAVIGATIONORBIT_H +#define OPENCS_VIEW_NAVIGATIONORBIT_H + +#include "navigation.hpp" + +#include + +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 diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp new file mode 100644 index 000000000..fa32e3959 --- /dev/null +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -0,0 +1,6 @@ + +#include "pagedworldspacewidget.hpp" + +CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget *parent) +: WorldspaceWidget (parent) +{} \ No newline at end of file diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp new file mode 100644 index 000000000..172e2477a --- /dev/null +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -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 diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6e327bb6e..31d5d0318 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -2,24 +2,34 @@ #include #include +#include #include #include #include #include +#include +#include + +#include "navigation.hpp" namespace CSVRender { - SceneWidget::SceneWidget(QWidget *parent) : QWidget(parent) , mWindow(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_NoSystemBackground); + setFocusPolicy (Qt::StrongFocus); + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); // Throw in a random color just to make sure multiple scenes work @@ -44,6 +54,16 @@ namespace CSVRender mCamera->lookAt(0,0,0); mCamera->setNearClipDistance(0.1); 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() @@ -81,7 +101,21 @@ namespace CSVRender 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) @@ -93,7 +127,6 @@ namespace CSVRender e->accept(); } - QPaintEngine* SceneWidget::paintEngine() const { // We don't want another paint engine to get in the way. @@ -130,4 +163,151 @@ namespace CSVRender 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; + } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 355a6331e..ad68897ac 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -8,33 +8,77 @@ namespace Ogre class Camera; class SceneManager; class RenderWindow; + class ColourValue; } namespace CSVRender { + class Navigation; class SceneWidget : public QWidget { Q_OBJECT - public: - SceneWidget(QWidget *parent); - virtual ~SceneWidget(void); + public: - QPaintEngine* paintEngine() const; + SceneWidget(QWidget *parent); + virtual ~SceneWidget(); - private: - void paintEvent(QPaintEvent* e); - void resizeEvent(QResizeEvent* e); - bool event(QEvent* e); + QPaintEngine* paintEngine() const; - void updateOgreWindow(); + void setAmbient (const Ogre::ColourValue& colour); + ///< \note The actual ambient colour may differ based on lighting settings. - Ogre::Camera* mCamera; - Ogre::SceneManager* mSceneMgr; - Ogre::RenderWindow* mWindow; + protected: + + 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 diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp new file mode 100644 index 000000000..c7edbe79b --- /dev/null +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -0,0 +1,66 @@ + +#include "unpagedworldspacewidget.hpp" + +#include + +#include "../../model/doc/document.hpp" + +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" + +void CSVRender::UnpagedWorldspaceWidget::update() +{ + const CSMWorld::Record& record = + dynamic_cast&> (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 ( + *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(); +} \ No newline at end of file diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp new file mode 100644 index 000000000..17dc46918 --- /dev/null +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -0,0 +1,44 @@ +#ifndef OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H +#define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H + +#include + +#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 diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp new file mode 100644 index 000000000..dcd152bb3 --- /dev/null +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -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; +} \ No newline at end of file diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp new file mode 100644 index 000000000..2eccca3bf --- /dev/null +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -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 \ No newline at end of file diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp index 182d1cdd6..d59f0c234 100644 --- a/apps/opencs/view/tools/reportsubview.cpp +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -40,5 +40,5 @@ void CSVTools::ReportSubView::updateEditorSetting (const QString& key, const QSt void CSVTools::ReportSubView::show (const QModelIndex& index) { - focusId (mModel->getUniversalId (index.row())); + focusId (mModel->getUniversalId (index.row()), ""); } \ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 33ae327a0..3601ae094 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -9,7 +9,8 @@ #include "../filter/filterbox.hpp" -#include "../render/scenewidget.hpp" +#include "../render/pagedworldspacewidget.hpp" +#include "../render/unpagedworldspacewidget.hpp" #include "tablebottombox.hpp" #include "creator.hpp" @@ -32,37 +33,20 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout2->setContentsMargins (QMargins (0, 0, 0, 0)); 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)); + + if (id.getId()[0]=='#') + mScene = new CSVRender::PagedWorldspaceWidget (this); + else + mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); + + SceneToolMode *tool = mScene->makeNavigationSelector (toolbar); + toolbar->addTool (tool); + layout2->addWidget (toolbar, 0); -// temporarily disable OGRE-integration (need to fix path problem first) -#if 0 - CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); - - layout2->addWidget (sceneWidget, 1); + layout2->addWidget (mScene, 1); layout->insertLayout (0, layout2, 1); -#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); - - layout->insertLayout (0, layout2, 1); - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); @@ -73,6 +57,10 @@ toolbar->addTool (new SceneToolMode (toolbar)); widget->setLayout (layout); setWidget (widget); + + mScene->selectDefaultNavigationMode(); + + connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); } void CSVWorld::SceneSubView::setEditLock (bool locked) @@ -91,3 +79,8 @@ void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } + +void CSVWorld::SceneSubView::closeRequest() +{ + deleteLater(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index a0fed908d..ecf3fe4e4 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -10,6 +10,11 @@ namespace CSMDoc class Document; } +namespace CSVRender +{ + class WorldspaceWidget; +} + namespace CSVWorld { class Table; @@ -21,6 +26,7 @@ namespace CSVWorld Q_OBJECT TableBottomBox *mBottom; + CSVRender::WorldspaceWidget *mScene; public: @@ -31,6 +37,10 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); + + private slots: + + void closeRequest(); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index edf3bc6de..4bb9955e6 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -2,7 +2,6 @@ #include "table.hpp" #include - #include #include #include @@ -10,6 +9,8 @@ #include #include +#include "../../model/doc/document.hpp" + #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtableproxymodel.hpp" @@ -35,8 +36,21 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (selectedRows.size()==1) { menu.addAction (mEditAction); + if (mCreateAction) 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) @@ -162,11 +176,12 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const return deletableIds; } -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, + bool createAndDelete, bool sorting, CSMDoc::Document& document) +: mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), + mDocument (document) { - mModel = &dynamic_cast (*data.getTableModel (id)); + mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); mProxyModel = new CSMWorld::IdTableProxyModel (this); 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()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - undoStack, this); + mDocument.getUndoStack(), this); mDelegates.push_back (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())); 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)), this, SLOT (tableSizeUpdate())); @@ -268,13 +287,13 @@ void CSVWorld::Table::revertRecord() if (revertableIds.size()>0) { if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); + mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); for (std::vector::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) - mUndoStack.endMacro(); + mDocument.getUndoStack().endMacro(); } } } @@ -288,13 +307,13 @@ void CSVWorld::Table::deleteRecord() if (deletableIds.size()>0) { if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); + mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); for (std::vector::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) - mUndoStack.endMacro(); + mDocument.getUndoStack().endMacro(); } } } @@ -306,7 +325,7 @@ void CSVWorld::Table::editRecord() QModelIndexList selectedRows = selectionModel()->selectedRows(); 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; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + + std::pair 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) { int columns = mModel->columnCount(); @@ -517,7 +553,7 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) std::auto_ptr command (new CSMWorld::ModifyCommand (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); - mUndoStack.push (command.release()); + mDocument.getUndoStack().push (command.release()); } } //TODO handle drops from different document } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 615a31b4d..e8d5648d1 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -10,13 +10,14 @@ #include "../../model/filter/node.hpp" #include "../../model/world/columnbase.hpp" -namespace CSMDoc { - class Document; -} - class QUndoStack; class QAction; +namespace CSMDoc +{ + class Document; +} + namespace CSMWorld { class Data; @@ -35,7 +36,6 @@ namespace CSVWorld Q_OBJECT std::vector mDelegates; - QUndoStack& mUndoStack; QAction *mEditAction; QAction *mCreateAction; QAction *mCloneAction; @@ -43,14 +43,12 @@ namespace CSVWorld QAction *mDeleteAction; QAction *mMoveUpAction; QAction *mMoveDownAction; + QAction *mViewAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; int mRecordStatusDisplay; - - /// \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; + CSMDoc::Document& mDocument; private: @@ -70,9 +68,8 @@ namespace CSVWorld public: - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, - bool sorting, const CSMDoc::Document& document); - + Table (const CSMWorld::UniversalId& id, bool createAndDelete, + bool sorting, CSMDoc::Document& document); ///< \param createAndDelete Allow creation and deletion of records. /// \param sorting Allow changing order of rows in the view via column headers. @@ -86,7 +83,7 @@ namespace CSVWorld signals: - void editRequest (int row); + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void selectionSizeChanged (int size); @@ -112,6 +109,8 @@ namespace CSVWorld void moveDownRecord(); + void viewRecord(); + public slots: void tableSizeUpdate(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e330d4775..2d08d186e 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -24,7 +24,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); 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); @@ -36,7 +36,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D 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)), mBottom, SLOT (selectionSizeChanged (int))); @@ -81,9 +82,9 @@ void CSVWorld::TableSubView::setEditLock (bool 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) diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b3c253919..910fec325 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -53,7 +53,7 @@ namespace CSVWorld private slots: - void editRequest (int row); + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void cloneRequest (const CSMWorld::UniversalId& toClone); void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, Qt::DropAction action); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index be583ea74..eb502de38 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -159,3 +159,10 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw gcov) 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) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e80bd954e..4c3cadb3b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1,6 +1,7 @@ #include "engine.hpp" #include +#include #include #include @@ -456,7 +457,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) else { pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; - pos.rot[0] = pos.rot[1] = pos.pos[2] = 0; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; world->changeToExteriorCell (pos); } @@ -621,4 +622,4 @@ void OMW::Engine::setActivationDistanceOverride (int distance) void OMW::Engine::setWarningsMode (int mode) { mWarningsMode = mode; -} \ No newline at end of file +} diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 33bba07e1..3d70fdc6a 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -3,7 +3,7 @@ #include -#include +#include namespace ESM { diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 56d9601fc..8e4e9703f 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/topic.hpp" diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 694970e23..52682342f 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -6,7 +6,7 @@ #include "MyGUI_TextureUtility.h" #include "MyGUI_FactoryManager.h" -#include +#include #include #include #include diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 28aa371cf..458cf2a19 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -5,7 +5,7 @@ #include "MyGUI_Widget.h" #include -#include +#include #include #include diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 92205c3e9..ada004612 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -1,5 +1,7 @@ #include "enchantingdialog.hpp" +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index a0d67b025..c6bd6d15d 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -206,9 +206,9 @@ struct JournalViewModelImpl : JournalViewModel const MWDialogue::Quest& quest = i->second; // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. - if (quest.getName().empty()) - visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); - else + // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed + // to appear in the quest book. + if (!quest.getName().empty()) visitor (reinterpret_cast (&i->second), toUtf8Span (quest.getName())); } } diff --git a/apps/openmw/mwgui/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp index 21c3a1178..9efdeae54 100644 --- a/apps/openmw/mwgui/journalviewmodel.hpp +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 868b58209..37e29591b 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,6 +1,13 @@ #include "loadingscreen.hpp" #include +#include +#include +#include +#include +#include +#include +#include #include @@ -66,6 +73,10 @@ namespace MWGui 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. // 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 :/ @@ -74,16 +85,36 @@ namespace MWGui mWindow->setVSyncEnabled(false); #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); if (mFirstLoad) { changeWallpaper(); } - else - { - mBackgroundImage->setImageTexture(""); - } MWBase::Environment::get().getWindowManager()->pushGuiMode(mFirstLoad ? GM_LoadingWallpaper : GM_Loading); } @@ -197,8 +228,6 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(0, true); - mWindow->getViewport(0)->setClearEveryFrame(false); - // First, swap buffers from last draw, then, queue an update of the // window contents, but don't swap buffers (which would have // caused a sync / flush and would be expensive). @@ -208,9 +237,6 @@ namespace MWGui mWindow->update(false); - mWindow->getViewport(0)->setClearEveryFrame(true); - - mRectangle->setVisible(false); // resume 3d rendering diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 2d1d7431f..e91e5951d 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -2,6 +2,7 @@ #define MWGUI_LOADINGSCREEN_H #include +#include #include "windowbase.hpp" diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 51e160d26..1cc9610df 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 1e52ff26a..249477551 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H -#include +#include #include "windowpinnablebase.hpp" diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index d729ee7fa..de96bcacd 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -1,5 +1,7 @@ #include "repair.hpp" +#include + #include #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 8716c4dea..f941c699b 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -1,5 +1,7 @@ #include "tooltips.hpp" +#include + #include #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f44078256..1b71157a7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "MyGUI_UString.h" #include "MyGUI_IPointer.h" #include "MyGUI_ResourceImageSetPointer.h" diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 314053799..b5d83427b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -455,7 +455,7 @@ namespace MWInput mInputBinder->adjustMouseRegion(width, height); } - bool InputManager::keyPressed( const SDL_KeyboardEvent &arg ) + void InputManager::keyPressed( const SDL_KeyboardEvent &arg ) { // Cut, copy & paste MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); @@ -501,7 +501,6 @@ namespace MWInput if (kc != OIS::KC_UNASSIGNED) MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); - return true; } void InputManager::textInput(const SDL_TextInputEvent &arg) @@ -512,23 +511,21 @@ namespace MWInput 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); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); 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); 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)); if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) @@ -539,20 +536,16 @@ namespace MWInput 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); 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); @@ -600,8 +593,6 @@ namespace MWInput MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true); } } - - return true; } void InputManager::windowFocusChange(bool have_focus) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d41b4c3f3..bd3f4954b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -86,13 +86,13 @@ namespace MWInput virtual void resetToDefaultBindings(); public: - virtual bool keyPressed(const SDL_KeyboardEvent &arg ); - virtual bool keyReleased( const SDL_KeyboardEvent &arg ); + virtual void keyPressed(const SDL_KeyboardEvent &arg ); + virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); - virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); + virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual void mouseMoved( const SFO::MouseMotionEvent &arg ); virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 5be90bf2c..fd2fa61ba 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -15,7 +15,7 @@ #include "../mwbase/dialoguemanager.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" #include "character.hpp" // fixme: for getActiveWeapon @@ -140,11 +140,12 @@ namespace MWMechanics { MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); 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 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) { const MWWorld::Store &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) mFollowTarget = false; - buildNewPath(actor); + buildNewPath(actor); //may fail to build a path, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - 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; + //if no new path leave mTargetAngle unchanged + if(!mPathFinder.getPath().empty()) + { + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + 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; mReadyToAttack = false; @@ -302,9 +307,13 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + float dist = -1; //hack to indicate first time, to construct a new path + if(!mPathFinder.getPath().empty()) + { + 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; bool isOutside = actor.getCell()->getCell()->isExterior(); @@ -313,7 +322,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if(dist > targetPosThreshold) + if((dist < 0) || (dist > targetPosThreshold)) { //construct new path only if target has moved away more than on 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, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + if(!mPathFinder.getPath().empty()) + { + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; + } } } } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 8918bfbe7..74587a626 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -509,4 +511,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) mTimeToStartDrowning = state.mTimeToStartDrowning; mLastDrowningHit = state.mLastDrowningHit; mLevelHealthBonus = state.mLevelHealthBonus; -} \ No newline at end of file +} diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index efa2bdf69..5314b5919 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -393,6 +393,8 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { + if (mPath.size() < 2) + return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2ddea7682..e62fee6e2 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -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 "< #include #include #include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index cd30cdf46..16e6ab017 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -3,6 +3,7 @@ #include #include +#include #include diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 241f7e470..ba39d10d5 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 7d41525b7..1e6119daa 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "animation.hpp" #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 018dc082a..76ad1890f 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -75,10 +75,9 @@ namespace MWRender if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) - { - land->loadData(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(mask); } for (int cellY=0; cellY #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d07aad31d..60760b7fb 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -4,6 +4,11 @@ #include #include #include +#include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index c97e5279a..9c10ca84b 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -109,6 +109,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) { uniqueID = uniqueID+1; sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); + sg->setOrigin(ptr.getRefData().getBaseNode()->getPosition()); mStaticGeometrySmall[ptr.getCell()] = sg; 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; sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); + sg->setOrigin(ptr.getRefData().getBaseNode()->getPosition()); mStaticGeometry[ptr.getCell()] = sg; } else diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index 246103471..67bc75e02 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/refraction.cpp b/apps/openmw/mwrender/refraction.cpp index d590dbf4c..f22968e9d 100644 --- a/apps/openmw/mwrender/refraction.cpp +++ b/apps/openmw/mwrender/refraction.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 844be52d8..7c70b17f0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -5,12 +5,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include @@ -649,6 +651,18 @@ void RenderingManager::setGlare(bool 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) { if (cell->getCell()->isExterior()) @@ -659,9 +673,6 @@ void RenderingManager::requestMap(MWWorld::CellStore* cell) Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5); dims.merge(mTerrain->getWorldBoundingBox(center)); - if (dims.isFinite()) - mTerrain->update(dims.getCenter()); - mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z); } else @@ -981,13 +992,11 @@ void RenderingManager::screenshot(Image &image, int w, int h) Ogre::PixelFormat pf = rt->suggestPixelFormat(); - std::vector data; - data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf)); - - Ogre::PixelBox pb(w, h, 1, pf, &data[0]); - rt->copyContentsToMemory(pb); - - image.loadDynamicImage(&data[0], w, h, pf); + image.loadDynamicImage( + 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 + ); + rt->copyContentsToMemory(image.getPixelBox()); // getPixelBox returns a box sharing the same memory as the image Ogre::TextureManager::getSingleton().remove(tempName); mRendering.getCamera()->setAspectRatio(oldAspect); @@ -1045,20 +1054,16 @@ void RenderingManager::enableTerrain(bool enable) { if (!mTerrain) { - Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(listener); - mTerrain = new Terrain::World(listener, mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::World(mRendering.getScene(), new MWRender::TerrainStorage(), RV_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"), Settings::Manager::getBool("split", "Shadows")); mTerrain->update(mRendering.getCamera()->getRealPosition()); - mTerrain->setLoadingListener(NULL); } mTerrain->setVisible(true); } - else - if (mTerrain) + else if (mTerrain) mTerrain->setVisible(false); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 64ec029ce..115a94786 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -180,6 +180,10 @@ public: void removeWaterRippleEmitter (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); ///< request the local map for a cell diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index e5db8346f..f52deedcc 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 9ebb0ab08..33e337649 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 39f7ccc85..90c08c299 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 197572db9..2558c95c5 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,12 +14,14 @@ #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" +#include + namespace MWRender { - Ogre::AxisAlignedBox TerrainStorage::getBounds() + void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) { - int minX = 0, minY = 0, maxX = 0, maxY = 0; + minX = 0, minY = 0, maxX = 0, maxY = 0; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -39,8 +42,6 @@ namespace MWRender // since grid coords are at cell origin, we need to add 1 cell maxX += 1; maxY += 1; - - return Ogre::AxisAlignedBox(minX, minY, 0, maxX, maxY, 0); } ESM::Land* TerrainStorage::getLand(int cellX, int cellY) @@ -48,10 +49,6 @@ namespace MWRender const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); ESM::Land* land = esmStore.get().search(cellX, cellY); - // Load the data we are definitely going to need - int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; - if (land && !land->isDataLoaded(mask)) - land->loadData(mask); return land; } @@ -169,10 +166,10 @@ namespace MWRender } - void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer) + void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, + std::vector& positions, + std::vector& normals, + std::vector& colours) { // LOD level n means every 2^n-th vertex is kept size_t increment = 1 << lodLevel; @@ -186,11 +183,8 @@ namespace MWRender size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; - std::vector colors; - colors.resize(numVerts*numVerts*4); - std::vector positions; + colours.resize(numVerts*numVerts*4); positions.resize(numVerts*numVerts*3); - std::vector normals; normals.resize(numVerts*numVerts*3); Ogre::Vector3 normal; @@ -276,7 +270,7 @@ namespace MWRender color.a = 1; Ogre::uint32 rsColor; Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); - memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); + memcpy(&colours[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); ++vertX; } @@ -289,10 +283,6 @@ namespace MWRender assert(vertX_ == numVerts); // Ensure we covered whole area } assert(vertY_ == numVerts); // Ensure we covered whole area - - vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); - normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); - colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); } TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY, @@ -318,9 +308,6 @@ namespace MWRender ESM::Land* land = getLand(cellX, cellY); if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VTEX)) - land->loadData(ESM::Land::DATA_VTEX); - int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin @@ -345,8 +332,24 @@ namespace MWRender return texture; } + void TerrainStorage::getBlendmaps (const std::vector& nodes, std::vector& out, bool pack) + { + for (std::vector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) + { + out.push_back(Terrain::LayerCollection()); + out.back().mTarget = *it; + getBlendmapsImpl((*it)->getSize(), (*it)->getCenter(), pack, out.back().mBlendmaps, out.back().mLayers); + } + } + void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, - bool pack, std::vector &blendmaps, std::vector &layerList) + bool pack, std::vector &blendmaps, std::vector &layerList) + { + getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList); + } + + void TerrainStorage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter, + bool pack, std::vector &blendmaps, std::vector &layerList) { // TODO - blending isn't completely right yet; the blending radius appears to be // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap @@ -391,16 +394,14 @@ namespace MWRender // Second iteration - create and fill in the blend maps const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; - std::vector data; - data.resize(blendmapSize * blendmapSize * channels, 0); for (int i=0; iloadRawData(stream, blendmapSize, blendmapSize, format); - blendmaps.push_back(map); + blendmaps.push_back(Ogre::PixelBox(blendmapSize, blendmapSize, 1, format, pData)); } } @@ -543,6 +540,12 @@ namespace MWRender info.mSpecular = true; } + // This wasn't cached, so the textures are probably not loaded either. + // Background load them so they are hopefully already loaded once we need them! + Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mDiffuseMap, "General"); + if (!info.mNormalMap.empty()) + Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mNormalMap, "General"); + mLayerInfoMap[texture] = info; return info; diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 5c2035952..b5e6012f3 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -1,6 +1,9 @@ #ifndef MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H +#include +#include + #include namespace MWRender @@ -14,10 +17,10 @@ namespace MWRender public: /// Get bounds of the whole terrain in cell units - virtual Ogre::AxisAlignedBox getBounds(); + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); - /// Get the minimum and maximum heights of a terrain chunk. - /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Get the minimum and maximum heights of a terrain region. + /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. /// @param size size of the chunk in cell units /// @param center center of the chunk in cell units @@ -27,20 +30,23 @@ namespace MWRender virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); /// Fill vertex buffers for a terrain chunk. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! + /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units - /// @param vertexBuffer buffer to write vertices - /// @param normalBuffer buffer to write vertex normals - /// @param colourBuffer buffer to write vertex colours - virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer); + /// @param positions buffer to write vertices + /// @param normals buffer to write vertex normals + /// @param colours buffer to write vertex colours + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, + std::vector& positions, + std::vector& normals, + std::vector& colours); /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from *one* background thread. /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - @@ -49,9 +55,21 @@ namespace MWRender /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, + std::vector& blendmaps, std::vector& layerList); + /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information. + /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. + /// @note The terrain chunks shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from *one* background thread. + /// @param nodes A collection of nodes for which to retrieve the aforementioned data + /// @param out Output vector + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack); + virtual float getHeightAt (const Ogre::Vector3& worldPos); virtual Terrain::LayerInfo getDefaultLayer(); @@ -81,6 +99,11 @@ namespace MWRender std::map mLayerInfoMap; Terrain::LayerInfo getLayerInfo(const std::string& texture); + + // Non-virtual + void getBlendmapsImpl (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + std::vector& blendmaps, + std::vector& layerList); }; } diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index adf20dc63..f3c0971e7 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -9,6 +9,11 @@ #include #include #include +#include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 9e3105168..1fa5d8834 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -1,11 +1,16 @@ #include "water.hpp" -#include +#include #include #include +#include #include #include #include +#include +#include +#include +#include #include "sky.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index a4a766226..de63b9906 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "locals.hpp" diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 58566225b..9a3dd7342 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include "openal_output.hpp" @@ -172,6 +174,7 @@ class OpenAL_SoundStream : public Sound DecoderPtr mDecoder; volatile bool mIsFinished; + volatile bool mIsInitialBatchEnqueued; void updateAll(bool local); @@ -264,7 +267,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) - , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false) { throwALerror(); @@ -315,26 +318,8 @@ void OpenAL_SoundStream::play() alSourcei(mSource, AL_BUFFER, 0); throwALerror(); mSamplesQueued = 0; - - std::vector data(mBufferSize); - - bool finished = false; - for(ALuint i = 0;i < sNumBuffers && !finished;i++) - { - size_t got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - ALuint bufid = mBuffers[i]; - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - throwALerror(); - mSamplesQueued += getBufferSampleCount(bufid); - } - } - - mIsFinished = finished; - alSourcePlay(mSource); + mIsFinished = false; + mIsInitialBatchEnqueued = false; mOutput.mStreamThread->add(this); } @@ -342,6 +327,7 @@ void OpenAL_SoundStream::stop() { mOutput.mStreamThread->remove(this); mIsFinished = true; + mIsInitialBatchEnqueued = false; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); @@ -454,6 +440,24 @@ bool OpenAL_SoundStream::process() } while(processed > 0); throwALerror(); } + else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet + std::vector data(mBufferSize); + + for(ALuint i = 0;i < sNumBuffers && !finished;i++) + { + size_t got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + ALuint bufid = mBuffers[i]; + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + throwALerror(); + mSamplesQueued += getBufferSampleCount(bufid); + } + } + mIsInitialBatchEnqueued = true; + } if(state != AL_PLAYING && state != AL_PAUSED) { @@ -471,6 +475,7 @@ bool OpenAL_SoundStream::process() std::cout<< "Error updating stream \""<getName()<<"\"" <updateSlot (slot, profile); - std::ofstream stream (slot->mPath.string().c_str()); + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); ESM::ESMWriter writer; diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 8f521c8a6..d8d2cefbf 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1a16870da..8ef5797ca 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 10afbc675..3d4413a35 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -43,7 +43,7 @@ namespace mPhysics (physics), mRendering (rendering) {} - bool InsertFunctor::InsertFunctor::operator() (const MWWorld::Ptr& ptr) + bool InsertFunctor::operator() (const MWWorld::Ptr& ptr) { if (mRescale) { @@ -79,55 +79,6 @@ namespace return true; } - - template - void insertCellRefList(MWRender::RenderingManager& rendering, - T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics, bool rescale, Loading::Listener* loadingListener) - { - if (!cellRefList.mList.empty()) - { - const MWWorld::Class& class_ = - MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.mList.begin(), &cell)); - for (typename T::List::iterator it = cellRefList.mList.begin(); - it != cellRefList.mList.end(); it++) - { - if (rescale) - { - if (it->mRef.mScale<0.5) - it->mRef.mScale = 0.5; - else if (it->mRef.mScale>2) - it->mRef.mScale = 2; - } - - if (it->mData.getCount() && it->mData.isEnabled()) - { - MWWorld::Ptr ptr (&*it, &cell); - - try - { - rendering.addObject(ptr); - class_.insertObject(ptr, physics); - - float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); - MWBase::Environment::get().getWorld()->localRotateObject(ptr, ax, ay, az); - - MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale); - class_.adjustPosition(ptr); - } - catch (const std::exception& e) - { - std::string error ("error during rendering: "); - std::cerr << error + e.what() << std::endl; - } - } - - loadingListener->increaseProgress(1); - } - } - } - } @@ -211,8 +162,6 @@ namespace MWWorld mRendering.cellAdded (cell); mRendering.configureAmbient(*cell); - mRendering.requestMap(cell); - mRendering.configureAmbient(*cell); } // register local scripts @@ -246,6 +195,11 @@ namespace MWWorld mechMgr->updateCell(old, player); mechMgr->watchActor(player); + mRendering.updateTerrain(); + + for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active) + mRendering.requestMap(*active); + MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); } @@ -260,12 +214,13 @@ namespace MWWorld void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { - mRendering.enableTerrain(true); Nif::NIFFile::CacheLock cachelock; Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); + mRendering.enableTerrain(true); + std::string loadingExteriorText = "#{sLoadingMessage3}"; loadingListener->setLabel(loadingExteriorText); @@ -410,11 +365,11 @@ namespace MWWorld Nif::NIFFile::CacheLock lock; MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.5); - mRendering.enableTerrain(false); - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); + mRendering.enableTerrain(false); + std::string loadingInteriorText = "#{sLoadingMessage2}"; loadingListener->setLabel(loadingInteriorText); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 7bd00d6bf..6b99c0a0c 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -388,6 +388,8 @@ namespace MWWorld typedef std::vector::const_iterator iterator; + // Must be threadsafe! Called from terrain background loading threads. + // Not a big deal here, since ESM::LandTexture can never be modified or inserted/erased const ESM::LandTexture *search(size_t index, size_t plugin) const { assert(plugin < mStatic.size()); const LandTextureList <exl = mStatic[plugin]; @@ -487,6 +489,8 @@ namespace MWWorld return iterator(mStatic.end()); } + // Must be threadsafe! Called from terrain background loading threads. + // Not a big deal here, since ESM::Land can never be modified or inserted/erased ESM::Land *search(int x, int y) const { ESM::Land land; land.mX = x, land.mY = y; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b51e224f8..f154f3b24 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1873,6 +1873,8 @@ namespace MWWorld bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) { + if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled()) + return false; // cannot get LOS unless both NPC's are enabled Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); float* pos1 = npc.getRefData().getPosition().pos; Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); diff --git a/cmake/FindSDL.cmake b/cmake/FindSDL.cmake deleted file mode 100644 index 0dc02f5b6..000000000 --- a/cmake/FindSDL.cmake +++ /dev/null @@ -1,177 +0,0 @@ -# Locate SDL library -# This module defines -# SDL_LIBRARY, the name of the library to link against -# SDL_FOUND, if false, do not try to link to SDL -# SDL_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL_BUILDING_LIBRARY -# If this is defined, then no SDL_main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL_LIBRARY variable. -# -# Don't forget to include SDLmain.h and SDLmain.m your project for the -# OS X framework based version. (Other versions link to -lSDLmain which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL_LIBRARY_TEMP in your configuration -# and no SDL_LIBRARY, it means CMake did not find your SDL library -# (SDL.dll, libsdl.so, SDL.framework, etc). -# Set SDL_LIBRARY_TEMP to point to your SDL library, and configure again. -# Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL_LIBRARY -# variable, but when these values are unset, SDL_LIBRARY does not get created. -# -# -# $SDLDIR is an environment variable that would -# correspond to the ./configure --prefix=$SDLDIR -# used in building SDL. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL guidelines. -# Added a search for SDLmain which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL/SDL.h to just SDL.h -# This needed to change because "proper" SDL convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL/ (see FreeBSD). - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -FIND_PATH(SDL_INCLUDE_DIR SDL.h - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES include/SDL include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local/include/SDL12 - /usr/local/include/SDL11 # FreeBSD ports - /usr/include/SDL12 - /usr/include/SDL11 - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) -#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}") - -# SDL-1.1 is the name used by FreeBSD ports... -# don't confuse it for the version number. -FIND_LIBRARY(SDL_LIBRARY_TEMP - NAMES SDL SDL-1.1 - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt -) - -#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}") - -IF(NOT SDL_BUILDING_LIBRARY) - IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDLmain for compatibility even though they don't - # necessarily need it. - FIND_LIBRARY(SDLMAIN_LIBRARY - NAMES SDLmain SDLmain-1.1 - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") -ENDIF(NOT SDL_BUILDING_LIBRARY) - -# SDL may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -SET(SDL_FOUND "NO") -IF(SDL_LIBRARY_TEMP) - # For SDLmain - IF(NOT SDL_BUILDING_LIBRARY) - IF(SDLMAIN_LIBRARY) - SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP}) - ENDIF(SDLMAIN_LIBRARY) - ENDIF(NOT SDL_BUILDING_LIBRARY) - - # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP}) - ENDIF(MINGW) - - # Set the final string here so the GUI reflects the final state. - SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "") - - SET(SDL_FOUND "YES") -ENDIF(SDL_LIBRARY_TEMP) - -#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}") - diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index fecd1654d..56ff1d545 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -85,10 +85,6 @@ function(get_git_head_revision _refspecvar _hashvar) endfunction() function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - #get_git_head_revision(refspec hash) if(NOT GIT_FOUND) @@ -133,7 +129,8 @@ endfunction() function(get_git_tag_revision _var) if(NOT GIT_FOUND) - find_package(Git QUIET) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() endif() execute_process(COMMAND diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 831b14057..db4ecad0b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -73,8 +73,9 @@ add_component_dir (translation translation ) +add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain - quadtreenode chunk world storage material + quadtreenode chunk world storage material buffercache defs ) add_component_dir (loadinglistener diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index eb741fb10..6574f096b 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "bsa_file.hpp" #include "../files/constrainedfiledatastream.hpp" diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 6f3ab3bce..017adf1e3 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -24,7 +24,7 @@ #ifndef BSA_BSA_FILE_H #define BSA_BSA_FILE_H -#include +#include #include #include #include diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index c1f167992..57842796f 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_ESM_DEFS_H #define OPENMW_ESM_DEFS_H -#include +#include namespace ESM { diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 6f51c767e..d3e6e7fea 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include namespace ESM diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 897c8fe73..b6c0ebc70 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_ESM_READER_H #define OPENMW_ESM_READER_H -#include +#include #include #include #include diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index f38591b7b..91f123eb7 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -80,8 +80,8 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? + writeT(0); // Size goes here + writeT(0); // Unused header? writeT(flags); mRecords.push_back(rec); @@ -105,7 +105,7 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here + writeT(0); // Size goes here mRecords.push_back(rec); assert(mRecords.back().size == 0); @@ -120,7 +120,7 @@ namespace ESM mStream->seekp(rec.position); mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(int)); + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); mCounting = true; mStream->seekp(0, std::ios::end); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 94f0a1004..33650e678 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -17,7 +17,7 @@ class ESMWriter { std::string name; std::streampos position; - size_t size; + uint32_t size; }; public: diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index dd7bf3e42..55d043c8a 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -249,7 +249,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) if (id.mPaged) { - id.mWorldspace = "default"; + id.mWorldspace = "sys::default"; id.mIndex.mX = mData.mX; id.mIndex.mY = mData.mY; } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 32abb7799..028341ced 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_ESM_LAND_H #define OPENMW_ESM_LAND_H -#include +#include #include "esmcommon.hpp" diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 321bcf7c8..66f6fde97 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include namespace { diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 71fd1b523..06ee9fb4e 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -219,7 +219,7 @@ LowLevelFile::LowLevelFile () LowLevelFile::~LowLevelFile () { - if (mHandle == INVALID_HANDLE_VALUE) + if (mHandle != INVALID_HANDLE_VALUE) CloseHandle (mHandle); } diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 91ae93b40..79e1cc2a5 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -42,7 +42,7 @@ #include #include -#include +#include #include "record.hpp" #include "niftypes.hpp" diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 43622cb9a..8bebe0589 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 43e8934c8..acab419b0 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index 840cf4bb0..1b9a899a0 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE #include diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp new file mode 100644 index 000000000..f693c0e40 --- /dev/null +++ b/components/terrain/buffercache.cpp @@ -0,0 +1,200 @@ +#include "buffercache.hpp" + +#include + +#include "defs.hpp" + +namespace Terrain +{ + + Ogre::HardwareVertexBufferSharedPtr BufferCache::getUVBuffer() + { + if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) + { + return mUvBufferMap[mNumVerts]; + } + + int vertexCount = mNumVerts * mNumVerts; + + std::vector uvs; + uvs.reserve(vertexCount*2); + + for (unsigned int col = 0; col < mNumVerts; ++col) + { + for (unsigned int row = 0; row < mNumVerts; ++row) + { + uvs.push_back(col / static_cast(mNumVerts-1)); // U + uvs.push_back(row / static_cast(mNumVerts-1)); // V + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr buffer = mgr->createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), + vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + + buffer->writeData(0, buffer->getSizeInBytes(), &uvs[0], true); + + mUvBufferMap[mNumVerts] = buffer; + return buffer; + } + + Ogre::HardwareIndexBufferSharedPtr BufferCache::getIndexBuffer(int flags) + { + unsigned int verts = mNumVerts; + + if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) + { + return mIndexBufferMap[flags]; + } + + // LOD level n means every 2^n-th vertex is kept + size_t lodLevel = (flags >> (4*4)); + + size_t lodDeltas[4]; + for (int i=0; i<4; ++i) + lodDeltas[i] = (flags >> (4*i)) & (0xf); + + bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); + + size_t increment = 1 << lodLevel; + assert(increment < verts); + std::vector indices; + indices.reserve((verts-1)*(verts-1)*2*3 / increment); + + size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; + // If any edge needs stitching we'll skip all edges at this point, + // mainly because stitching one edge would have an effect on corners and on the adjacent edges + if (anyDeltas) + { + colStart += increment; + colEnd -= increment; + rowEnd -= increment; + rowStart += increment; + } + for (size_t row = rowStart; row < rowEnd; row += increment) + { + for (size_t col = colStart; col < colEnd; col += increment) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); + + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); + } + } + + size_t innerStep = increment; + if (anyDeltas) + { + // Now configure LOD transitions at the edges - this is pretty tedious, + // and some very long and boring code, but it works great + + // South + size_t row = 0; + size_t outerStep = 1 << (lodDeltas[South] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+outerStep)+row); + // Make sure not to touch the right edge + if (col+outerStep == verts-1) + indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); + else + indices.push_back(verts*(col+outerStep)+row+innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col)+row); + indices.push_back(verts*(col+i+innerStep)+row+innerStep); + indices.push_back(verts*(col+i)+row+innerStep); + } + } + + // North + row = verts-1; + outerStep = 1 << (lodDeltas[North] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*(col+outerStep)+row); + indices.push_back(verts*col+row); + // Make sure not to touch the left edge + if (col == 0) + indices.push_back(verts*(col+innerStep)+row-innerStep); + else + indices.push_back(verts*col+row-innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col+i)+row-innerStep); + indices.push_back(verts*(col+i+innerStep)+row-innerStep); + indices.push_back(verts*(col+outerStep)+row); + } + } + + // West + size_t col = 0; + outerStep = 1 << (lodDeltas[West] + lodLevel); + for (size_t row = 0; row < verts-1; row += outerStep) + { + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*col+row); + // Make sure not to touch the top edge + if (row+outerStep == verts-1) + indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); + else + indices.push_back(verts*(col+innerStep)+row+outerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row); + indices.push_back(verts*(col+innerStep)+row+i); + indices.push_back(verts*(col+innerStep)+row+i+innerStep); + } + } + + // East + col = verts-1; + outerStep = 1 << (lodDeltas[East] + lodLevel); + for (size_t row = 0; row < verts-1; row += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*col+row+outerStep); + // Make sure not to touch the bottom edge + if (row == 0) + indices.push_back(verts*(col-innerStep)+row+innerStep); + else + indices.push_back(verts*(col-innerStep)+row); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*(col-innerStep)+row+i+innerStep); + indices.push_back(verts*(col-innerStep)+row+i); + } + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, + indices.size(), Ogre::HardwareBuffer::HBU_STATIC); + buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); + mIndexBufferMap[flags] = buffer; + return buffer; + } + +} diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp new file mode 100644 index 000000000..f0aea9bfd --- /dev/null +++ b/components/terrain/buffercache.hpp @@ -0,0 +1,36 @@ +#ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H +#define COMPONENTS_TERRAIN_BUFFERCACHE_H + +#include +#include + +#include + +namespace Terrain +{ + + /// @brief Implements creation and caching of vertex buffers for terrain chunks. + class BufferCache + { + public: + BufferCache(unsigned int numVerts) : mNumVerts(numVerts) {} + + /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) + /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) + Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags); + + Ogre::HardwareVertexBufferSharedPtr getUVBuffer (); + + private: + // Index buffers are shared across terrain batches where possible. There is one index buffer for each + // combination of LOD deltas and index buffer LOD we may need. + std::map mIndexBufferMap; + + std::map mUvBufferMap; + + unsigned int mNumVerts; + }; + +} + +#endif diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index a5c629088..0820dcc73 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -2,30 +2,23 @@ #include #include +#include +#include -#include "quadtreenode.hpp" -#include "world.hpp" -#include "storage.hpp" +#include + + +#include "world.hpp" // FIXME: for LoadResponseData, move to backgroundloader.hpp namespace Terrain { - Chunk::Chunk(QuadTreeNode* node, short lodLevel) - : mNode(node) - , mVertexLod(lodLevel) - , mAdditionalLod(0) + Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data) + : mBounds(bounds) { mVertexData = OGRE_NEW Ogre::VertexData; mVertexData->vertexStart = 0; - - unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); - - // Set the total number of vertices - size_t numVertsOneSide = mNode->getSize() * (verts-1); - numVertsOneSide /= 1 << lodLevel; - numVertsOneSide += 1; - assert(numVertsOneSide == verts); - mVertexData->vertexCount = numVertsOneSide * numVertsOneSide; + mVertexData->vertexCount = data.mPositions.size()/3; // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration; @@ -35,80 +28,50 @@ namespace Terrain // Positions vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - mVertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + Ogre::HardwareVertexBufferSharedPtr vertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + // Normals vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - mNormalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + Ogre::HardwareVertexBufferSharedPtr normalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + // UV texture coordinates vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0); - Ogre::HardwareVertexBufferSharedPtr uvBuf = mNode->getTerrain()->getVertexBuffer(numVertsOneSide); // Colours vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + Ogre::HardwareVertexBufferSharedPtr colourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), - mVertexBuffer, mNormalBuffer, mColourBuffer); + vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &data.mPositions[0], true); + normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &data.mNormals[0], true); + colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &data.mColours[0], true); - mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); - mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); - mVertexData->vertexBufferBinding->setBinding(2, uvBuf); - mVertexData->vertexBufferBinding->setBinding(3, mColourBuffer); + mVertexData->vertexBufferBinding->setBinding(0, vertexBuffer); + mVertexData->vertexBufferBinding->setBinding(1, normalBuffer); + mVertexData->vertexBufferBinding->setBinding(2, uvBuffer); + mVertexData->vertexBufferBinding->setBinding(3, colourBuffer); mIndexData = OGRE_NEW Ogre::IndexData(); mIndexData->indexStart = 0; } - - - void Chunk::updateIndexBuffer() + void Chunk::setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer) { - // Fetch a suitable index buffer (which may be shared) - size_t ourLod = mVertexLod + mAdditionalLod; - - int flags = 0; - - for (int i=0; i<4; ++i) - { - QuadTreeNode* neighbour = mNode->getNeighbour((Direction)i); - - // If the neighbour isn't currently rendering itself, - // go up until we find one. NOTE: We don't need to go down, - // because in that case neighbour's detail would be higher than - // our detail and the neighbour would handle stitching by itself. - while (neighbour && !neighbour->hasChunk()) - neighbour = neighbour->getParent(); - - size_t lod = 0; - if (neighbour) - lod = neighbour->getActualLodLevel(); - - if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - - lod = 0; // neighbours with more detail will do the stitching themselves - - // Use 4 bits for each LOD delta - if (lod > 0) - { - assert (lod - ourLod < (1 << 4)); - flags |= int(lod - ourLod) << (4*i); - } - } - - flags |= ((int)mAdditionalLod) << (4*4); - - size_t numIndices; - mIndexBuffer = mNode->getTerrain()->getIndexBuffer(flags, numIndices); - mIndexData->indexCount = numIndices; - mIndexData->indexBuffer = mIndexBuffer; + mIndexData->indexBuffer = buffer; + mIndexData->indexCount = buffer->getNumIndexes(); } Chunk::~Chunk() { +#if TERRAIN_USE_SHADER + sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); +#endif + Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); + OGRE_DELETE mVertexData; OGRE_DELETE mIndexData; } @@ -120,12 +83,12 @@ namespace Terrain const Ogre::AxisAlignedBox& Chunk::getBoundingBox(void) const { - return mNode->getBoundingBox(); + return mBounds; } Ogre::Real Chunk::getBoundingRadius(void) const { - return mNode->getBoundingBox().getHalfSize().length(); + return mBounds.getHalfSize().length(); } void Chunk::_updateRenderQueue(Ogre::RenderQueue* queue) @@ -146,7 +109,8 @@ namespace Terrain void Chunk::getRenderOperation(Ogre::RenderOperation& op) { - assert (!mIndexBuffer.isNull() && "Trying to render, but no index buffer set!"); + assert (!mIndexData->indexBuffer.isNull() && "Trying to render, but no index buffer set!"); + assert(!mMaterial.isNull() && "Trying to render, but no material set!"); op.useIndexes = true; op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; op.vertexData = mVertexData; diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index d74c65ba6..d037661ae 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -7,7 +7,8 @@ namespace Terrain { - class QuadTreeNode; + class BufferCache; + struct LoadResponseData; /** * @brief Renders a chunk of terrain, either using alpha splatting or a composite map. @@ -15,18 +16,14 @@ namespace Terrain class Chunk : public Ogre::Renderable, public Ogre::MovableObject { public: - /// @param lodLevel LOD level for the vertex buffer. - Chunk (QuadTreeNode* node, short lodLevel); + Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data); + virtual ~Chunk(); + /// @note Takes ownership of \a material void setMaterial (const Ogre::MaterialPtr& material); - /// Set additional LOD applied on top of vertex LOD. \n - /// This is achieved by changing the index buffer to omit vertices. - void setAdditionalLod (size_t lod) { mAdditionalLod = lod; } - size_t getAdditionalLod() { return mAdditionalLod; } - - void updateIndexBuffer(); + void setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer); // Inherited from MovableObject virtual const Ogre::String& getMovableType(void) const { static Ogre::String t = "MW_TERRAIN"; return t; } @@ -44,18 +41,11 @@ namespace Terrain virtual const Ogre::LightList& getLights(void) const; private: - QuadTreeNode* mNode; + Ogre::AxisAlignedBox mBounds; Ogre::MaterialPtr mMaterial; - size_t mVertexLod; - size_t mAdditionalLod; - Ogre::VertexData* mVertexData; Ogre::IndexData* mIndexData; - Ogre::HardwareVertexBufferSharedPtr mVertexBuffer; - Ogre::HardwareVertexBufferSharedPtr mNormalBuffer; - Ogre::HardwareVertexBufferSharedPtr mColourBuffer; - Ogre::HardwareIndexBufferSharedPtr mIndexBuffer; }; } diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp new file mode 100644 index 000000000..685937653 --- /dev/null +++ b/components/terrain/defs.hpp @@ -0,0 +1,65 @@ +#ifndef COMPONENTS_TERRAIN_DEFS_HPP +#define COMPONENTS_TERRAIN_DEFS_HPP + +namespace Terrain +{ + class QuadTreeNode; + + /// The alignment of the terrain + enum Alignment + { + /// Terrain is in the X/Z plane + Align_XZ = 0, + /// Terrain is in the X/Y plane + Align_XY = 1, + /// Terrain is in the Y/Z plane. + /// UNTESTED - use at own risk. + /// Besides, X as up axis? What is wrong with you? ;) + Align_YZ = 2 + }; + + inline void convertPosition(Alignment align, float &x, float &y, float &z) + { + switch (align) + { + case Align_XY: + return; + case Align_XZ: + std::swap(y, z); + // This is since -Z should be going *into* the screen + // If not doing this, we'd get wrong vertex winding + z *= -1; + return; + case Align_YZ: + std::swap(x, y); + std::swap(y, z); + return; + } + } + + enum Direction + { + North = 0, + East = 1, + South = 2, + West = 3 + }; + + struct LayerInfo + { + std::string mDiffuseMap; + std::string mNormalMap; + bool mParallax; // Height info in normal map alpha channel? + bool mSpecular; // Specular info in diffuse map alpha channel? + }; + + struct LayerCollection + { + QuadTreeNode* mTarget; + // Since we can't create a texture from a different thread, this only holds the raw texel data + std::vector mBlendmaps; + std::vector mLayers; + }; +} + +#endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 8e78d2216..faa73a986 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -6,7 +6,9 @@ #include +#if TERRAIN_USE_SHADER #include +#endif namespace { @@ -46,11 +48,15 @@ namespace Terrain Ogre::MaterialPtr MaterialGenerator::generate(Ogre::MaterialPtr mat) { + assert(!mLayerList.empty() && "Can't create material with no layers"); + return create(mat, false, false); } Ogre::MaterialPtr MaterialGenerator::generateForCompositeMapRTT(Ogre::MaterialPtr mat) { + assert(!mLayerList.empty() && "Can't create material with no layers"); + return create(mat, true, false); } @@ -64,7 +70,9 @@ namespace Terrain assert(!renderCompositeMap || !displayCompositeMap); if (!mat.isNull()) { +#if TERRAIN_USE_SHADER sh::Factory::getInstance().destroyMaterialInstance(mat->getName()); +#endif Ogre::MaterialManager::getSingleton().remove(mat->getName()); } @@ -144,6 +152,7 @@ namespace Terrain return mat; } +#if TERRAIN_USE_SHADER else { sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance (name.str()); @@ -235,7 +244,6 @@ namespace Terrain sh::MaterialInstancePass* p = material->createPass (); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); if (layerOffset != 0) @@ -344,6 +352,7 @@ namespace Terrain } } } +#endif return Ogre::MaterialManager::getSingleton().getByName(name.str()); } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 82ccc7c89..40a8baaf0 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -2,11 +2,14 @@ #include #include +#include +#include +#include #include "world.hpp" #include "chunk.hpp" #include "storage.hpp" - +#include "buffercache.hpp" #include "material.hpp" using namespace Terrain; @@ -141,7 +144,6 @@ namespace QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) : mMaterialGenerator(NULL) - , mIsActive(false) , mIsDummy(false) , mSize(size) , mLodLevel(Log2(mSize)) @@ -153,6 +155,7 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const , mParent(parent) , mTerrain(terrain) , mChunk(NULL) + , mLoadState(LS_Unloaded) { mBounds.setNull(); for (int i=0; i<4; ++i) @@ -169,7 +172,11 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const pos = mParent->getCenter(); pos = mCenter - pos; float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - mSceneNode->setPosition(Ogre::Vector3(pos.x*cellWorldSize, pos.y*cellWorldSize, 0)); + + Ogre::Vector3 sceneNodePos (pos.x*cellWorldSize, pos.y*cellWorldSize, 0); + mTerrain->convertPosition(sceneNodePos); + + mSceneNode->setPosition(sceneNodePos); mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } @@ -212,11 +219,31 @@ void QuadTreeNode::initAabb() mChildren[i]->initAabb(); mBounds.merge(mChildren[i]->getBoundingBox()); } - mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, mBounds.getMinimum().z), - Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, mBounds.getMaximum().z)); + float minH, maxH; + switch (mTerrain->getAlign()) + { + case Terrain::Align_XY: + minH = mBounds.getMinimum().z; + maxH = mBounds.getMaximum().z; + break; + case Terrain::Align_XZ: + minH = mBounds.getMinimum().y; + maxH = mBounds.getMaximum().y; + break; + case Terrain::Align_YZ: + minH = mBounds.getMinimum().x; + maxH = mBounds.getMaximum().x; + break; + } + Ogre::Vector3 min(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, minH); + Ogre::Vector3 max(Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, maxH)); + mBounds = Ogre::AxisAlignedBox (min, max); + mTerrain->convertBounds(mBounds); } - mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0), - mBounds.getMaximum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0)); + Ogre::Vector3 offset(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0); + mTerrain->convertPosition(offset); + mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + offset, + mBounds.getMaximum() + offset); } void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) @@ -229,169 +256,226 @@ const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox() return mBounds; } -void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loadingListener) +const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox() { - const Ogre::AxisAlignedBox& bounds = getBoundingBox(); - if (bounds.isNull()) - return; + return mWorldBounds; +} + +bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) +{ + if (isDummy()) + return true; + + if (mBounds.isNull()) + return true; float dist = distance(mWorldBounds, cameraPos); - bool distantLand = mTerrain->getDistantLandEnabled(); - // Make sure our scene node is attached if (!mSceneNode->isInSceneGraph()) { mParent->getSceneNode()->addChild(mSceneNode); } - /// \todo implement error metrics or some other means of not using arbitrary values - /// (general quality needs to be user configurable as well) + // Simple LOD selection + /// \todo use error metrics? size_t wantedLod = 0; - if (dist > 8192*1) - wantedLod = 1; - if (dist > 8192*2) - wantedLod = 2; - if (dist > 8192*5) - wantedLod = 3; - if (dist > 8192*12) - wantedLod = 4; - if (dist > 8192*32) - wantedLod = 5; - if (dist > 8192*64) + float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); + + if (!mTerrain->getDistantLandEnabled() && dist > cellWorldSize) + return true; + + if (dist > cellWorldSize*64) wantedLod = 6; + else if (dist > cellWorldSize*32) + wantedLod = 5; + else if (dist > cellWorldSize*12) + wantedLod = 4; + else if (dist > cellWorldSize*5) + wantedLod = 3; + else if (dist > cellWorldSize*2) + wantedLod = 2; + else if (dist > cellWorldSize * 1.42) // < sqrt2 so the 3x3 grid around player is always highest lod + wantedLod = 1; - bool hadChunk = hasChunk(); + bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod; - if (loadingListener) - loadingListener->indicateProgress(); - - if (!distantLand && dist > 8192*2) - { - if (mIsActive) - { - destroyChunks(true); - mIsActive = false; - } - return; - } - - mIsActive = true; - - if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod) + if (wantToDisplay) { // Wanted LOD is small enough to render this node in one chunk - if (!mChunk) + if (mLoadState == LS_Unloaded) { - mChunk = new Chunk(this, mLodLevel); - mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); - mChunk->setCastShadows(true); - mSceneNode->attachObject(mChunk); + mLoadState = LS_Loading; + mTerrain->queueLoad(this); + return false; + } - mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); - mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); + if (mLoadState == LS_Loaded) + { + // Additional (index buffer) LOD is currently disabled. + // This is due to a problem with the LOD selection when a node splits. + // After splitting, the distance is measured from the children's bounding boxes, which are possibly + // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD + // than the original node. + // In short, we'd sometimes get a switch to a lesser detail when actually moving closer. + // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour + // node hasn't split yet, and has a higher LOD than our node's child: + // ----- ----- ------------ + // | LOD | LOD | | + // | 1 | 1 | | + // |-----|-----| LOD 0 | + // | LOD | LOD | | + // | 0 | 0 | | + // ----- ----- ------------ + // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're + // doing here. + // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way. + //mChunk->setAdditionalLod(wantedLod - mLodLevel); - if (mSize == 1) + if (!mChunk->getVisible() && hasChildren()) { - ensureLayerInfo(); - mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + for (int i=0; i<4; ++i) + mChildren[i]->unload(true); + } + mChunk->setVisible(true); + + return true; + } + return false; // LS_Loading + } + + // We do not want to display this node - delegate to children if they are already loaded + if (!wantToDisplay && hasChildren()) + { + if (mChunk) + { + // Are children already loaded? + bool childrenLoaded = true; + for (int i=0; i<4; ++i) + if (!mChildren[i]->update(cameraPos)) + childrenLoaded = false; + + if (!childrenLoaded) + { + // Make sure child scene nodes are detached until all children are loaded + mSceneNode->removeAllChildren(); } else { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); - } - } + // Delegation went well, we can unload now + unload(); - // Additional (index buffer) LOD is currently disabled. - // This is due to a problem with the LOD selection when a node splits. - // After splitting, the distance is measured from the children's bounding boxes, which are possibly - // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD - // than the original node. - // In short, we'd sometimes get a switch to a lesser detail when actually moving closer. - // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour - // node hasn't split yet, and has a higher LOD than our node's child: - // ----- ----- ------------ - // | LOD | LOD | | - // | 1 | 1 | | - // |-----|-----| LOD 0 | - // | LOD | LOD | | - // | 0 | 0 | | - // ----- ----- ------------ - // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're - // doing here. - // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way. - //mChunk->setAdditionalLod(wantedLod - mLodLevel); - - mChunk->setVisible(true); - - if (!hadChunk && hasChildren()) - { - // Make sure child scene nodes are detached - mSceneNode->removeAllChildren(); - - // If distant land is enabled, keep the chunks around in case we need them again, - // otherwise, prefer low memory usage - if (!distantLand) for (int i=0; i<4; ++i) - mChildren[i]->destroyChunks(true); + { + if (!mChildren[i]->getSceneNode()->isInSceneGraph()) + mSceneNode->addChild(mChildren[i]->getSceneNode()); + } + } + return true; } - } - else - { - // Wanted LOD is too detailed to be rendered in one chunk, - // so split it up by delegating to child nodes - if (hadChunk) + else { - // If distant land is enabled, keep the chunks around in case we need them again, - // otherwise, prefer low memory usage - if (!distantLand) - destroyChunks(false); - else if (mChunk) - mChunk->setVisible(false); + bool success = true; + for (int i=0; i<4; ++i) + success = mChildren[i]->update(cameraPos) & success; + return success; } - assert(hasChildren() && "Leaf node's LOD needs to be 0"); - for (int i=0; i<4; ++i) - mChildren[i]->update(cameraPos, loadingListener); } + return false; } -void QuadTreeNode::destroyChunks(bool children) +void QuadTreeNode::load(const LoadResponseData &data) +{ + assert (!mChunk); + + mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); + mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); + mChunk->setCastShadows(true); + mSceneNode->attachObject(mChunk); + + mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); + mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); + + if (mTerrain->areLayersLoaded()) + { + if (mSize == 1) + { + mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + } + else + { + ensureCompositeMap(); + mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + } + } + // else: will be loaded in loadMaterials() after background thread has finished loading layers + mChunk->setVisible(false); + + mLoadState = LS_Loaded; +} + +void QuadTreeNode::unload(bool recursive) { if (mChunk) { - Ogre::MaterialManager::getSingleton().remove(mChunk->getMaterial()->getName()); mSceneNode->detachObject(mChunk); delete mChunk; mChunk = NULL; - // destroy blendmaps - if (mMaterialGenerator) - { - const std::vector& list = mMaterialGenerator->getBlendmapList(); - for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) - Ogre::TextureManager::getSingleton().remove((*it)->getName()); - mMaterialGenerator->setBlendmapList(std::vector()); - mMaterialGenerator->setLayerList(std::vector()); - mMaterialGenerator->setCompositeMap(""); - } if (!mCompositeMap.isNull()) { Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName()); mCompositeMap.setNull(); } + + // Do *not* set this when we are still loading! + mLoadState = LS_Unloaded; } - else if (children && hasChildren()) + + if (recursive && hasChildren()) + { for (int i=0; i<4; ++i) - mChildren[i]->destroyChunks(true); + mChildren[i]->unload(); + } } void QuadTreeNode::updateIndexBuffers() { if (hasChunk()) - mChunk->updateIndexBuffer(); + { + // Fetch a suitable index buffer (which may be shared) + size_t ourLod = getActualLodLevel(); + + int flags = 0; + + for (int i=0; i<4; ++i) + { + QuadTreeNode* neighbour = getNeighbour((Direction)i); + + // If the neighbour isn't currently rendering itself, + // go up until we find one. NOTE: We don't need to go down, + // because in that case neighbour's detail would be higher than + // our detail and the neighbour would handle stitching by itself. + while (neighbour && !neighbour->hasChunk()) + neighbour = neighbour->getParent(); + size_t lod = 0; + if (neighbour) + lod = neighbour->getActualLodLevel(); + if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - + lod = 0; // neighbours with more detail will do the stitching themselves + // Use 4 bits for each LOD delta + if (lod > 0) + { + assert (lod - ourLod < (1 << 4)); + flags |= int(lod - ourLod) << (4*i); + } + } + flags |= 0 /*((int)mAdditionalLod)*/ << (4*4); + + mChunk->setIndexBuffer(mTerrain->getBufferCache().getIndexBuffer(flags)); + } else if (hasChildren()) { for (int i=0; i<4; ++i) @@ -407,20 +491,56 @@ bool QuadTreeNode::hasChunk() size_t QuadTreeNode::getActualLodLevel() { assert(hasChunk() && "Can't get actual LOD level if this node has no render chunk"); - return mLodLevel + mChunk->getAdditionalLod(); + return mLodLevel /* + mChunk->getAdditionalLod() */; } -void QuadTreeNode::ensureLayerInfo() +void QuadTreeNode::loadLayers(const LayerCollection& collection) { - if (mMaterialGenerator->hasLayers()) + assert (!mMaterialGenerator->hasLayers()); + + std::vector blendTextures; + for (std::vector::const_iterator it = collection.mBlendmaps.begin(); it != collection.mBlendmaps.end(); ++it) + { + // TODO: clean up blend textures on destruction + static int count=0; + Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/" + + Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format); + + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true)); + map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format); + blendTextures.push_back(map); + } + + mMaterialGenerator->setLayerList(collection.mLayers); + mMaterialGenerator->setBlendmapList(blendTextures); +} + +void QuadTreeNode::loadMaterials() +{ + if (isDummy()) return; - std::vector blendmaps; - std::vector layerList; - mTerrain->getStorage()->getBlendmaps(mSize, mCenter, mTerrain->getShadersEnabled(), blendmaps, layerList); + // Load children first since we depend on them when creating a composite map + if (hasChildren()) + { + for (int i=0; i<4; ++i) + mChildren[i]->loadMaterials(); + } - mMaterialGenerator->setLayerList(layerList); - mMaterialGenerator->setBlendmapList(blendmaps); + if (mChunk) + { + if (mSize == 1) + { + mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + } + else + { + ensureCompositeMap(); + mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + } + } } void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) @@ -456,8 +576,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) } else { - ensureLayerInfo(); - + // TODO: when to destroy? Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT(Ogre::MaterialPtr()); makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material); } @@ -501,13 +620,3 @@ void QuadTreeNode::applyMaterials() for (int i=0; i<4; ++i) mChildren[i]->applyMaterials(); } - -void QuadTreeNode::setVisible(bool visible) -{ - if (!visible && mChunk) - mChunk->setVisible(false); - - if (hasChildren()) - for (int i=0; i<4; ++i) - mChildren[i]->setVisible(visible); -} diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index ea299c096..c57589487 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include "defs.hpp" namespace Ogre { @@ -17,14 +17,7 @@ namespace Terrain class World; class Chunk; class MaterialGenerator; - - enum Direction - { - North = 0, - East = 1, - South = 2, - West = 3 - }; + struct LoadResponseData; enum ChildDirection { @@ -35,6 +28,13 @@ namespace Terrain Root }; + enum LoadState + { + LS_Unloaded, + LS_Loading, + LS_Loaded + }; + /** * @brief A node in the quad tree for our terrain. Depending on LOD, * a node can either choose to render itself in one batch (merging its children), @@ -51,8 +51,6 @@ namespace Terrain QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent); ~QuadTreeNode(); - void setVisible(bool visible); - /// Rebuild all materials void applyMaterials(); @@ -95,10 +93,14 @@ namespace Terrain /// Get bounding box in local coordinates const Ogre::AxisAlignedBox& getBoundingBox(); + const Ogre::AxisAlignedBox& getWorldBoundingBox(); + World* getTerrain() { return mTerrain; } /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. - void update (const Ogre::Vector3& cameraPos, Loading::Listener* loadingListener); + /// @param force Always choose to render this node, even if not the perfect LOD. + /// @return Did we (or all of our children) choose to render? + bool update (const Ogre::Vector3& cameraPos); /// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided. /// Call after QuadTreeNode::update! @@ -120,17 +122,25 @@ namespace Terrain /// Add a textured quad to a specific 2d area in the composite map scenemanager. /// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply /// call this method on their children. + /// @note Do not call this before World::areLayersLoaded() == true /// @param area area in image space to put the quad /// @param quads collect quads here so they can be deleted later void prepareForCompositeMap(Ogre::TRect area); + /// Create a chunk for this node from the given data. + void load (const LoadResponseData& data); + void unload(bool recursive=false); + void loadLayers (const LayerCollection& collection); + /// This is recursive! Call it once on the root node after all leafs have loaded layers. + void loadMaterials(); + + LoadState getLoadState() { return mLoadState; } + private: // Stored here for convenience in case we need layer list again MaterialGenerator* mMaterialGenerator; - /// Is this node (or any of its child nodes) currently configured to render itself? - /// (only relevant when distant land is disabled, otherwise whole terrain is always rendered) - bool mIsActive; + LoadState mLoadState; bool mIsDummy; float mSize; @@ -152,7 +162,6 @@ namespace Terrain Ogre::TexturePtr mCompositeMap; - void ensureLayerInfo(); void ensureCompositeMap(); }; diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 021e01c7e..d3770ea57 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -1,24 +1,12 @@ #ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H -#include -#include - -#include - #include +#include "defs.hpp" + namespace Terrain { - - struct LayerInfo - { - std::string mDiffuseMap; - std::string mNormalMap; - bool mParallax; // Height info in normal map alpha channel? - bool mSpecular; // Specular info in diffuse map alpha channel? - }; - /// We keep storage of terrain data abstract here since we need different implementations for game and editor class Storage { @@ -27,10 +15,10 @@ namespace Terrain public: /// Get bounds of the whole terrain in cell units - virtual Ogre::AxisAlignedBox getBounds() = 0; + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; - /// Get the minimum and maximum heights of a terrain chunk. - /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Get the minimum and maximum heights of a terrain region. + /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. /// @param size size of the chunk in cell units /// @param center center of the chunk in cell units @@ -40,20 +28,23 @@ namespace Terrain virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max) = 0; /// Fill vertex buffers for a terrain chunk. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! + /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units - /// @param vertexBuffer buffer to write vertices - /// @param normalBuffer buffer to write vertex normals - /// @param colourBuffer buffer to write vertex colours - virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; + /// @param positions buffer to write vertices + /// @param normals buffer to write vertex normals + /// @param colours buffer to write vertex colours + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, + std::vector& positions, + std::vector& normals, + std::vector& colours) = 0; /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - @@ -62,9 +53,21 @@ namespace Terrain /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, + std::vector& blendmaps, std::vector& layerList) = 0; + /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information. + /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. + /// @note The terrain chunks shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! + /// @param nodes A collection of nodes for which to retrieve the aforementioned data + /// @param out Output vector + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack) = 0; + virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0; virtual LayerInfo getDefaultLayer() = 0; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index f4070393d..844144d7c 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -2,12 +2,12 @@ #include #include -#include #include +#include +#include +#include #include -#include - #include "storage.hpp" #include "quadtreenode.hpp" @@ -51,23 +51,38 @@ namespace namespace Terrain { - World::World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool distantLand, bool shaders) + const Ogre::uint REQ_ID_CHUNK = 1; + const Ogre::uint REQ_ID_LAYERS = 2; + + World::World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) : mStorage(storage) - , mMinBatchSize(1) - , mMaxBatchSize(64) + , mMinBatchSize(minBatchSize) + , mMaxBatchSize(maxBatchSize) , mSceneMgr(sceneMgr) , mVisibilityFlags(visibilityFlags) , mDistantLand(distantLand) , mShaders(shaders) , mVisible(true) - , mLoadingListener(loadingListener) + , mAlign(align) + , mMaxX(0) + , mMinX(0) + , mMaxY(0) + , mMinY(0) + , mChunksLoading(0) + , mWorkQueueChannel(0) + , mCache(storage->getCellVertices()) + , mLayerLoadPending(true) { - loadingListener->setLabel("Creating terrain"); - loadingListener->indicateProgress(); +#if TERRAIN_USE_SHADER == 0 + if (mShaders) + std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl; + mShaders = false; +#endif mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + /// \todo make composite map size configurable Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -76,35 +91,52 @@ namespace Terrain mCompositeMapRenderTarget->setAutoUpdated(false); mCompositeMapRenderTarget->addViewport(compositeMapCam); - mBounds = storage->getBounds(); + storage->getBounds(mMinX, mMaxX, mMinY, mMaxY); - int origSizeX = mBounds.getSize().x; - int origSizeY = mBounds.getSize().y; + int origSizeX = mMaxX-mMinX; + int origSizeY = mMaxY-mMinY; // Dividing a quad tree only works well for powers of two, so round up to the nearest one int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); // Adjust the center according to the new size - Ogre::Vector3 center = mBounds.getCenter() + Ogre::Vector3((size-origSizeX)/2.f, (size-origSizeY)/2.f, 0); + float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; + float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL); - buildQuadTree(mRootNode); - loadingListener->indicateProgress(); + // While building the quadtree, remember leaf nodes since we need to load their layers + LayersRequestData data; + data.mPack = getShadersEnabled(); + + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); + buildQuadTree(mRootNode, data.mNodes); + //loadingListener->indicateProgress(); mRootNode->initAabb(); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); mRootNode->initNeighbours(); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); + + Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); + mWorkQueueChannel = wq->getChannel("LargeTerrain"); + wq->addRequestHandler(mWorkQueueChannel, this); + wq->addResponseHandler(mWorkQueueChannel, this); + + // Start loading layers in the background (for leaf nodes) + wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data)); } World::~World() { + Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); + wq->removeRequestHandler(mWorkQueueChannel, this); + wq->removeResponseHandler(mWorkQueueChannel, this); + delete mRootNode; delete mStorage; } - void World::buildQuadTree(QuadTreeNode *node) + void World::buildQuadTree(QuadTreeNode *node, std::vector& leafs) { float halfSize = node->getSize()/2.f; @@ -115,17 +147,22 @@ namespace Terrain Ogre::Vector2 center = node->getCenter(); float cellWorldSize = getStorage()->getCellWorldSize(); if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) - node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), - Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ))); + { + Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), + Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); + convertBounds(bounds); + node->setBoundingBox(bounds); + leafs.push_back(node); + } else node->markAsDummy(); // no data available for this node, skip it return; } - if (node->getCenter().x - halfSize > mBounds.getMaximum().x - || node->getCenter().x + halfSize < mBounds.getMinimum().x - || node->getCenter().y - halfSize > mBounds.getMaximum().y - || node->getCenter().y + halfSize < mBounds.getMinimum().y ) + if (node->getCenter().x - halfSize > mMaxX + || node->getCenter().x + halfSize < mMinX + || node->getCenter().y - halfSize > mMaxY + || node->getCenter().y + halfSize < mMinY ) // Out of bounds of the actual terrain - this will happen because // we rounded the size up to the next power of two { @@ -138,10 +175,10 @@ namespace Terrain node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f)); node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f)); node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f); - buildQuadTree(node->getChild(SW)); - buildQuadTree(node->getChild(SE)); - buildQuadTree(node->getChild(NW)); - buildQuadTree(node->getChild(NE)); + buildQuadTree(node->getChild(SW), leafs); + buildQuadTree(node->getChild(SE), leafs); + buildQuadTree(node->getChild(NW), leafs); + buildQuadTree(node->getChild(NE), leafs); // if all children are dummy, we are also dummy for (int i=0; i<4; ++i) @@ -156,218 +193,19 @@ namespace Terrain { if (!mVisible) return; - mRootNode->update(cameraPos, mLoadingListener); + mRootNode->update(cameraPos); mRootNode->updateIndexBuffers(); } Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center) { - if (center.x > mBounds.getMaximum().x - || center.x < mBounds.getMinimum().x - || center.y > mBounds.getMaximum().y - || center.y < mBounds.getMinimum().y) + if (center.x > mMaxX + || center.x < mMinX + || center.y > mMaxY + || center.y < mMinY) return Ogre::AxisAlignedBox::BOX_NULL; QuadTreeNode* node = findNode(center, mRootNode); - Ogre::AxisAlignedBox box = node->getBoundingBox(); - float cellWorldSize = getStorage()->getCellWorldSize(); - box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize, - box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize); - return box; - } - - Ogre::HardwareVertexBufferSharedPtr World::getVertexBuffer(int numVertsOneSide) - { - if (mUvBufferMap.find(numVertsOneSide) != mUvBufferMap.end()) - { - return mUvBufferMap[numVertsOneSide]; - } - - int vertexCount = numVertsOneSide * numVertsOneSide; - - std::vector uvs; - uvs.reserve(vertexCount*2); - - for (int col = 0; col < numVertsOneSide; ++col) - { - for (int row = 0; row < numVertsOneSide; ++row) - { - uvs.push_back(col / static_cast(numVertsOneSide-1)); // U - uvs.push_back(row / static_cast(numVertsOneSide-1)); // V - } - } - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareVertexBufferSharedPtr buffer = mgr->createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - - buffer->writeData(0, buffer->getSizeInBytes(), &uvs[0], true); - - mUvBufferMap[numVertsOneSide] = buffer; - return buffer; - } - - Ogre::HardwareIndexBufferSharedPtr World::getIndexBuffer(int flags, size_t& numIndices) - { - unsigned int verts = mStorage->getCellVertices(); - - if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) - { - numIndices = mIndexBufferMap[flags]->getNumIndexes(); - return mIndexBufferMap[flags]; - } - - // LOD level n means every 2^n-th vertex is kept - size_t lodLevel = (flags >> (4*4)); - - size_t lodDeltas[4]; - for (int i=0; i<4; ++i) - lodDeltas[i] = (flags >> (4*i)) & (0xf); - - bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); - - size_t increment = 1 << lodLevel; - assert(increment < verts); - std::vector indices; - indices.reserve((verts-1)*(verts-1)*2*3 / increment); - - size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; - // If any edge needs stitching we'll skip all edges at this point, - // mainly because stitching one edge would have an effect on corners and on the adjacent edges - if (anyDeltas) - { - colStart += increment; - colEnd -= increment; - rowEnd -= increment; - rowStart += increment; - } - for (size_t row = rowStart; row < rowEnd; row += increment) - { - for (size_t col = colStart; col < colEnd; col += increment) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row+increment); - indices.push_back(verts*col+row+increment); - - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row); - indices.push_back(verts*(col+increment)+row+increment); - } - } - - size_t innerStep = increment; - if (anyDeltas) - { - // Now configure LOD transitions at the edges - this is pretty tedious, - // and some very long and boring code, but it works great - - // South - size_t row = 0; - size_t outerStep = 1 << (lodDeltas[South] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+outerStep)+row); - // Make sure not to touch the right edge - if (col+outerStep == verts-1) - indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); - else - indices.push_back(verts*(col+outerStep)+row+innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col)+row); - indices.push_back(verts*(col+i+innerStep)+row+innerStep); - indices.push_back(verts*(col+i)+row+innerStep); - } - } - - // North - row = verts-1; - outerStep = 1 << (lodDeltas[North] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*(col+outerStep)+row); - indices.push_back(verts*col+row); - // Make sure not to touch the left edge - if (col == 0) - indices.push_back(verts*(col+innerStep)+row-innerStep); - else - indices.push_back(verts*col+row-innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col+i)+row-innerStep); - indices.push_back(verts*(col+i+innerStep)+row-innerStep); - indices.push_back(verts*(col+outerStep)+row); - } - } - - // West - size_t col = 0; - outerStep = 1 << (lodDeltas[West] + lodLevel); - for (size_t row = 0; row < verts-1; row += outerStep) - { - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*col+row); - // Make sure not to touch the top edge - if (row+outerStep == verts-1) - indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); - else - indices.push_back(verts*(col+innerStep)+row+outerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row); - indices.push_back(verts*(col+innerStep)+row+i); - indices.push_back(verts*(col+innerStep)+row+i+innerStep); - } - } - - // East - col = verts-1; - outerStep = 1 << (lodDeltas[East] + lodLevel); - for (size_t row = 0; row < verts-1; row += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*col+row+outerStep); - // Make sure not to touch the bottom edge - if (row == 0) - indices.push_back(verts*(col-innerStep)+row+innerStep); - else - indices.push_back(verts*(col-innerStep)+row); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*(col-innerStep)+row+i+innerStep); - indices.push_back(verts*(col-innerStep)+row+i); - } - } - } - - - - numIndices = indices.size(); - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, - numIndices, Ogre::HardwareBuffer::HBU_STATIC); - buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); - mIndexBufferMap[flags] = buffer; - return buffer; + return node->getWorldBoundingBox(); } void World::renderCompositeMap(Ogre::TexturePtr target) @@ -409,5 +247,108 @@ namespace Terrain return mVisible; } + void World::convertPosition(float &x, float &y, float &z) + { + Terrain::convertPosition(mAlign, x, y, z); + } + void World::convertPosition(Ogre::Vector3 &pos) + { + convertPosition(pos.x, pos.y, pos.z); + } + + void World::convertBounds(Ogre::AxisAlignedBox& bounds) + { + switch (mAlign) + { + case Align_XY: + return; + case Align_XZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + // Because we changed sign of Z + std::swap(bounds.getMinimum().z, bounds.getMaximum().z); + return; + case Align_YZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + return; + } + } + + void World::syncLoad() + { + while (mChunksLoading || mLayerLoadPending) + { + OGRE_THREAD_SLEEP(0); + Ogre::Root::getSingleton().getWorkQueue()->processResponses(); + } + } + + Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ) + { + if (req->getType() == REQ_ID_CHUNK) + { + const LoadRequestData data = Ogre::any_cast(req->getData()); + + QuadTreeNode* node = data.mNode; + + LoadResponseData* responseData = new LoadResponseData(); + + getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(), + responseData->mPositions, responseData->mNormals, responseData->mColours); + + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } + else // REQ_ID_LAYERS + { + const LayersRequestData data = Ogre::any_cast(req->getData()); + + LayersResponseData* responseData = new LayersResponseData(); + + getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack); + + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } + } + + void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ) + { + assert(res->succeeded() && "Response failure not handled"); + + if (res->getRequest()->getType() == REQ_ID_CHUNK) + { + LoadResponseData* data = Ogre::any_cast(res->getData()); + + const LoadRequestData requestData = Ogre::any_cast(res->getRequest()->getData()); + + requestData.mNode->load(*data); + + delete data; + + --mChunksLoading; + } + else // REQ_ID_LAYERS + { + LayersResponseData* data = Ogre::any_cast(res->getData()); + + for (std::vector::iterator it = data->mLayerCollections.begin(); it != data->mLayerCollections.end(); ++it) + { + it->mTarget->loadLayers(*it); + } + + mRootNode->loadMaterials(); + + mLayerLoadPending = false; + } + } + + void World::queueLoad(QuadTreeNode *node) + { + LoadRequestData data; + data.mNode = node; + + Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data)); + ++mChunksLoading; + } } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index b8c1b0a7d..26a6d034d 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -1,15 +1,12 @@ #ifndef COMPONENTS_TERRAIN_H #define COMPONENTS_TERRAIN_H -#include -#include #include #include +#include -namespace Loading -{ - class Listener; -} +#include "defs.hpp" +#include "buffercache.hpp" namespace Ogre { @@ -29,11 +26,10 @@ namespace Terrain * Cracks at LOD transitions are avoided using stitching. * @note Multiple cameras are not supported yet */ - class World + class World : public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler { public: /// @note takes ownership of \a storage - /// @param loadingListener Listener to update with progress /// @param sceneMgr scene manager to use /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param visbilityFlags visibility flags for the created meshes @@ -41,12 +37,13 @@ namespace Terrain /// This is a temporary option until it can be streamlined. /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually /// faster so this is just here for compatibility. - World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, - Storage* storage, int visiblityFlags, bool distantLand, bool shaders); + /// @param align The align of the terrain, see Alignment enum + /// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree. + /// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree. + World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visiblityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize); ~World(); - void setLoadingListener(Loading::Listener* loadingListener) { mLoadingListener = loadingListener; } - bool getDistantLandEnabled() { return mDistantLand; } bool getShadersEnabled() { return mShaders; } bool getShadowsEnabled() { return mShadows; } @@ -86,14 +83,24 @@ namespace Terrain void enableSplattingShader(bool enabled); + Alignment getAlign() { return mAlign; } + + /// Wait until all background loading is complete. + void syncLoad(); + private: + // Called from a background worker thread + Ogre::WorkQueue::Response* handleRequest(const Ogre::WorkQueue::Request* req, const Ogre::WorkQueue* srcQ); + // Called from the main thread + void handleResponse(const Ogre::WorkQueue::Response* res, const Ogre::WorkQueue* srcQ); + Ogre::uint16 mWorkQueueChannel; + bool mDistantLand; bool mShaders; bool mShadows; bool mSplitShadows; bool mVisible; - - Loading::Listener* mLoadingListener; + Alignment mAlign; QuadTreeNode* mRootNode; Ogre::SceneNode* mRootSceneNode; @@ -101,54 +108,86 @@ namespace Terrain int mVisibilityFlags; + /// The number of chunks currently loading in a background thread. If 0, we have finished loading! + int mChunksLoading; + Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mCompositeMapSceneMgr; /// Bounds in cell units - Ogre::AxisAlignedBox mBounds; + float mMinX, mMaxX, mMinY, mMaxY; /// Minimum size of a terrain batch along one side (in cell units) float mMinBatchSize; /// Maximum size of a terrain batch along one side (in cell units) float mMaxBatchSize; - void buildQuadTree(QuadTreeNode* node); + void buildQuadTree(QuadTreeNode* node, std::vector& leafs); + + BufferCache mCache; + + // Are layers for leaf nodes loaded? This is done once at startup (but in a background thread) + bool mLayerLoadPending; public: // ----INTERNAL---- - - enum IndexBufferFlags - { - IBF_North = 1 << 0, - IBF_East = 1 << 1, - IBF_South = 1 << 2, - IBF_West = 1 << 3 - }; - - /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) - /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) - /// @param numIndices number of indices that were used will be written here - Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags, size_t& numIndices); - - Ogre::HardwareVertexBufferSharedPtr getVertexBuffer (int numVertsOneSide); - Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } + BufferCache& getBufferCache() { return mCache; } + + bool areLayersLoaded() { return !mLayerLoadPending; } // Delete all quads void clearCompositeMapSceneManager(); void renderCompositeMap (Ogre::TexturePtr target); + // Convert the given position from Z-up align, i.e. Align_XY to the wanted align set in mAlign + void convertPosition (float& x, float& y, float& z); + void convertPosition (Ogre::Vector3& pos); + void convertBounds (Ogre::AxisAlignedBox& bounds); + + // Adds a WorkQueue request to load a chunk for this node in the background. + void queueLoad (QuadTreeNode* node); + private: - // Index buffers are shared across terrain batches where possible. There is one index buffer for each - // combination of LOD deltas and index buffer LOD we may need. - std::map mIndexBufferMap; - - std::map mUvBufferMap; - Ogre::RenderTarget* mCompositeMapRenderTarget; Ogre::TexturePtr mCompositeMapRenderTexture; }; + struct LoadRequestData + { + QuadTreeNode* mNode; + + friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r) + { return o; } + }; + + struct LoadResponseData + { + std::vector mPositions; + std::vector mNormals; + std::vector mColours; + + friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r) + { return o; } + }; + + struct LayersRequestData + { + std::vector mNodes; + bool mPack; + + friend std::ostream& operator<<(std::ostream& o, const LayersRequestData& r) + { return o; } + }; + + struct LayersResponseData + { + std::vector mLayerCollections; + + friend std::ostream& operator<<(std::ostream& o, const LayersResponseData& r) + { return o; } + }; + } #endif diff --git a/credits.txt b/credits.txt index 561931cde..601255763 100644 --- a/credits.txt +++ b/credits.txt @@ -20,6 +20,7 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick +cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index 907cba5fc..a83ae539e 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -102,19 +102,19 @@ namespace ICS JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; // MouseListener - bool mouseMoved(const SFO::MouseMotionEvent &evt); - bool mousePressed(const SDL_MouseButtonEvent &evt, Uint8); - bool mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); + void mouseMoved(const SFO::MouseMotionEvent &evt); + void mousePressed(const SDL_MouseButtonEvent &evt, Uint8); + void mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); // KeyListener - bool keyPressed(const SDL_KeyboardEvent &evt); - bool keyReleased(const SDL_KeyboardEvent &evt); + void keyPressed(const SDL_KeyboardEvent &evt); + void keyReleased(const SDL_KeyboardEvent &evt); // JoyStickListener - bool buttonPressed(const SDL_JoyButtonEvent &evt, int button); - bool buttonReleased(const SDL_JoyButtonEvent &evt, int button); - bool axisMoved(const SDL_JoyAxisEvent &evt, int axis); - bool povMoved(const SDL_JoyHatEvent &evt, int index); + void buttonPressed(const SDL_JoyButtonEvent &evt, int button); + void buttonReleased(const SDL_JoyButtonEvent &evt, int button); + void axisMoved(const SDL_JoyAxisEvent &evt, int axis); + void povMoved(const SDL_JoyHatEvent &evt, int index); //TODO: does this have an SDL equivalent? //bool sliderMoved(const OIS::JoyStickEvent &evt, int index); diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index 8e501d501..35762d2b3 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -318,7 +318,7 @@ namespace ICS } // joyStick listeners - bool InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) + void InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { @@ -354,11 +354,9 @@ namespace ICS mDetectingBindingControl, evt.which, button, mDetectingBindingDirection); } } - - return true; } - bool InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) + void InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { @@ -371,10 +369,9 @@ namespace ICS } } } - return true; } - bool InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) + void InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) { if(mActive) { @@ -388,7 +385,7 @@ namespace ICS { ctrl->setIgnoreAutoReverse(true); - float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MAX; + float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MIN; float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN); if(joystickBinderItem.direction == Control::INCREASE) @@ -417,12 +414,10 @@ namespace ICS } } } - - return true; } //Here be dragons, apparently - bool InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) + void InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) { if(mActive) { @@ -542,13 +537,11 @@ namespace ICS } } } - - return true; } //TODO: does this have an SDL equivalent? /* - bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) + void InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) { if(mActive) { @@ -590,8 +583,6 @@ namespace ICS } } } - - return true; } */ diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp index 01d68f784..0a9a34d63 100644 --- a/extern/oics/ICSInputControlSystem_keyboard.cpp +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -85,7 +85,7 @@ namespace ICS return SDLK_UNKNOWN; } - bool InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) + void InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) { if(mActive) { @@ -118,11 +118,9 @@ namespace ICS mDetectingBindingControl, evt.keysym.sym, mDetectingBindingDirection); } } - - return true; - } + } - bool InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) + void InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) { if(mActive) { @@ -132,8 +130,6 @@ namespace ICS it->second.control->setChangingDirection(Control::STOP); } } - - return true; } void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp index 52eb894ed..be18ebbc0 100644 --- a/extern/oics/ICSInputControlSystem_mouse.cpp +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -219,7 +219,7 @@ namespace ICS } // mouse Listeners - bool InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) + void InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) { if(mActive) { @@ -304,11 +304,9 @@ namespace ICS } } } - - return true; } - bool InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) + void InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -341,11 +339,9 @@ namespace ICS mDetectingBindingControl, btn, mDetectingBindingDirection); } } - - return true; } - bool InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) + void InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -355,8 +351,6 @@ namespace ICS it->second.control->setChangingDirection(Control::STOP); } } - - return true; } // mouse auto bindings diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h index 48adb4545..0fb4d6f06 100644 --- a/extern/sdl4ogre/events.h +++ b/extern/sdl4ogre/events.h @@ -26,9 +26,9 @@ class MouseListener { public: virtual ~MouseListener() {} - virtual bool mouseMoved( const MouseMotionEvent &arg ) = 0; - virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; - virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual void mouseMoved( const MouseMotionEvent &arg ) = 0; + virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; }; class KeyListener @@ -36,8 +36,8 @@ class KeyListener public: virtual ~KeyListener() {} virtual void textInput (const SDL_TextInputEvent& arg) {} - virtual bool keyPressed(const SDL_KeyboardEvent &arg) = 0; - virtual bool keyReleased(const SDL_KeyboardEvent &arg) = 0; + virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0; + virtual void keyReleased(const SDL_KeyboardEvent &arg) = 0; }; class JoyListener @@ -45,18 +45,18 @@ class JoyListener public: virtual ~JoyListener() {} /** @remarks Joystick button down event */ - virtual bool buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; + virtual void buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; /** @remarks Joystick button up event */ - virtual bool buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; + virtual void buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; /** @remarks Joystick axis moved event */ - virtual bool axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; + virtual void axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; //-- Not so common control events, so are not required --// //! Joystick Event, and povID - virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;} + virtual void povMoved( const SDL_JoyHatEvent &arg, int index) {} }; class WindowListener diff --git a/extern/sdl4ogre/sdlcursormanager.cpp b/extern/sdl4ogre/sdlcursormanager.cpp index 65fb7f98b..5ef274b7e 100644 --- a/extern/sdl4ogre/sdlcursormanager.cpp +++ b/extern/sdl4ogre/sdlcursormanager.cpp @@ -1,6 +1,7 @@ #include "sdlcursormanager.hpp" #include +#include #include #include diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp index f819043cf..2a14cc6b4 100644 --- a/extern/sdl4ogre/sdlwindowhelper.cpp +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/extern/shiny/Main/Preprocessor.hpp b/extern/shiny/Main/Preprocessor.hpp index 7ee30ae7f..4eb533499 100644 --- a/extern/shiny/Main/Preprocessor.hpp +++ b/extern/shiny/Main/Preprocessor.hpp @@ -1,6 +1,8 @@ #ifndef SH_PREPROCESSOR_H #define SH_PREPROCESSOR_H +#include + #include #include diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp index f215f4ab7..ad8e6d2b0 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp @@ -1,5 +1,7 @@ #include "OgreTextureUnitState.hpp" +#include + #include #include diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 481b99bad..f124abb99 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -23,7 +23,7 @@ namespace Physic mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); Ogre::Quaternion inverse = mBoxRotation.Inverse(); - mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); + mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z); mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } @@ -85,8 +85,8 @@ namespace Physic Ogre::Quaternion PhysicActor::getRotation() { assert(mBody); - btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; - return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); + btQuaternion quat = mBody->getWorldTransform().getRotation(); + return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse; } void PhysicActor::setScale(float scale){ diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 6cd7244b8..4ef611dc8 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -162,7 +162,7 @@ namespace Physic Ogre::Vector3 mBoxScaledTranslation; Ogre::Quaternion mBoxRotation; - btQuaternion mBoxRotationInverse; + Ogre::Quaternion mBoxRotationInverse; Ogre::Vector3 mForce; bool mOnGround; diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 923b0b7e3..20b9296da 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace Ogre; diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp index 9c32924f1..cc5f572cf 100644 --- a/libs/openengine/ogre/imagerotate.cpp +++ b/libs/openengine/ogre/imagerotate.cpp @@ -8,6 +8,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include using namespace Ogre; using namespace OEngine::Render; diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index c86697497..c816f2060 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 69375b74d..5aeb35c28 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include + #include #include diff --git a/libs/openengine/ogre/selectionbuffer.hpp b/libs/openengine/ogre/selectionbuffer.hpp index c487b24b0..b9b4cd9d9 100644 --- a/libs/openengine/ogre/selectionbuffer.hpp +++ b/libs/openengine/ogre/selectionbuffer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace OEngine { diff --git a/libs/platform/stdint.h b/libs/platform/stdint.h deleted file mode 100644 index 00af741b1..000000000 --- a/libs/platform/stdint.h +++ /dev/null @@ -1,21 +0,0 @@ -// Wrapper for MSVC -#ifndef _STDINT_WRAPPER_H -#define _STDINT_WRAPPER_H - -#if (_MSC_VER >= 1600) - -#include - -#else - -#include - -// Pull the boost names into the global namespace for convenience -using boost::int32_t; -using boost::uint32_t; -using boost::int64_t; -using boost::uint64_t; - -#endif - -#endif diff --git a/readme.txt b/readme.txt index 045b18b8b..bb17a68e7 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.28.0 +Version: 0.29.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -97,6 +97,74 @@ Allowed options: CHANGELOG +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + 0.28.0 Bug #399: Inventory changes are not visible immediately