From 01ae91f6407c1e1090417432f3d79bef0b89b063 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Apr 2013 01:24:17 +0200 Subject: [PATCH 1/6] Fix several NPCs spawning in the ground --- apps/openmw/mwworld/scene.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 2244a4fc6..439f76131 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -118,6 +118,7 @@ namespace MWWorld float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; + // Load terrain physics first... if (cell->mCell->isExterior()) { ESM::Land* land = @@ -137,6 +138,7 @@ namespace MWWorld } } + // ... then references. This is important for adjustPosition to work correctly. /// \todo rescale depending on the state of a new GMST insertCell (*cell, true); @@ -439,7 +441,6 @@ namespace MWWorld insertCellRefList(mRendering, cell.mBooks, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mClothes, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale); - insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale); @@ -447,11 +448,13 @@ namespace MWWorld insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mMiscItems, cell, *mPhysics, rescale); - insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mProbes, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mRepairs, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mStatics, cell, *mPhysics, rescale); insertCellRefList(mRendering, cell.mWeapons, cell, *mPhysics, rescale); + // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) + insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale); } void Scene::addObjectToScene (const Ptr& ptr) From ec6dff38b187691b809c4b90ac15c27a972d4da2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Apr 2013 11:40:36 +0200 Subject: [PATCH 2/6] added basic spell table --- apps/opencs/model/world/data.cpp | 19 +++++++++++++++++++ apps/opencs/model/world/data.hpp | 6 ++++++ apps/opencs/model/world/universalid.cpp | 2 ++ apps/opencs/model/world/universalid.hpp | 4 +++- apps/opencs/view/doc/view.cpp | 9 +++++++++ apps/opencs/view/doc/view.hpp | 2 ++ apps/opencs/view/world/subviews.cpp | 1 + components/esm/loadspel.cpp | 10 ++++++++++ components/esm/loadspel.hpp | 3 +++ 9 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index b385c5b4c..af2b17bd9 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -99,6 +99,13 @@ CSMWorld::Data::Data() mBirthsigns.addColumn (new TextureColumn); mBirthsigns.addColumn (new DescriptionColumn); + mSpells.addColumn (new StringIdColumn); + mSpells.addColumn (new RecordStateColumn); + mSpells.addColumn (new NameColumn); + mSpells.addColumn (new FlagColumn ("Autocalc", 0x1)); + mSpells.addColumn (new FlagColumn ("Starter Spell", 0x2)); + mSpells.addColumn (new FlagColumn ("Always Succeeds", 0x4)); + addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill); @@ -109,6 +116,7 @@ CSMWorld::Data::Data() addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script); addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); + addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); } CSMWorld::Data::~Data() @@ -217,6 +225,16 @@ CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() return mBirthsigns; } +const CSMWorld::IdCollection& CSMWorld::Data::getSpells() const +{ + return mSpells; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSpells() +{ + return mSpells; +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); @@ -261,6 +279,7 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_SCPT: mScripts.load (reader, base); break; case ESM::REC_REGN: mRegions.load (reader, base); break; case ESM::REC_BSGN: mBirthsigns.load (reader, base); break; + case ESM::REC_SPEL: mSpells.load (reader, base); break; default: diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 122e855d8..d7b69ba5e 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "idcollection.hpp" #include "universalid.hpp" @@ -36,6 +37,7 @@ namespace CSMWorld IdCollection mScripts; IdCollection mRegions; IdCollection mBirthsigns; + IdCollection mSpells; std::vector mModels; std::map mModelIndex; @@ -92,6 +94,10 @@ namespace CSMWorld IdCollection& getBirthsigns(); + const IdCollection& getSpells() const; + + IdCollection& getSpells(); + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 6d305d6c0..11f487788 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -27,6 +27,7 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -43,6 +44,7 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 0586719f1..5586b22e7 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -53,7 +53,9 @@ namespace CSMWorld Type_Regions, Type_Region, Type_Birthsigns, - Type_Birthsign + Type_Birthsign, + Type_Spells, + Type_Spell }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index af9b81420..dfdcb1036 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -121,6 +121,10 @@ void CSVDoc::View::setupWorldMenu() QAction *birthsigns = new QAction (tr ("Birthsigns"), this); connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); world->addAction (birthsigns); + + QAction *spells = new QAction (tr ("Spells"), this); + connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); + world->addAction (spells); } void CSVDoc::View::setupUi() @@ -316,6 +320,11 @@ void CSVDoc::View::addBirthsignsSubView() addSubView (CSMWorld::UniversalId::Type_Birthsigns); } +void CSVDoc::View::addSpellsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Spells); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 12944e569..9241efbb9 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -131,6 +131,8 @@ namespace CSVDoc void addRegionsSubView(); void addBirthsignsSubView(); + + void addSpellsSubView(); }; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 23c66319c..c9ef4df8d 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -24,6 +24,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Scripts, CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, + CSMWorld::UniversalId::Type_Spells, CSMWorld::UniversalId::Type_None // end marker }; diff --git a/components/esm/loadspel.cpp b/components/esm/loadspel.cpp index b0f1ca64b..8149fe4ce 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -20,4 +20,14 @@ void Spell::save(ESMWriter &esm) mEffects.save(esm); } + void Spell::blank() + { + mData.mType = 0; + mData.mCost = 0; + mData.mFlags = 0; + + mName.clear(); + + mEffects.mList.clear(); + } } diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index 0d5e0be52..3a620962d 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -43,6 +43,9 @@ struct Spell void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif From ae0a7d5bcde2e15b228b388f9e693b1453378a36 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Apr 2013 11:53:47 +0200 Subject: [PATCH 3/6] added cost and type columns to spell table --- apps/opencs/model/world/columnbase.hpp | 3 +- apps/opencs/model/world/columns.hpp | 48 ++++++++++++++++++++++++++ apps/opencs/model/world/data.cpp | 2 ++ apps/opencs/view/doc/viewmanager.cpp | 8 +++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 5c2ce8a67..23049164f 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -34,7 +34,8 @@ namespace CSMWorld Display_GlobalVarType, Display_Specialisation, Display_Attribute, - Display_Boolean + Display_Boolean, + Display_SpellType }; std::string mTitle; diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index fbc533779..6d6d1b1ef 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -675,6 +675,54 @@ namespace CSMWorld return true; } }; + + template + struct SpellTypeColumn : public Column + { + SpellTypeColumn() : Column ("Type", ColumnBase::Display_SpellType) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mType; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mType = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct CostColumn : public Column + { + CostColumn() : Column ("Cost", ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mCost; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mCost = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index af2b17bd9..84b49b6bc 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -102,6 +102,8 @@ CSMWorld::Data::Data() mSpells.addColumn (new StringIdColumn); mSpells.addColumn (new RecordStateColumn); mSpells.addColumn (new NameColumn); + mSpells.addColumn (new SpellTypeColumn); + mSpells.addColumn (new CostColumn); mSpells.addColumn (new FlagColumn ("Autocalc", 0x1)); mSpells.addColumn (new FlagColumn ("Starter Spell", 0x2)); mSpells.addColumn (new FlagColumn ("Always Succeeds", 0x4)); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 3be7228b3..050bd51fe 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -49,6 +49,11 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) "Luck", 0 }; + static const char *sSpellTypes[] = + { + "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 + }; + mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType, @@ -62,6 +67,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute, new CSVWorld::EnumDelegateFactory (sAttributes, true)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_SpellType, + new CSVWorld::EnumDelegateFactory (sSpellTypes)); } CSVDoc::ViewManager::~ViewManager() From 40bb772e34aedf01c472aad93147c3a81a5a983d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Apr 2013 12:44:49 +0200 Subject: [PATCH 4/6] added verifier for spell record --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/spellcheck.cpp | 35 ++++++++++++++++++++++++++ apps/opencs/model/tools/spellcheck.hpp | 29 +++++++++++++++++++++ apps/opencs/model/tools/tools.cpp | 3 +++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/model/tools/spellcheck.cpp create mode 100644 apps/opencs/model/tools/spellcheck.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 87221d7af..76d4280c8 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -36,7 +36,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck - birthsigncheck + birthsigncheck spellcheck ) diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp new file mode 100644 index 000000000..3adee0a4e --- /dev/null +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -0,0 +1,35 @@ + +#include "spellcheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection& spells) +: mSpells (spells) +{} + +int CSMTools::SpellCheckStage::setup() +{ + return mSpells.getSize(); +} + +void CSMTools::SpellCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Spell& spell = mSpells.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); + + // test for empty name and description + if (spell.mName.empty()) + messages.push_back (id.toString() + "|" + spell.mId + " has an empty name"); + + // test for invalid cost values + if (spell.mData.mCost<0) + messages.push_back (id.toString() + "|" + spell.mId + " has a negative spell costs"); + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp new file mode 100644 index 000000000..056639219 --- /dev/null +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SPELLCHECK_H +#define CSM_TOOLS_SPELLCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that spell records are internally consistent + class SpellCheckStage : public Stage + { + const CSMWorld::IdCollection& mSpells; + + public: + + SpellCheckStage (const CSMWorld::IdCollection& spells); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 78796de41..803861203 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -19,6 +19,7 @@ #include "soundcheck.hpp" #include "regioncheck.hpp" #include "birthsigncheck.hpp" +#include "spellcheck.hpp" CSMTools::Operation *CSMTools::Tools::get (int type) { @@ -72,6 +73,8 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() mVerifier->appendStage (new RegionCheckStage (mData.getRegions())); mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + + mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); } return mVerifier; From 6ca2b1af743b659e44b9601f99c985a92a66e99a Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Tue, 9 Apr 2013 19:24:41 +0100 Subject: [PATCH 5/6] fix for turning animations playing when in vanity mode --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 13 +++++++++++-- apps/openmw/mwrender/renderingmanager.cpp | 10 ++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 040dc703c..39e985890 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -319,6 +319,7 @@ namespace MWBase virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; virtual void changeVanityModeScale(float factor) = 0; + virtual bool vanityRotateCamera(float * rot) = 0; virtual void renderPlayer() = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c23106be1..6a34161e0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -533,8 +533,17 @@ namespace MWInput float scale = MWBase::Environment::get().getFrameDuration(); if(scale <= 0.0f) scale = 1.0f; - mPlayer.setYaw(x/scale); - mPlayer.setPitch(-y/scale); + float rot[3]; + rot[0] = -y; + rot[1] = 0.0f; + rot[2] = x; + + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) + { + mPlayer.setYaw(x/scale); + mPlayer.setPitch(-y/scale); + } if (arg.state.Z.rel) MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.state.Z.rel); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index cd5ab19b9..029cf394b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -891,6 +891,16 @@ void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float mPlayer->getSightAngles(pitch, yaw); } +bool RenderingManager::vanityRotateCamera(float* rot) +{ + if(!mPlayer->isVanityOrPreviewModeEnabled()) + return false; + + Ogre::Vector3 vRot(rot); + mPlayer->rotateCamera(vRot, true); + return true; +} + void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) { return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b343a60bd..cece7a95f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -88,6 +88,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->setCameraDistance(-factor/120.f*10, true, true); } + bool vanityRotateCamera(float* rot); + void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); void attachCameraTo(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ae9b7a06b..11ccd8f2f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1364,6 +1364,11 @@ namespace MWWorld return physactor && physactor->getOnGround(); } + bool World::vanityRotateCamera(float * rot) + { + return mRendering->vanityRotateCamera(rot); + } + void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5ae87a1ff..7b12babee 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -361,6 +361,8 @@ namespace MWWorld mRendering->changeVanityModeScale(factor); } + virtual bool vanityRotateCamera(float * rot); + virtual void renderPlayer(); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); From 0e7d555cdf5ebd475792ffe13c0ad7d653288267 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Apr 2013 20:31:00 +0200 Subject: [PATCH 6/6] Terrain material now uses multiple passes if required, which means it can support an arbitrary number of layers. Also re-enables PSSM. --- apps/openmw/mwgui/settingswindow.cpp | 4 +- apps/openmw/mwrender/shadows.cpp | 5 +- apps/openmw/mwrender/terrainmaterial.cpp | 161 ++++++++++++++++------- apps/openmw/mwrender/terrainmaterial.hpp | 3 + files/materials/shadowcaster.shader | 3 +- files/materials/terrain.shader | 33 ++++- 6 files changed, 148 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 58754472d..5fb6e5000 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -236,9 +236,7 @@ namespace MWGui mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); - //mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); - mShadowsLargeDistance->setCaptionWithReplacing("#{sOff}"); - mShadowsLargeDistance->setEnabled (false); + mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsEnabledButton->setCaptionWithReplacing(Settings::Manager::getBool("enabled", "Shadows") ? "#{sOn}" : "#{sOff}"); mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 595a82294..0d066a0ec 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -28,10 +28,7 @@ void Shadows::recreate() { bool enabled = Settings::Manager::getBool("enabled", "Shadows"); - // Split shadow maps are currently disabled because the terrain cannot cope with them - // (Too many texture units) Solution would be a multi-pass terrain material - //bool split = Settings::Manager::getBool("split", "Shadows"); - const bool split = false; + bool split = Settings::Manager::getBool("split", "Shadows"); sh::Factory::getInstance ().setGlobalSetting ("shadows", enabled && !split ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("shadows_pssm", enabled && split ? "true" : "false"); diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 8a568883d..dd74254be 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -68,59 +68,113 @@ namespace MWRender Ogre::MaterialManager::getSingleton().remove(matName); mMaterial = sh::Factory::getInstance().createMaterialInstance (matName); - mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); - sh::MaterialInstancePass* p = mMaterial->createPass (); + int numPasses = getRequiredPasses(terrain); + int maxLayersInOnePass = getMaxLayersPerPass(terrain); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); - p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); - - p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); - - // global colour map - sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); - colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); - colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - - // global normal map - sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); - normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); - normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - - Ogre::uint maxLayers = getMaxLayers(terrain); - Ogre::uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - Ogre::uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - - p->mShaderProperties.setProperty ("num_layers", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numLayers)))); - p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); - - // blend maps - for (Ogre::uint i = 0; i < numBlendTextures; ++i) + for (int pass=0; passcreateTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); - blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(i)))); - blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - } + int layerOffset = maxLayersInOnePass * pass; + int blendmapOffset = (pass == 0) ? 1 : 0; // the first layer of the first pass is the base layer and does not need a blend map - // layer maps - for (Ogre::uint i = 0; i < numLayers; ++i) - { - sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); - diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); - p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), - sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4)) + "." + getComponent(int((i-1) % 4))))); - } + sh::MaterialInstancePass* p = mMaterial->createPass (); - // shadow - for (Ogre::uint i = 0; i < 3; ++i) - { - sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); - shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); - } + p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); + p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); + if (pass != 0) + { + p->setProperty ("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); + // Only write if depth is equal to the depth value written by the previous pass. + p->setProperty ("depth_func", sh::makeProperty(new sh::StringValue("equal"))); + } - p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty(new sh::StringValue( - Ogre::StringConverter::toString(numBlendTextures + numLayers + 2)))); + p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); + p->mShaderProperties.setProperty ("is_first_pass", sh::makeProperty(new sh::BooleanValue(pass == 0))); + + // global colour map + sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); + colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); + colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + // global normal map + sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); + normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); + normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + Ogre::uint numLayersInThisPass = std::min(maxLayersInOnePass, terrain->getLayerCount()-layerOffset); + + // HACK: Terrain::getLayerBlendTextureIndex should be const, but it is not. + // Remove this once ogre got fixed. + Ogre::Terrain* nonconstTerrain = const_cast(terrain); + + // a blend map might be shared between two passes + // so we can't just use terrain->getBlendTextureCount() + Ogre::uint numBlendTextures=0; + std::vector blendTextures; + for (unsigned int layer=blendmapOffset; layergetBlendTextureName(nonconstTerrain->getLayerBlendTextureIndex( + static_cast(layerOffset+layer)).first); + if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) + { + blendTextures.push_back(blendTextureName); + ++numBlendTextures; + } + } + + p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numLayersInThisPass)))); + p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); + + // blend maps + // the index of the first blend map used in this pass + int blendmapStart; + if (terrain->getLayerCount() == 1) // special case. if there's only one layer, we don't need blend maps at all + blendmapStart = 0; + else + blendmapStart = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+blendmapOffset)).first; + for (Ogre::uint i = 0; i < numBlendTextures; ++i) + { + sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); + blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(blendmapStart+i)))); + blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + } + + // layer maps + for (Ogre::uint i = 0; i < numLayersInThisPass; ++i) + { + sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); + diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(layerOffset+i, 0)))); + + if (i+layerOffset > 0) + { + int blendTextureIndex = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+i)).first; + int blendTextureComponent = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+i)).second; + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(blendTextureIndex-blendmapStart) + "." + getComponent(blendTextureComponent)))); + } + else + { + // just to make it shut up about blendmap_component_0 not existing in the first pass. + // it might be retrieved, but will never survive the preprocessing step. + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(""))); + } + } + + // shadow + for (Ogre::uint i = 0; i < 3; ++i) + { + sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); + shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); + } + + p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty (new sh::StringValue( + Ogre::StringConverter::toString(numBlendTextures + numLayersInThisPass + 2)))); + + // make sure the pass index is fed to the permutation handler, because blendmap components may be different + p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(pass))); + } return Ogre::MaterialManager::getSingleton().getByName(matName); } @@ -142,6 +196,11 @@ namespace MWRender } Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const + { + return 255; + } + + int TerrainMaterial::Profile::getMaxLayersPerPass (const Ogre::Terrain* terrain) { // count the texture units free Ogre::uint8 freeTextureUnits = 16; @@ -151,11 +210,21 @@ namespace MWRender --freeTextureUnits; // shadow --freeTextureUnits; + --freeTextureUnits; + --freeTextureUnits; // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) return static_cast(freeTextureUnits / (1.25f)); } + int TerrainMaterial::Profile::getRequiredPasses (const Ogre::Terrain* terrain) + { + int maxLayersPerPass = getMaxLayersPerPass(terrain); + assert(terrain->getLayerCount()); + assert(maxLayersPerPass); + return std::ceil(static_cast(terrain->getLayerCount()) / maxLayersPerPass); + } + void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) { } diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index fe1214cf5..c90499bae 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -72,6 +72,9 @@ namespace MWRender private: sh::MaterialInstance* mMaterial; + int getRequiredPasses (const Ogre::Terrain* terrain); + int getMaxLayersPerPass (const Ogre::Terrain* terrain); + bool mGlobalColourMap; }; diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index b312d414c..b992366a7 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -45,8 +45,7 @@ // use alpha channel of the first texture float alpha = shSample(texture1, UV).a; - // discard if alpha is less than 0.5 - if (alpha < 1.0) + if (alpha < 0.5) discard; #endif diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index c73b582f8..de90a6cf6 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -1,6 +1,6 @@ #include "core.h" -#define IS_FIRST_PASS 1 +#define IS_FIRST_PASS (@shPropertyString(pass_index) == 0) #define FOG @shGlobalSettingBool(fog) @@ -23,6 +23,9 @@ #define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) +#if !IS_FIRST_PASS +// This is not the first pass. +#endif #if NEED_DEPTH @shAllocatePassthrough(1, depth) @@ -222,7 +225,11 @@ float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); #endif - + +#if !IS_FIRST_PASS +float combinedAlpha = 0.f; +#endif + // Layer calculations @shForeach(@shPropertyString(num_blendmaps)) float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @@ -232,12 +239,20 @@ @shForeach(@shPropertyString(num_layers)) -#if IS_FIRST_PASS == 1 && @shIterator == 0 - // first layer of first pass doesn't need a blend map +#if IS_FIRST_PASS + #if @shIterator == 0 + // first layer of first pass is the base layer and doesn't need a blend map albedo = shSample(diffuseMap0, UV * 10).rgb; -#else + #else albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); - + #endif +#else + #if @shIterator == 0 + albedo = shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator); + #else + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + #endif + combinedAlpha += blendValues@shPropertyString(blendmap_component_@shIterator); #endif @shEndForeach @@ -325,6 +340,12 @@ // prevent negative colour output (for example with negative lights) shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); + +#if IS_FIRST_PASS + shOutputColour(0).a = 1; +#else + shOutputColour(0).a = min(combinedAlpha, 1.f); +#endif } #endif