Merge remote-tracking branch 'scrawl/master'

actorid
Marc Zinnschlag 11 years ago
commit 60a8a9e684

@ -219,7 +219,7 @@ bool Launcher::MainDialog::showFirstRunDialog()
}
// Create the file if it doesn't already exist, else the importer will fail
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg");
QString path = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + QString("openmw.cfg");
QFile file(path);
if (!file.exists()) {
@ -334,7 +334,7 @@ bool Launcher::MainDialog::setupLauncherSettings()
{
mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QStringList paths;
paths.append(QString("launcher.cfg"));
@ -440,7 +440,7 @@ bool Launcher::expansions(Launcher::UnshieldThread& cd)
bool Launcher::MainDialog::setupGameSettings()
{
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
// Load the user config file first, separately
@ -591,7 +591,7 @@ bool Launcher::MainDialog::setupGraphicsSettings()
{
mGraphicsSettings.setMultiValueEnabled(false);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
QFile localDefault(QString("settings-default.cfg"));
@ -678,7 +678,7 @@ bool Launcher::MainDialog::writeSettings()
mGraphicsPage->saveSettings();
mDataFilesPage->saveSettings();
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QDir dir(userPath);
if (!dir.exists()) {

@ -16,7 +16,7 @@ MwIniImporter::MwIniImporter()
const char *map[][2] =
{
{ "fps", "General:Show FPS" },
{ "nosound", "General:Disable Audio" },
{ "no-sound", "General:Disable Audio" },
{ 0, 0 }
};
const char *fallback[] = {

@ -86,10 +86,6 @@ void CS::Editor::setupDataFiles()
return;
}
// Set the charset for reading the esm/esp files
// QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
//mFileDialog.setEncoding(encoding);
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
mDocumentManager.setResourceDir (variables["resources"].as<std::string>());

@ -42,10 +42,10 @@ int main(int argc, char *argv[])
// 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 :(
OgreInit::OgreInit ogreInit;
ogreInit.init("./opencsOgre.log"); // TODO log path?
Application mApplication (argc, argv);
OgreInit::OgreInit ogreInit;
ogreInit.init("./opencsOgre.log"); // TODO log path?
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());

@ -2221,7 +2221,7 @@ void CSMDoc::Document::createBase()
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_)
: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
mProjectPath ((configuration.getUserPath() / "projects") /
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
{
@ -2254,7 +2254,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co
}
else
{
boost::filesystem::path locCustomFiltersPath (configuration.getUserPath());
boost::filesystem::path locCustomFiltersPath (configuration.getUserDataPath());
locCustomFiltersPath /= "defaultfilters";
if (boost::filesystem::exists(locCustomFiltersPath))

@ -15,7 +15,7 @@
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration)
{
boost::filesystem::path projectPath = configuration.getUserPath() / "projects";
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
if (!boost::filesystem::is_directory (projectPath))
boost::filesystem::create_directories (projectPath);
@ -53,4 +53,4 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
{
mResDir = boost::filesystem::system_complete(parResDir);
}
}

@ -251,7 +251,7 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName)
bool localOk = loadFromFile(localFilePath);
//user
mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName;
mUserFilePath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + fileName;
loadFromFile(mUserFilePath);
if (!(localOk || globalOk))

@ -20,7 +20,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows
characterpreview externalrendering globalmap videoplayer ripplesimulation refraction
terrainstorage
terrainstorage renderconst
)
add_openmw_dir (mwinput

@ -65,10 +65,6 @@ void OMW::Engine::executeLocalScripts()
localScripts.setIgnore (MWWorld::Ptr());
}
void OMW::Engine::setAnimationVerbose(bool animverbose)
{
}
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
{
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
@ -301,7 +297,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed.");
// load user settings if they exist, otherwise just load the default settings as user settings
const std::string settingspath = mCfgMgr.getUserPath().string() + "/settings.cfg";
const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg";
if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath);
else if (boost::filesystem::exists(localdefault))
@ -373,7 +369,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string();
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab);
mEnvironment.setInputManager (input);
@ -536,7 +532,7 @@ void OMW::Engine::screenshot()
// Count screenshots.
int shotCount = 0;
const std::string screenshotPath = mCfgMgr.getUserPath().string();
const std::string& screenshotPath = mCfgMgr.getUserDataPath().string();
// Find the first unused filename with a do-while
std::ostringstream stream;

@ -171,8 +171,6 @@ namespace OMW
/// Font encoding
void setEncoding(const ToUTF8::FromType& encoding);
void setAnimationVerbose(bool animverbose);
void setFallbackValues(std::map<std::string,std::string> map);
/// Enable console-only script functionality

@ -121,10 +121,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon")
("anim-verbose", bpo::value<bool>()->implicit_value(true)
->default_value(false), "output animation indices files")
("nosound", bpo::value<bool>()->implicit_value(true)
("no-sound", bpo::value<bool>()->implicit_value(true)
->default_value(false), "disable all sounds")
("script-verbose", bpo::value<bool>()->implicit_value(true)
@ -168,8 +165,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
bpo::store(valid_opts, variables);
bpo::notify(variables);
cfgMgr.readConfiguration(variables, desc);
bool run = true;
if (variables.count ("help"))
@ -187,6 +182,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
if (!run)
return false;
cfgMgr.readConfiguration(variables, desc);
engine.setGrabMouse(!variables.count("no-grab"));
// Font encoding settings
@ -237,10 +234,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setNewGame(variables["new-game"].as<bool>());
// other settings
engine.setSoundUsage(!variables["nosound"].as<bool>());
engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
engine.setCompileAll(variables["script-all"].as<bool>());
engine.setAnimationVerbose(variables["anim-verbose"].as<bool>());
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
engine.setScriptConsoleMode (variables["script-console"].as<bool>());
engine.setStartupScript (variables["script-run"].as<std::string>());

@ -141,15 +141,15 @@ void MWBase::Environment::cleanup()
delete mScriptManager;
mScriptManager = 0;
delete mWindowManager;
mWindowManager = 0;
delete mWorld;
mWorld = 0;
delete mSoundManager;
mSoundManager = 0;
delete mWindowManager;
mWindowManager = 0;
delete mInputManager;
mInputManager = 0;
}

@ -204,6 +204,7 @@ namespace MWBase
virtual void activateQuickKey (int index) = 0;
virtual std::string getSelectedSpell() = 0;
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;
virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0;
virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0;

@ -414,6 +414,13 @@ namespace MWBase
virtual bool toggleGodMode() = 0;
/**
* @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.
* @param actor
* @return true if the spell can be casted (i.e. the animation should start)
*/
virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0;
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,

@ -466,7 +466,7 @@ namespace MWGui
std::string CreateClassDialog::getName() const
{
return mEditName->getOnlyText();
return mEditName->getCaption();
}
std::string CreateClassDialog::getDescription() const

@ -228,8 +228,8 @@ namespace MWGui
DescriptionDialog();
~DescriptionDialog();
std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; }
void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); }
std::string getTextInput() const { return mTextEdit->getCaption(); }
void setTextInput(const std::string &text) { mTextEdit->setCaption(text); }
protected:
void onOkClicked(MyGUI::Widget* _sender);

@ -406,13 +406,14 @@ namespace MWGui
setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")");
mPtr = object;
}
// User clicked on an object. Restore focus to the console command line.
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);
}
else
{
setTitle("#{sConsoleTitle}");
mPtr = MWWorld::Ptr();
}
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);
}
void Console::onReferenceUnavailable()

@ -61,8 +61,9 @@ namespace MWGui
mDraggedWidget = baseWidget;
MyGUI::ImageBox* image = baseWidget->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
int pos = path.rfind(".");
path.erase(pos);
size_t pos = path.rfind(".");
if (pos != std::string::npos)
path.erase(pos);
path.append(".dds");
image->setImageTexture(path);
image->setNeedMouseFocus(false);

@ -120,10 +120,13 @@ namespace MWGui
Settings::Manager::getFloat(setting + " y", "Windows") * viewSize.height);
MyGUI::IntSize size (Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width,
Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height);
if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight())
mPreviewDirty = true;
mMainWidget->setPosition(pos);
mMainWidget->setSize(size);
adjustPanes();
mPreviewDirty = true;
}
TradeItemModel* InventoryWindow::getTradeModel()

@ -269,13 +269,39 @@ namespace MWGui
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
if (type == Type_Item || type == Type_MagicItem)
{
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() < 1)
{
// Try searching for a compatible replacement
std::string id = item.getCellRef().mRefID;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, id))
{
item = *it;
button->getChildAt(0)->setUserData(item);
break;
}
}
if (item.getRefData().getCount() < 1)
{
// No replacement was found
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item));
return;
}
}
}
if (type == Type_Magic)
{
std::string spellId = button->getChildAt(0)->getUserString("Spell");
spells.setSelectedSpell(spellId);
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
}
@ -283,15 +309,6 @@ namespace MWGui
{
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() < 1)
{
// TODO: Try to find a replacement with the same ID?
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item));
return;
}
boost::shared_ptr<MWWorld::Action> action = MWWorld::Class::get(item).use(item);
action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
@ -310,14 +327,6 @@ namespace MWGui
{
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() == 0)
{
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item));
return;
}
// retrieve ContainerStoreIterator to the item
MWWorld::ContainerStoreIterator it = store.begin();
for (; it != store.end(); ++it)
@ -336,13 +345,9 @@ namespace MWGui
MWWorld::ActionEquip action(item);
action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ());
// since we changed equipping status, update the inventory window
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
}
store.setSelectedEnchantItem(it);
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item);
}
}

@ -86,61 +86,8 @@ namespace MWGui
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
// the following code switches between selected enchanted item and selected spell (only one of these
// can be active at a time)
std::string selectedSpell = spells.getSelectedSpell();
MWWorld::Ptr selectedItem;
if (store.getSelectedEnchantItem() != store.end())
{
selectedSpell = "";
selectedItem = *store.getSelectedEnchantItem();
bool allowSelectedItem = true;
// make sure that the item is still in the player inventory, otherwise it can't be selected
bool found = false;
for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it)
{
if (*it == selectedItem)
found = true;
}
if (!found)
allowSelectedItem = false;
// if the selected item can be equipped, make sure that it actually is equipped
std::pair<std::vector<int>, bool> slots_;
slots_ = MWWorld::Class::get(selectedItem).getEquipmentSlots(selectedItem);
if (!slots_.first.empty())
{
bool equipped = false;
for (int i=0; i < MWWorld::InventoryStore::Slots; ++i)
{
if (store.getSlot(i) != store.end() && *store.getSlot(i) == selectedItem)
{
equipped = true;
break;
}
}
if (!equipped)
allowSelectedItem = false;
}
if (!allowSelectedItem)
{
store.setSelectedEnchantItem(store.end());
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->unsetSelectedSpell();
selectedItem = MWWorld::Ptr();
}
}
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
spellList.push_back (it->first);
}
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
@ -210,7 +157,7 @@ namespace MWGui
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected);
if (*it == selectedSpell)
if (*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell())
t->setStateSelected(true);
mHeight += spellHeight;
@ -229,7 +176,7 @@ namespace MWGui
t->setUserString("Spell", *it);
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected);
t->setStateSelected(*it == selectedSpell);
t->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell());
// cost / success chance
MyGUI::Button* costChance = mSpellView->createWidget<MyGUI::Button>("SpellText",
@ -239,7 +186,7 @@ namespace MWGui
costChance->setCaption(cost + "/" + chance);
costChance->setTextAlign(MyGUI::Align::Right);
costChance->setNeedMouseFocus(false);
costChance->setStateSelected(*it == selectedSpell);
costChance->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell());
mHeight += spellHeight;
@ -276,7 +223,9 @@ namespace MWGui
t->setUserString("Equipped", equipped ? "true" : "false");
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected);
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
t->setStateSelected(item == selectedItem);
if (store.getSelectedEnchantItem() != store.end())
t->setStateSelected(item == *store.getSelectedEnchantItem());
// cost / charge
MyGUI::Button* costCharge = mSpellView->createWidget<MyGUI::Button>(equipped ? "SpellText" : "SpellTextUnequipped",
@ -302,7 +251,8 @@ namespace MWGui
costCharge->setCaption(cost + "/" + charge);
costCharge->setTextAlign(MyGUI::Align::Right);
costCharge->setNeedMouseFocus(false);
costCharge->setStateSelected(item == selectedItem);
if (store.getSelectedEnchantItem() != store.end())
costCharge->setStateSelected(item == *store.getSelectedEnchantItem());
mHeight += spellHeight;
}
@ -349,9 +299,7 @@ namespace MWGui
void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::Spells& spells = stats.getSpells();
MWWorld::Ptr item = *_sender->getUserData<MWWorld::Ptr>();
// retrieve ContainerStoreIterator to the item
@ -379,7 +327,6 @@ namespace MWGui
}
store.setSelectedEnchantItem(it);
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item);
updateSpells();
@ -389,9 +336,7 @@ namespace MWGui
{
std::string spellId = _sender->getUserString("Spell");
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::Spells& spells = stats.getSpells();
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
@ -419,7 +364,6 @@ namespace MWGui
}
else
{
spells.setSelectedSpell(spellId);
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
}
@ -450,10 +394,7 @@ namespace MWGui
MWMechanics::Spells& spells = stats.getSpells();
if (spells.getSelectedSpell() == mSpellToDelete)
{
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->unsetSelectedSpell();
}
spells.remove(mSpellToDelete);

@ -15,8 +15,8 @@ namespace MWGui
public:
TextInputDialog();
std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; }
void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); }
std::string getTextInput() const { return mTextEdit->getCaption(); }
void setTextInput(const std::string &text) { mTextEdit->setCaption(text); }
void setNextButtonShow(bool shown);
void setTextLabel(const std::string &label);

@ -1010,6 +1010,7 @@ namespace MWGui
void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent)
{
mSelectedSpell = spellId;
mHud->setSelectedSpell(spellId, successChancePercent);
const ESM::Spell* spell =
@ -1020,6 +1021,7 @@ namespace MWGui
void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item)
{
mSelectedSpell = "";
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>()
.find(MWWorld::Class::get(item).getEnchantment(item));
@ -1039,6 +1041,7 @@ namespace MWGui
void WindowManager::unsetSelectedSpell()
{
mSelectedSpell = "";
mHud->unsetSelectedSpell();
mSpellWindow->setTitle("#{sNone}");
}

@ -200,6 +200,7 @@ namespace MWGui
virtual void activateQuickKey (int index);
virtual std::string getSelectedSpell() { return mSelectedSpell; }
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent);
virtual void setSelectedEnchantItem(const MWWorld::Ptr& item);
virtual void setSelectedWeapon(const MWWorld::Ptr& item);
@ -288,6 +289,8 @@ namespace MWGui
void trackWindow(OEngine::GUI::Layout* layout, const std::string& name);
void onWindowChangeCoord(MyGUI::Window* _sender);
std::string mSelectedSpell;
OEngine::GUI::MyGUIManager *mGuiManager;
OEngine::Render::OgreRenderer *mRendering;
HUD *mHud;

@ -378,10 +378,9 @@ namespace MWInput
MWBase::Environment::get().getWorld()->togglePreviewMode(true);
}
} else {
if (mPreviewPOVDelay > 0.5) {
//disable preview mode
MWBase::Environment::get().getWorld()->togglePreviewMode(false);
} else if (mPreviewPOVDelay > 0.f) {
//disable preview mode
MWBase::Environment::get().getWorld()->togglePreviewMode(false);
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) {
MWBase::Environment::get().getWorld()->togglePOV();
}
mPreviewPOVDelay = 0.f;

@ -350,7 +350,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mSkipAnim(false)
, mSecondsOfRunning(0)
, mSecondsOfSwimming(0)
, mFallHeight(0)
{
if(!mAnimation)
return;
@ -426,10 +425,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
{
forcestateupdate = true;
// Shields shouldn't be visible during spellcasting
// Shields/torches shouldn't be visible during spellcasting or hand-to-hand
// There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop",
// but they are also present in weapon drawing animation.
mAnimation->showShield(weaptype != WeapType_Spell);
mAnimation->showCarriedLeft(weaptype != WeapType_Spell && weaptype != WeapType_HandToHand);
std::string weapgroup;
if(weaptype == WeapType_None)
@ -505,10 +504,21 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
mAttackType.clear();
if(mWeaponType == WeapType_Spell)
{
// Unset casting flag, otherwise pressing the mouse button down would
// continue casting every frame if there is no animation
mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false);
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const std::string spellid = stats.getSpells().getSelectedSpell();
if(!spellid.empty())
// For the player, set the spell we want to cast
// This has to be done at the start of the casting animation,
// *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
if (mPtr.getRefData().getHandle() == "player")
stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell());
std::string spellid = stats.getSpells().getSelectedSpell();
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
{
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
@ -709,7 +719,8 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name())
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()
&& mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
{
if(!mAnimation->isPlaying("torch"))
mAnimation->play("torch", Priority_Torch,
@ -789,10 +800,7 @@ void CharacterController::update(float duration)
}
if(sneak || inwater || flying)
{
vec.z = 0.0f;
mFallHeight = mPtr.getRefData().getPosition().pos[2];
}
if(!onground && !flying && !inwater)
{
@ -801,11 +809,7 @@ void CharacterController::update(float duration)
if (world->isSlowFalling(mPtr))
{
// SlowFalling spell effect is active, do not keep previous fall height
mFallHeight = mPtr.getRefData().getPosition().pos[2];
}
else
{
mFallHeight = std::max(mFallHeight, mPtr.getRefData().getPosition().pos[2]);
cls.getCreatureStats(mPtr).land();
}
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
@ -861,7 +865,8 @@ void CharacterController::update(float duration)
mJumpState = JumpState_Landing;
vec.z = 0.0f;
float healthLost = cls.getFallDamage(mPtr, mFallHeight - mPtr.getRefData().getPosition().pos[2]);
float height = cls.getCreatureStats(mPtr).land();
float healthLost = cls.getFallDamage(mPtr, height);
if (healthLost > 0.0f)
{
const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm();
@ -882,8 +887,6 @@ void CharacterController::update(float duration)
//TODO: actor falls over
}
}
mFallHeight = mPtr.getRefData().getPosition().pos[2];
}
else
{
@ -922,6 +925,9 @@ void CharacterController::update(float duration)
}
}
if (onground || inwater || flying)
cls.getCreatureStats(mPtr).land();
if(movestate != CharState_None)
clearAnimQueue();

@ -156,9 +156,6 @@ class CharacterController
float mSecondsOfSwimming;
float mSecondsOfRunning;
// used for acrobatics progress and fall damages
float mFallHeight;
std::string mAttackType; // slash, chop or thrust
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);

@ -14,7 +14,8 @@ namespace MWMechanics
mTalkedTo (false), mAlarmed (false),
mAttacked (false), mHostile (false),
mAttackingOrSpell(false), mAttackType(AT_Chop),
mIsWerewolf(false)
mIsWerewolf(false),
mFallHeight(0)
{
for (int i=0; i<4; ++i)
mAiSettings[i] = 0;
@ -356,4 +357,16 @@ namespace MWMechanics
{
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
}
void CreatureStats::addToFallHeight(float height)
{
mFallHeight += height;
}
float CreatureStats::land()
{
float height = mFallHeight;
mFallHeight = 0;
return height;
}
}

@ -36,6 +36,8 @@ namespace MWMechanics
bool mHostile;
bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not.
float mFallHeight;
int mAttackType;
std::string mLastHitObject; // The last object to hit this actor
@ -49,6 +51,12 @@ namespace MWMechanics
public:
CreatureStats();
void addToFallHeight(float height);
/// Reset the fall height
/// @return total fall height
float land();
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);

@ -300,10 +300,11 @@ namespace MWMechanics
if (item.getCellRef().mEnchantmentCharge == -1)
item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge;
if (mCaster.getRefData().getHandle() == "player" && item.getCellRef().mEnchantmentCharge < castCost)
if (item.getCellRef().mEnchantmentCharge < castCost)
{
// TODO: Should there be a sound here?
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
if (mCaster.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
return false;
}
@ -370,33 +371,7 @@ namespace MWMechanics
fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss));
stats.setFatigue(fatigue);
// Check mana
bool fail = false;
DynamicStat<float> magicka = stats.getMagicka();
if (magicka.getCurrent() < spell->mData.mCost)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}");
fail = true;
}
// Reduce mana
if (!fail)
{
magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);
stats.setMagicka(magicka);
}
// If this is a power, check if it was already used in last 24h
if (!fail && spell->mData.mType & ESM::Spell::ST_Power)
{
if (stats.canUsePower(spell->mId))
stats.usePower(spell->mId);
else
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sPowerAlreadyUsed}");
fail = true;
}
}
// Check success
int successChance = getSpellSuccessChance(spell, mCaster);

@ -125,7 +125,7 @@ namespace MWMechanics
const ESM::Spell *spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (iter->first);
if (spell->mData.mType & ESM::Spell::ST_Disease)
if (spell->mData.mType == ESM::Spell::ST_Disease)
mSpells.erase(iter++);
else
iter++;
@ -139,7 +139,7 @@ namespace MWMechanics
const ESM::Spell *spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (iter->first);
if (spell->mData.mType & ESM::Spell::ST_Blight)
if (spell->mData.mType == ESM::Spell::ST_Blight)
mSpells.erase(iter++);
else
iter++;

@ -150,9 +150,12 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store)
}
}
void Actors::update (float duration)
void Actors::update (Ogre::Camera* camera)
{
// Nothing to do
for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end(); ++iter)
{
iter->second->preRender(camera);
}
}
Animation* Actors::getAnimation(const MWWorld::Ptr &ptr)

@ -47,7 +47,7 @@ namespace MWRender
void removeCell(MWWorld::CellStore* store);
void update (float duration);
void update (Ogre::Camera* camera);
/// Updates containing cell for object rendering data
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);

@ -146,29 +146,21 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly)
struct AddGlow
{
Ogre::Vector3* mColor;
AddGlow(Ogre::Vector3* col) : mColor(col) {}
NifOgre::MaterialControllerManager* mMaterialControllerMgr;
AddGlow(Ogre::Vector3* col, NifOgre::MaterialControllerManager* materialControllerMgr)
: mColor(col)
, mMaterialControllerMgr(materialControllerMgr)
{}
// TODO: integrate this with material controllers?
void operator()(Ogre::Entity* entity) const
{
unsigned int numsubs = entity->getNumSubEntities();
for(unsigned int i = 0;i < numsubs;++i)
{
unsigned int numsubs = entity->getNumSubEntities();
for(unsigned int i = 0;i < numsubs;++i)
{
Ogre::SubEntity* subEnt = entity->getSubEntity(i);
std::string newName = subEnt->getMaterialName() + "@fx";
if (sh::Factory::getInstance().searchInstance(newName) == NULL)
{
sh::MaterialInstance* instance =
sh::Factory::getInstance().createMaterialInstance(newName, subEnt->getMaterialName());
instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z)));
}
subEnt->setMaterialName(newName);
}
}
if (!entity->getNumSubEntities())
return;
Ogre::MaterialPtr writableMaterial = mMaterialControllerMgr->getWritableMaterial(entity);
sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(writableMaterial->getName());
instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z)));
}
};
@ -216,7 +208,7 @@ void Animation::setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint3
if (enchantedGlow)
std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(),
AddGlow(glowColor));
AddGlow(glowColor, &objlist->mMaterialControllerMgr));
}
@ -1004,6 +996,16 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj)
mSkelBase->detachObjectFromBone(obj);
}
bool Animation::isPlaying(Group group) const
{
for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)
{
if(stateiter->second.mGroups == group)
return true;
}
return false;
}
void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture)
{
// Early out if we already have this effect
@ -1025,6 +1027,10 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
params.mObjects = NifOgre::Loader::createObjects(mInsert, model);
else
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
setRenderProperties(params.mObjects, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL);
params.mLoop = loop;
params.mEffectId = effectId;
params.mBoneName = bonename;
@ -1040,17 +1046,12 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i)
{
Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i];
sh::Factory::getInstance()._ensureMaterial(partSys->getMaterialName(), "Default");
Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName());
static int count = 0;
Ogre::String materialName = "openmw/" + Ogre::StringConverter::toString(count++);
// TODO: destroy when effect is removed
Ogre::MaterialPtr newMat = mat->clone(materialName);
partSys->setMaterialName(materialName);
for (int t=0; t<newMat->getNumTechniques(); ++t)
Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys);
for (int t=0; t<mat->getNumTechniques(); ++t)
{
Ogre::Technique* tech = newMat->getTechnique(t);
Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p)
{
Ogre::Pass* pass = tech->getPass(p);
@ -1125,6 +1126,16 @@ void Animation::updateEffects(float duration)
}
}
void Animation::preRender(Ogre::Camera *camera)
{
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
NifOgre::ObjectScenePtr objects = it->mObjects;
objects->rotateBillboardNodes(camera);
}
mObjectRoot->rotateBillboardNodes(camera);
}
// TODO: Should not be here
Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item)
{
@ -1188,6 +1199,8 @@ bool ObjectAnimation::canBatch() const
{
if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty())
return false;
if (!mObjectRoot->mBillboardNodes.empty())
return false;
return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(),
FindEntityTransparency()) == mObjectRoot->mEntities.end();
}

@ -216,6 +216,9 @@ public:
void removeEffect (int effectId);
void getLoopingEffects (std::vector<int>& out);
/// Prepare this animation for being rendered with \a camera (rotates billboard nodes)
virtual void preRender (Ogre::Camera* camera);
virtual void setAlpha(float alpha) {}
private:
void updateEffects(float duration);
@ -255,6 +258,8 @@ public:
/** Returns true if the named animation group is playing. */
bool isPlaying(const std::string &groupname) const;
bool isPlaying(Group group) const;
/** Gets info about the given animation group.
* \param groupname Animation group to check.
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
@ -274,7 +279,7 @@ public:
virtual Ogre::Vector3 runAnimation(float duration);
virtual void showWeapons(bool showWeapon);
virtual void showShield(bool show) {}
virtual void showCarriedLeft(bool show) {}
void enableLights(bool enable);

@ -29,7 +29,9 @@ namespace MWRender
mNearest(30.f),
mFurthest(800.f),
mIsNearest(false),
mIsFurthest(false)
mIsFurthest(false),
mVanityToggleQueued(false),
mViewModeToggleQueued(false)
{
mVanity.enabled = false;
mVanity.allowed = true;
@ -103,6 +105,23 @@ namespace MWRender
void Camera::update(float duration, bool paused)
{
if (!mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
{
// Now process the view changes we queued earlier
if (mVanityToggleQueued)
{
toggleVanityMode(!mVanity.enabled);
mVanityToggleQueued = false;
}
if (mViewModeToggleQueued)
{
togglePreviewMode(false);
toggleViewMode();
mViewModeToggleQueued = false;
}
}
updateListener();
if (paused)
return;
@ -121,6 +140,14 @@ namespace MWRender
void Camera::toggleViewMode()
{
// Changing the view will stop all playing animations, so if we are playing
// anything important, queue the view change for later
if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
{
mViewModeToggleQueued = true;
return;
}
mFirstPersonView = !mFirstPersonView;
processViewChange();
@ -140,6 +167,14 @@ namespace MWRender
bool Camera::toggleVanityMode(bool enable)
{
// Changing the view will stop all playing animations, so if we are playing
// anything important, queue the view change for later
if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
{
mVanityToggleQueued = true;
return false;
}
if(!mVanity.allowed && enable)
return false;
@ -168,6 +203,9 @@ namespace MWRender
void Camera::togglePreviewMode(bool enable)
{
if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
return;
if(mPreviewMode == enable)
return;
@ -184,7 +222,6 @@ namespace MWRender
}
mCamera->setPosition(0.f, 0.f, offset);
rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false);
}
void Camera::setSneakOffset()
@ -319,6 +356,7 @@ namespace MWRender
mAnimation->setViewMode(NpcAnimation::VM_Normal);
mCameraNode->attachObject(mCamera);
}
rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false);
}
void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera)

@ -47,6 +47,9 @@ namespace MWRender
bool mDistanceAdjusted;
bool mVanityToggleQueued;
bool mViewModeToggleQueued;
/// Updates sound manager listener data
void updateListener();
@ -77,6 +80,7 @@ namespace MWRender
bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow);
/// @note this may be ignored if an important animation is currently playing
void togglePreviewMode(bool enable);
/// \brief Lowers the camera for sneak.

@ -117,7 +117,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
mListenerDisabled(disableListener),
mViewMode(viewMode),
mShowWeapons(false),
mShowShield(true),
mShowCarriedLeft(true),
mFirstPersonOffset(0.f, 0.f, 0.f),
mAlpha(1.f)
{
@ -319,7 +319,7 @@ void NpcAnimation::updateParts()
}
showWeapons(mShowWeapons);
showShield(mShowShield);
showCarriedLeft(mShowCarriedLeft);
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
static std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > sRaceMapping;
@ -654,32 +654,24 @@ void NpcAnimation::showWeapons(bool showWeapon)
mAlpha = 1.f;
}
void NpcAnimation::showShield(bool show)
void NpcAnimation::showCarriedLeft(bool show)
{
mShowShield = show;
mShowCarriedLeft = show;
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name())
if(show && iter != inv.end())
{
// ... Except for lights, which are still shown during spellcasting since they
// have their own (one-handed) casting animations
show = true;
}
if(show && shield != inv.end())
{
Ogre::Vector3 glowColor = getEnchantmentColor(*shield);
std::string mesh = MWWorld::Class::get(*shield).getModel(*shield);
Ogre::Vector3 glowColor = getEnchantmentColor(*iter);
std::string mesh = MWWorld::Class::get(*iter).getModel(*iter);
addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1,
mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor);
mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor);
if (shield->getTypeName() == typeid(ESM::Light).name())
addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get<ESM::Light>()->mBase);
if (iter->getTypeName() == typeid(ESM::Light).name())
addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get<ESM::Light>()->mBase);
}
else
{
removeIndividualPart(ESM::PRT_Shield);
}
}
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
@ -730,6 +722,17 @@ void NpcAnimation::setAlpha(float alpha)
}
}
void NpcAnimation::preRender(Ogre::Camera *camera)
{
Animation::preRender(camera);
for (int i=0; i<ESM::PRT_Count; ++i)
{
if (mObjectParts[i].isNull())
continue;
mObjectParts[i]->rotateBillboardNodes(camera);
}
}
void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene)
{
ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent()

@ -53,7 +53,7 @@ private:
std::string mHairModel;
ViewMode mViewMode;
bool mShowWeapons;
bool mShowShield;
bool mShowCarriedLeft;
int mVisibilityFlags;
@ -100,7 +100,7 @@ public:
virtual Ogre::Vector3 runAnimation(float timepassed);
virtual void showWeapons(bool showWeapon);
virtual void showShield(bool showShield);
virtual void showCarriedLeft(bool showa);
void setViewMode(ViewMode viewMode);
@ -116,6 +116,9 @@ public:
/// Make the NPC only partially visible
virtual void setAlpha(float alpha);
/// Prepare this animation for being rendered with \a camera (rotates billboard nodes)
virtual void preRender (Ogre::Camera* camera);
};
}

@ -245,11 +245,16 @@ void Objects::disableLights()
it->second->enableLights(false);
}
void Objects::update(const float dt)
void Objects::update(float dt, Ogre::Camera* camera)
{
PtrAnimationMap::const_iterator it = mObjects.begin();
for(;it != mObjects.end();it++)
it->second->runAnimation(dt);
it = mObjects.begin();
for(;it != mObjects.end();it++)
it->second->preRender(camera);
}
void Objects::rebuildStaticGeometry()

@ -48,7 +48,7 @@ public:
void enableLights();
void disableLights();
void update (const float dt);
void update (float dt, Ogre::Camera* camera);
///< per-frame update
Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*);

@ -372,9 +372,9 @@ void RenderingManager::update (float duration, bool paused)
if(paused)
return;
mActors->update (duration);
mObjects->update (duration);
mActors->update (mRendering.getCamera());
mPlayerAnimation->preRender(mRendering.getCamera());
mObjects->update (duration, mRendering.getCamera());
mSkyManager->update(duration);

@ -989,9 +989,12 @@ void VideoState::deinit()
this->audioq.cond.notify_one();
this->videoq.cond.notify_one();
this->parse_thread.join();
this->video_thread.join();
this->refresh_thread.join();
if (this->parse_thread.joinable())
this->parse_thread.join();
if (this->video_thread.joinable())
this->video_thread.join();
if (this->refresh_thread.joinable())
this->refresh_thread.join();
if(this->audio_st)
avcodec_close((*this->audio_st)->codec);

@ -131,6 +131,15 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
MWWorld::ContainerStoreIterator it = addImp(itemPtr);
MWWorld::Ptr item = *it;
// we may have copied an item from the world, so reset a few things first
item.getRefData().setBaseNode(NULL);
item.getCellRef().mPos.rot[0] = 0;
item.getCellRef().mPos.rot[1] = 0;
item.getCellRef().mPos.rot[2] = 0;
item.getCellRef().mPos.pos[0] = 0;
item.getCellRef().mPos.pos[1] = 0;
item.getCellRef().mPos.pos[2] = 0;
std::string script = MWWorld::Class::get(item).getScript(item);
if(script != "")
{

@ -94,6 +94,8 @@ namespace MWWorld
ContainerStoreIterator addNewStack (const Ptr& ptr);
///< Add the item to this container (do not try to stack it onto existing items)
virtual void flagAsModified();
public:
virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2);
@ -105,10 +107,6 @@ namespace MWWorld
void clear();
///< Empty container.
virtual void flagAsModified();
///< \attention This function is internal to the world model and should not be called from
/// outside.
float getWeight() const;
///< Return total weight of the items contained in *this.

@ -180,6 +180,29 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
std::pair<std::vector<int>, bool> itemsSlots =
MWWorld::Class::get (*iter).getEquipmentSlots (*iter);
// Skip items that have *only* harmful permanent effects
if (!test.getClass().getEnchantment(test).empty())
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Enchantment* enchantment = store.get<ESM::Enchantment>().find(test.getClass().getEnchantment(test));
bool harmfulEffect = false;
bool usefulEffect = false;
if (enchantment->mData.mType == ESM::Enchantment::ConstantEffect)
{
for (std::vector<ESM::ENAMstruct>::const_iterator it = enchantment->mEffects.mList.begin();
it != enchantment->mEffects.mList.end(); ++it)
{
const ESM::MagicEffect* effect = store.get<ESM::MagicEffect>().find(it->mEffectID);
if (effect->mData.mFlags & ESM::MagicEffect::Harmful)
harmfulEffect = true;
else
usefulEffect = true;
}
}
if (harmfulEffect && !usefulEffect)
continue;
}
for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin());
iter2!=itemsSlots.first.end(); ++iter2)
{

@ -18,6 +18,8 @@
#include "../mwbase/world.hpp" // FIXME
#include "../mwbase/environment.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include <components/esm/loadgmst.hpp>
#include "../mwworld/esmstore.hpp"
@ -573,9 +575,17 @@ namespace MWWorld
if(cell->hasWater())
waterlevel = cell->mWater;
float oldHeight = iter->first.getRefData().getPosition().pos[2];
Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum,
world->isFlying(iter->first),
waterlevel, mEngine);
float heightDiff = newpos.z - oldHeight;
if (heightDiff < 0)
iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff);
mMovementResults.push_back(std::make_pair(iter->first, newpos));
}

@ -25,9 +25,6 @@ ESM::CellRef& MWWorld::Ptr::getCellRef() const
{
assert(mRef);
if (mContainerStore)
mContainerStore->flagAsModified();
return mRef->mRef;
}
@ -35,9 +32,6 @@ MWWorld::RefData& MWWorld::Ptr::getRefData() const
{
assert(mRef);
if (mContainerStore)
mContainerStore->flagAsModified();
return mRef->mData;
}

@ -2049,15 +2049,56 @@ namespace MWWorld
}
}
bool World::startSpellCast(const Ptr &actor)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
std::string message;
bool fail = false;
bool isPlayer = (actor == getPlayer().getPlayer());
std::string selectedSpell = stats.getSpells().getSelectedSpell();
if (!selectedSpell.empty())
{
const ESM::Spell* spell = getStore().get<ESM::Spell>().search(selectedSpell);
// Check mana
MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
if (magicka.getCurrent() < spell->mData.mCost)
{
message = "#{sMagicInsufficientSP}";
fail = true;
}
// If this is a power, check if it was already used in the last 24h
if (!fail && spell->mData.mType == ESM::Spell::ST_Power)
{
if (stats.canUsePower(spell->mId))
stats.usePower(spell->mId);
else
{
message = "#{sPowerAlreadyUsed}";
fail = true;
}
}
// Reduce mana
magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);
stats.setMagicka(magicka);
}
if (isPlayer && fail)
MWBase::Environment::get().getWindowManager()->messageBox(message);
return !fail;
}
void World::castSpell(const Ptr &actor)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
InventoryStore& inv = actor.getClass().getInventoryStore(actor);
// Unset casting flag, otherwise pressing the mouse button down would continue casting every frame if using an enchantment
// (which casts instantly without an animation)
stats.setAttackingOrSpell(false);
MWWorld::Ptr target = getFacedObject();
std::string selectedSpell = stats.getSpells().getSelectedSpell();

@ -499,6 +499,17 @@ namespace MWWorld
virtual bool toggleGodMode();
/**
* @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.
* @param actor
* @return true if the spell can be casted (i.e. the animation should start)
*/
virtual bool startSpellCast (const MWWorld::Ptr& actor);
/**
* @brief Cast the actual spell, should be called mid-animation
* @param actor
*/
virtual void castSpell (const MWWorld::Ptr& actor);
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,

@ -26,9 +26,10 @@ ConfigurationManager::ConfigurationManager()
{
setupTokensMapping();
boost::filesystem::create_directories(mFixedPath.getUserPath());
boost::filesystem::create_directories(mFixedPath.getUserConfigPath());
boost::filesystem::create_directories(mFixedPath.getUserDataPath());
mLogPath = mFixedPath.getUserPath();
mLogPath = mFixedPath.getUserConfigPath();
}
ConfigurationManager::~ConfigurationManager()
@ -39,19 +40,19 @@ void ConfigurationManager::setupTokensMapping()
{
mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath));
mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath));
mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserPath));
mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath));
mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));
}
void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables,
boost::program_options::options_description& description)
{
loadConfig(mFixedPath.getUserPath(), variables, description);
loadConfig(mFixedPath.getUserConfigPath(), variables, description);
boost::program_options::notify(variables);
loadConfig(mFixedPath.getLocalPath(), variables, description);
boost::program_options::notify(variables);
loadConfig(mFixedPath.getGlobalPath(), variables, description);
loadConfig(mFixedPath.getGlobalConfigPath(), variables, description);
boost::program_options::notify(variables);
}
@ -141,12 +142,17 @@ void ConfigurationManager::loadConfig(const boost::filesystem::path& path,
const boost::filesystem::path& ConfigurationManager::getGlobalPath() const
{
return mFixedPath.getGlobalPath();
return mFixedPath.getGlobalConfigPath();
}
const boost::filesystem::path& ConfigurationManager::getUserPath() const
const boost::filesystem::path& ConfigurationManager::getUserConfigPath() const
{
return mFixedPath.getUserPath();
return mFixedPath.getUserConfigPath();
}
const boost::filesystem::path& ConfigurationManager::getUserDataPath() const
{
return mFixedPath.getUserDataPath();
}
const boost::filesystem::path& ConfigurationManager::getLocalPath() const

@ -36,7 +36,7 @@ struct ConfigurationManager
/**< Fixed paths */
const boost::filesystem::path& getGlobalPath() const;
const boost::filesystem::path& getUserPath() const;
const boost::filesystem::path& getUserConfigPath() const;
const boost::filesystem::path& getLocalPath() const;
const boost::filesystem::path& getGlobalDataPath() const;

@ -48,8 +48,9 @@ struct FixedPath
*/
FixedPath(const std::string& application_name)
: mPath(application_name + "/")
, mUserPath(mPath.getUserPath())
, mGlobalPath(mPath.getGlobalPath())
, mUserConfigPath(mPath.getUserConfigPath())
, mUserDataPath(mPath.getUserDataPath())
, mGlobalConfigPath(mPath.getGlobalConfigPath())
, mLocalPath(mPath.getLocalPath())
, mGlobalDataPath(mPath.getGlobalDataPath())
, mInstallPath(mPath.getInstallPath())
@ -59,28 +60,27 @@ struct FixedPath
/**
* \brief Return path pointing to the user local configuration directory.
*
* \return boost::filesystem::path
*/
const boost::filesystem::path& getUserPath() const
const boost::filesystem::path& getUserConfigPath() const
{
return mUserConfigPath;
}
const boost::filesystem::path& getUserDataPath() const
{
return mUserPath;
return mUserDataPath;
}
/**
* \brief Return path pointing to the global (system) configuration directory.
*
* \return boost::filesystem::path
*/
const boost::filesystem::path& getGlobalPath() const
const boost::filesystem::path& getGlobalConfigPath() const
{
return mGlobalPath;
return mGlobalConfigPath;
}
/**
* \brief Return path pointing to the directory where application was started.
*
* \return boost::filesystem::path
*/
const boost::filesystem::path& getLocalPath() const
{
@ -105,8 +105,9 @@ struct FixedPath
private:
PathType mPath;
boost::filesystem::path mUserPath; /**< User path */
boost::filesystem::path mGlobalPath; /**< Global path */
boost::filesystem::path mUserConfigPath; /**< User path */
boost::filesystem::path mUserDataPath;
boost::filesystem::path mGlobalConfigPath; /**< Global path */
boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */
boost::filesystem::path mGlobalDataPath; /**< Global application data path */

@ -8,6 +8,39 @@
#include <unistd.h>
#include <boost/filesystem/fstream.hpp>
namespace
{
boost::filesystem::path getUserHome()
{
const char* dir = getenv("HOME");
if (dir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
dir = pwd->pw_dir;
}
}
if (dir == NULL)
return boost::filesystem::path();
else
return boost::filesystem::path(dir);
}
boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback)
{
const char* result = getenv(envVariable.c_str());
if (!result)
return fallback;
boost::filesystem::path dir(result);
if (dir.empty())
return fallback;
else
return dir;
}
}
/**
* \namespace Files
*/
@ -19,51 +52,22 @@ LinuxPath::LinuxPath(const std::string& application_name)
{
}
boost::filesystem::path LinuxPath::getUserPath() const
boost::filesystem::path LinuxPath::getUserConfigPath() const
{
boost::filesystem::path userPath(".");
const char* theDir = getenv("HOME");
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir);
}
return getEnv("XDG_CONFIG_HOME", getUserHome() / ".config") / mName;
}
return userPath / ".config" / mName;
boost::filesystem::path LinuxPath::getUserDataPath() const
{
return getEnv("XDG_DATA_HOME", getUserHome() / ".local/share") / mName;
}
boost::filesystem::path LinuxPath::getCachePath() const
{
boost::filesystem::path userPath(".");
const char* theDir = getenv("HOME");
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir);
}
return userPath / ".cache" / mName;
return getEnv("XDG_CACHE_HOME", getUserHome() / ".cache") / mName;
}
boost::filesystem::path LinuxPath::getGlobalPath() const
boost::filesystem::path LinuxPath::getGlobalConfigPath() const
{
boost::filesystem::path globalPath("/etc/");
return globalPath / mName;
@ -84,17 +88,9 @@ boost::filesystem::path LinuxPath::getInstallPath() const
{
boost::filesystem::path installPath;
char *homePath = getenv("HOME");
if (homePath == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
homePath = pwd->pw_dir;
}
}
boost::filesystem::path homePath = getUserHome();
if (homePath != NULL)
if (!homePath.empty())
{
boost::filesystem::path wineDefaultRegistry(homePath);
wineDefaultRegistry /= ".wine/system.reg";

@ -20,44 +20,34 @@ struct LinuxPath
/**
* \brief Return path to the user directory.
*
* \return boost::filesystem::path
*/
boost::filesystem::path getUserPath() const;
boost::filesystem::path getUserConfigPath() const;
boost::filesystem::path getUserDataPath() const;
/**
* \brief Return path to the global (system) directory where game files could be placed.
*
* \return boost::filesystem::path
* \brief Return path to the global (system) directory where config files can be placed.
*/
boost::filesystem::path getGlobalPath() const;
boost::filesystem::path getGlobalConfigPath() const;
/**
* \brief Return path to the runtime configuration directory which is the
* place where an application was started.
*
* \return boost::filesystem::path
*/
boost::filesystem::path getLocalPath() const;
/**
* \brief
*
* \return boost::filesystem::path
* \brief Return path to the global (system) directory where game files can be placed.
*/
boost::filesystem::path getGlobalDataPath() const;
/**
* \brief
*
* \return boost::filesystem::path
*/
boost::filesystem::path getCachePath() const;
/**
* \brief Gets the path of the installed Morrowind version if there is one.
*
* \return boost::filesystem::path
*/
boost::filesystem::path getInstallPath() const;

@ -11,9 +11,26 @@
* FIXME: Someone with MacOS system should check this and correct if necessary
*/
/**
* \namespace Files
*/
namespace
{
boost::filesystem::path getUserHome()
{
const char* dir = getenv("HOME");
if (dir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
dir = pwd->pw_dir;
}
}
if (dir == NULL)
return boost::filesystem::path();
else
return boost::filesystem::path(dir);
}
}
namespace Files
{
@ -22,28 +39,24 @@ MacOsPath::MacOsPath(const std::string& application_name)
{
}
boost::filesystem::path MacOsPath::getUserPath() const
boost::filesystem::path MacOsPath::getUserConfigPath() const
{
boost::filesystem::path userPath(".");
boost::filesystem::path userPath (getUserHome());
userPath /= "Library/Preferences/";
const char* theDir = getenv("HOME");
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir) / "Library/Preferences/";
}
return userPath / mName;
}
boost::filesystem::path MacOsPath::getUserDataPath() const
{
// TODO: probably wrong?
boost::filesystem::path userPath (getUserHome());
userPath /= "Library/Preferences/";
return userPath / mName;
}
boost::filesystem::path MacOsPath::getGlobalPath() const
boost::filesystem::path MacOsPath::getGlobalConfigPath() const
{
boost::filesystem::path globalPath("/Library/Preferences/");
return globalPath / mName;
@ -51,23 +64,9 @@ boost::filesystem::path MacOsPath::getGlobalPath() const
boost::filesystem::path MacOsPath::getCachePath() const
{
boost::filesystem::path userPath(".");
const char* theDir = getenv("HOME");
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir) / "Library/Caches" / mName;
}
return userPath;
boost::filesystem::path userPath (getUserHome());
userPath /= "Library/Caches";
return userPath / mName;
}
boost::filesystem::path MacOsPath::getLocalPath() const
@ -85,17 +84,9 @@ boost::filesystem::path MacOsPath::getInstallPath() const
{
boost::filesystem::path installPath;
char *homePath = getenv("HOME");
if (homePath == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
homePath = pwd->pw_dir;
}
}
boost::filesystem::path homePath = getUserHome();
if (homePath != NULL)
if (!homePath.empty())
{
boost::filesystem::path wineDefaultRegistry(homePath);
wineDefaultRegistry /= ".wine/system.reg";

@ -23,14 +23,16 @@ struct MacOsPath
*
* \return boost::filesystem::path
*/
boost::filesystem::path getUserPath() const;
boost::filesystem::path getUserConfigPath() const;
boost::filesystem::path getUserDataPath() const;
/**
* \brief Return path to the global (system) directory.
*
* \return boost::filesystem::path
*/
boost::filesystem::path getGlobalPath() const;
boost::filesystem::path getGlobalConfigPath() const;
/**
* \brief Return path to the runtime directory which is the

@ -25,7 +25,7 @@ WindowsPath::WindowsPath(const std::string& application_name)
{
}
boost::filesystem::path WindowsPath::getUserPath() const
boost::filesystem::path WindowsPath::getUserConfigPath() const
{
boost::filesystem::path userPath(".");
@ -41,7 +41,13 @@ boost::filesystem::path WindowsPath::getUserPath() const
return userPath / mName;
}
boost::filesystem::path WindowsPath::getGlobalPath() const
boost::filesystem::path WindowsPath::getUserDataPath() const
{
// Have some chaos, windows people!
return getUserConfigPath();
}
boost::filesystem::path WindowsPath::getGlobalConfigPath() const
{
boost::filesystem::path globalPath(".");

@ -29,14 +29,16 @@ struct WindowsPath
*
* \return boost::filesystem::path
*/
boost::filesystem::path getUserPath() const;
boost::filesystem::path getUserConfigPath() const;
boost::filesystem::path getUserDataPath() const;
/**
* \brief Returns "X:\Program Files\"
*
* \return boost::filesystem::path
*/
boost::filesystem::path getGlobalPath() const;
boost::filesystem::path getGlobalConfigPath() const;
/**
* \brief Return local path which is a location where

@ -210,7 +210,7 @@ static const RecordFactoryEntry recordFactories [] = {
{ "AvoidNode", &construct <NiNode >, RC_AvoidNode },
{ "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiBillboardNode },
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
{ "NiRotatingParticles", &construct <NiRotatingParticles >, RC_NiRotatingParticles },
{ "NiAutoNormalParticles", &construct <NiAutoNormalParticles >, RC_NiAutoNormalParticles },

@ -36,6 +36,7 @@ enum RecordType
{
RC_MISSING = 0,
RC_NiNode,
RC_NiBillboardNode,
RC_AvoidNode,
RC_NiTriShape,
RC_NiRotatingParticles,

@ -324,6 +324,12 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture]));
instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture]));
instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture]));
instance->setProperty("darkMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DarkTexture]));
if (!texName[Nif::NiTexturingProperty::BaseTexture].empty())
{
instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("diffuseMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::BaseTexture].uvSet)));
}
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
{
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
@ -334,6 +340,11 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet)));
}
if (!texName[Nif::NiTexturingProperty::DarkTexture].empty())
{
instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("darkMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DarkTexture].uvSet)));
}
bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty()
&& texName[Nif::NiTexturingProperty::BumpTexture].find("_nh.") != std::string::npos;
@ -343,25 +354,30 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
{
if(i == Nif::NiTexturingProperty::BaseTexture ||
i == Nif::NiTexturingProperty::DetailTexture ||
i == Nif::NiTexturingProperty::DarkTexture ||
i == Nif::NiTexturingProperty::BumpTexture ||
i == Nif::NiTexturingProperty::GlowTexture)
continue;
if(!texName[i].empty())
warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i));
warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i) + " in " + name);
}
if (vertexColour)
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
// Add transparency if NiAlphaProperty was present
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
if (result.first)
// Override alpha flags based on our override list (transparency-overrides.cfg)
if (!texName[0].empty())
{
alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */
alphaTest = result.second;
depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
if (result.first)
{
alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */
alphaTest = result.second;
depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on
}
}
// Add transparency if NiAlphaProperty was present
if((alphaFlags&1))
{
std::string blend_mode;
@ -390,7 +406,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off")));
// depth_func???
sh::Factory::getInstance()._ensureMaterial(name, "Default");
return name;
}

@ -116,21 +116,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
bool vertShadowBuffer = false;
bool geomMorpherController = false;
if(!shape->controller.empty())
{
Nif::ControllerPtr ctrl = shape->controller;
do {
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
vertShadowBuffer = true;
geomMorpherController = true;
break;
}
} while(!(ctrl=ctrl->next).empty());
}
if(skin != NULL)
{
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
@ -350,10 +335,39 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest);
}
// Create a dummy vertex animation track if there's a geom morpher controller
// This is required to make Ogre create the buffers we will use for software vertex animation
if (srcVerts.size() && geomMorpherController)
mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH);
if(!shape->controller.empty())
{
Nif::ControllerPtr ctrl = shape->controller;
do {
// Load GeomMorpherController into an Ogre::Pose and Animation
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
const Nif::NiGeomMorpherController *geom =
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
const std::vector<Nif::NiMorphData::MorphData>& morphs = geom->data.getPtr()->mMorphs;
// Note we are not interested in morph 0, which just contains the original vertices
for (unsigned int i = 1; i < morphs.size(); ++i)
{
Ogre::Pose* pose = mesh->createPose(i);
const Nif::NiMorphData::MorphData& data = morphs[i];
for (unsigned int v = 0; v < data.mVertices.size(); ++v)
pose->addVertex(v, data.mVertices[v]);
Ogre::String animationID = Ogre::StringConverter::toString(ctrl->recIndex)
+ "_" + Ogre::StringConverter::toString(i);
Ogre::VertexAnimationTrack* track =
mesh->createAnimation(animationID, 0)
->createVertexTrack(1, Ogre::VAT_POSE);
Ogre::VertexPoseKeyFrame* keyframe = track->createVertexPoseKeyFrame(0);
keyframe->addPoseReference(i-1, 1);
}
break;
}
} while(!(ctrl=ctrl->next).empty());
}
}

@ -110,6 +110,17 @@ ObjectScene::~ObjectScene()
mSkelBase = NULL;
}
void ObjectScene::rotateBillboardNodes(Ogre::Camera *camera)
{
for (std::vector<Ogre::Node*>::iterator it = mBillboardNodes.begin(); it != mBillboardNodes.end(); ++it)
{
assert(mSkelBase);
Ogre::Node* node = *it;
node->_setDerivedOrientation(mSkelBase->getParentNode()->_getDerivedOrientation().Inverse() *
camera->getRealOrientation());
}
}
// Animates a texture
class FlipController
{
@ -167,6 +178,7 @@ public:
if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture)
|| (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture)
|| (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture)
|| (texture->getName() == "darkMap" && mTexSlot == Nif::NiTexturingProperty::DarkTexture)
|| (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture))
texture->setTextureName(mTextures[curTexture]);
}
@ -295,9 +307,6 @@ public:
return mData.back().isSet;
}
// FIXME: We are not getting all objects here. Skinned meshes get
// attached to the object's root node, and won't be connected via a
// TagPoint.
static void setVisible(Ogre::Node *node, int vis)
{
Ogre::Node::ChildNodeIterator iter = node->getChildIterator();
@ -306,6 +315,12 @@ public:
node = iter.getNext();
setVisible(node, vis);
// Skinned meshes and particle systems are attached to the scene node, not the bone.
// We use the Node's user data to connect it with the mesh / particle system.
Ogre::Any customData = node->getUserObjectBindings().getUserAny();
if (!customData.isEmpty())
Ogre::any_cast<Ogre::MovableObject*>(customData)->setVisible(vis);
Ogre::TagPoint *tag = dynamic_cast<Ogre::TagPoint*>(node);
if(tag != NULL)
{
@ -519,18 +534,18 @@ public:
class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator
{
private:
Ogre::SubEntity *mSubEntity;
Ogre::Entity *mEntity;
std::vector<Nif::NiMorphData::MorphData> mMorphs;
std::vector<float> mValues;
size_t mControllerIndex;
std::vector<Ogre::Vector3> mVertices;
public:
Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data)
: mSubEntity(subent)
Value(Ogre::Entity *ent, const Nif::NiMorphData *data, size_t controllerIndex)
: mEntity(ent)
, mMorphs(data->mMorphs)
, mControllerIndex(controllerIndex)
{
mValues.resize(mMorphs.size()-1, 0.f);
}
virtual Ogre::Real getValue() const
@ -543,21 +558,7 @@ public:
{
if (mMorphs.size() <= 1)
return;
#if OGRE_DOUBLE_PRECISION
#error "This code needs to be rewritten for double precision mode"
#endif
Ogre::VertexData* data = mSubEntity->_getSoftwareVertexAnimVertexData();
const Ogre::VertexElement* posElem =
data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
Ogre::HardwareVertexBufferSharedPtr vbuf =
data->vertexBufferBinding->getBuffer(posElem->getSource());
bool needToUpdate = false;
int i=0;
int i = 1;
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
{
float val = 0;
@ -565,37 +566,13 @@ public:
val = interpKey(it->mData.mKeys, time);
val = std::max(0.f, std::min(1.f, val));
if (val != mValues[i])
needToUpdate = true;
mValues[i] = val;
}
if (!needToUpdate)
return;
// The first morph key always contains the original positions
mVertices = mMorphs[0].mVertices;
i = 0;
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
{
float val = mValues[i];
if (it->mVertices.size() != mMorphs[0].mVertices.size())
continue;
Ogre::String animationID = Ogre::StringConverter::toString(mControllerIndex)
+ "_" + Ogre::StringConverter::toString(i);
if (val != 0)
{
for (unsigned int v=0; v<mVertices.size(); ++v)
mVertices[v] += it->mVertices[v] * val;
}
Ogre::AnimationState* state = mEntity->getAnimationState(animationID);
state->setEnabled(val > 0);
state->setWeight(val);
}
if (mVertices.size() * sizeof(float)*3 != vbuf->getSizeInBytes())
return;
vbuf->writeData(0, vbuf->getSizeInBytes(), &mVertices[0]);
mSubEntity->_markBuffersUsedForAnimation();
}
};
@ -614,13 +591,6 @@ class NIFObjectLoader
std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl;
}
static void fail(const std::string &msg)
{
std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl;
abort();
}
static void createEntity(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, ObjectScenePtr scene,
const Nif::Node *node, int flags, int animflags)
@ -648,6 +618,7 @@ class NIFObjectLoader
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast<Ogre::MovableObject*>(entity)));
scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
}
}
@ -677,7 +648,8 @@ class NIFObjectLoader
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr()));
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(
entity, geom->data.getPtr(), geom->recIndex));
GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
@ -773,8 +745,6 @@ class NIFObjectLoader
emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees()));
emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees()));
emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees()));
emitter->setParameter("skelbase", skelBaseName);
emitter->setParameter("bone", bone->getName());
Nif::ExtraPtr e = partctrl->extra;
while(!e.empty())
@ -867,7 +837,9 @@ class NIFObjectLoader
partsys->setParticleQuota(particledata->numParticles);
partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace));
sceneNode->attachObject(partsys);
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
scene->mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
Nif::ControllerPtr ctrl = partnode->controller;
while(!ctrl.empty())
@ -880,6 +852,9 @@ class NIFObjectLoader
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
// Set the emitter bone as user data on the particle system
// so the emitters/affectors can access it easily.
partsys->getUserObjectBindings().setUserAny(Ogre::Any(trgtbone));
createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName());
}
@ -1007,6 +982,17 @@ class NIFObjectLoader
else
flags |= node->flags;
if (node->recType == Nif::RC_NiBillboardNode)
{
// TODO: figure out what the flags mean.
// NifSkope has names for them, but doesn't implement them.
// Change mBillboardNodes to map <Bone, billboard type>
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
Ogre::Bone* bone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
bone->setManuallyControlled(true);
scene->mBillboardNodes.push_back(bone);
}
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
@ -1243,17 +1229,6 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo
}
}
for(size_t i = 0;i < scene->mParticles.size();i++)
{
Ogre::ParticleSystem *partsys = scene->mParticles[i];
if(partsys->isAttached())
partsys->detachFromParent();
Ogre::TagPoint *tag = scene->mSkelBase->attachObjectToBone(
scene->mSkelBase->getSkeleton()->getRootBone()->getName(), partsys);
tag->setScale(scale);
}
return scene;
}

@ -45,6 +45,8 @@ class MaterialControllerManager
{
public:
~MaterialControllerManager();
/// @attention if \a movable is an Entity, it needs to have *one* SubEntity
Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable);
private:
@ -59,6 +61,9 @@ struct ObjectScene {
std::vector<Ogre::ParticleSystem*> mParticles;
std::vector<Ogre::Light*> mLights;
// Nodes that should always face the camera when rendering
std::vector<Ogre::Node*> mBillboardNodes;
Ogre::SceneManager* mSceneMgr;
// The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length.
@ -74,6 +79,9 @@ struct ObjectScene {
{ }
~ObjectScene();
// Rotate nodes in mBillboardNodes so they face the given camera
void rotateBillboardNodes(Ogre::Camera* camera);
};
typedef Ogre::SharedPtr<ObjectScene> ObjectScenePtr;

@ -16,46 +16,11 @@
class NifEmitter : public Ogre::ParticleEmitter
{
public:
std::string mSkelBaseName;
Ogre::Bone* mBone;
Ogre::Bone* mEmitterBone;
Ogre::Bone* mParticleBone;
Ogre::ParticleSystem* getPartSys() { return mParent; }
class CmdSkelBase : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter* emitter = static_cast<NifEmitter*>(target);
emitter->mSkelBaseName = val;
}
};
class CmdBone : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter* emitter = static_cast<NifEmitter*>(target);
assert(!emitter->mSkelBaseName.empty() && "Base entity needs to be set first");
Ogre::ParticleSystem* partsys = emitter->getPartSys();
Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(emitter->mSkelBaseName);
Ogre::Bone* bone = ent->getSkeleton()->getBone(val);
assert(bone);
emitter->mBone = bone;
}
};
/** Command object for the emitter width (see Ogre::ParamCommand).*/
class CmdWidth : public Ogre::ParamCommand
{
@ -165,8 +130,10 @@ public:
NifEmitter(Ogre::ParticleSystem *psys)
: Ogre::ParticleEmitter(psys)
, mBone(NULL)
{
mEmitterBone = Ogre::any_cast<Ogre::Bone*>(psys->getUserObjectBindings().getUserAny());
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
initDefaults("Nif");
}
@ -180,7 +147,6 @@ public:
/** See Ogre::ParticleEmitter. */
void _initParticle(Ogre::Particle *particle)
{
assert (mBone && "No node set");
Ogre::Vector3 xOff, yOff, zOff;
// Call superclass
@ -189,23 +155,41 @@ public:
xOff = Ogre::Math::SymmetricRandom() * mXRange;
yOff = Ogre::Math::SymmetricRandom() * mYRange;
zOff = Ogre::Math::SymmetricRandom() * mZRange;
particle->position = mBone->_getDerivedPosition() + xOff + yOff + zOff;
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
Ogre::Vector3& position = particle->mPosition;
Ogre::Vector3& direction = particle->mDirection;
Ogre::ColourValue& colour = particle->mColour;
Ogre::Real& totalTimeToLive = particle->mTotalTimeToLive;
Ogre::Real& timeToLive = particle->mTimeToLive;
#else
Ogre::Vector3& position = particle->position;
Ogre::Vector3& direction = particle->direction;
Ogre::ColourValue& colour = particle->colour;
Ogre::Real& totalTimeToLive = particle->totalTimeToLive;
Ogre::Real& timeToLive = particle->timeToLive;
#endif
position = xOff + yOff + zOff +
mParticleBone->_getDerivedOrientation().Inverse() * (mEmitterBone->_getDerivedPosition()
- mParticleBone->_getDerivedPosition());
// Generate complex data by reference
genEmissionColour(particle->colour);
genEmissionColour(colour);
// NOTE: We do not use mDirection/mAngle for the initial direction.
Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom();
Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom();
particle->direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) *
direction = (mParticleBone->_getDerivedOrientation().Inverse()
* mEmitterBone->_getDerivedOrientation() *
Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) *
Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) *
Ogre::Vector3::UNIT_Z;
genEmissionVelocity(particle->direction);
genEmissionVelocity(direction);
// Generate simpler data
particle->timeToLive = particle->totalTimeToLive = genEmissionTTL();
timeToLive = totalTimeToLive = genEmissionTTL();
}
/** Overloaded to update the trans. matrix */
@ -361,16 +345,6 @@ protected:
Ogre::PT_REAL),
&msHorizontalAngleCmd);
dict->addParameter(Ogre::ParameterDef("bone",
"The bone where the particles should be spawned",
Ogre::PT_STRING),
&msBoneCmd);
dict->addParameter(Ogre::ParameterDef("skelbase",
"The name of the entity containing the bone (see 'bone' parameter)",
Ogre::PT_STRING),
&msSkelBaseCmd);
return true;
}
return false;
@ -384,8 +358,6 @@ protected:
static CmdVerticalAngle msVerticalAngleCmd;
static CmdHorizontalDir msHorizontalDirCmd;
static CmdHorizontalAngle msHorizontalAngleCmd;
static CmdBone msBoneCmd;
static CmdSkelBase msSkelBaseCmd;
};
NifEmitter::CmdWidth NifEmitter::msWidthCmd;
NifEmitter::CmdHeight NifEmitter::msHeightCmd;
@ -394,8 +366,6 @@ NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd;
NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd;
NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd;
NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd;
NifEmitter::CmdBone NifEmitter::msBoneCmd;
NifEmitter::CmdSkelBase NifEmitter::msSkelBaseCmd;
Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys)
{
@ -466,9 +436,13 @@ public:
/** See Ogre::ParticleAffector. */
void _initParticle(Ogre::Particle *particle)
{
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
const Ogre::Real life_time = particle->mTotalTimeToLive;
Ogre::Real particle_time = particle->mTimeToLive;
#else
const Ogre::Real life_time = particle->totalTimeToLive;
Ogre::Real particle_time = particle->timeToLive;
#endif
Ogre::Real width = mParent->getDefaultWidth();
Ogre::Real height = mParent->getDefaultHeight();
if(life_time-particle_time < mGrowTime)
@ -493,9 +467,13 @@ public:
while (!pi.end())
{
Ogre::Particle *p = pi.getNext();
const Ogre::Real life_time = p->totalTimeToLive;
Ogre::Real particle_time = p->timeToLive;
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
const Ogre::Real life_time = p->mTotalTimeToLive;
Ogre::Real particle_time = p->mTimeToLive;
#else
const Ogre::Real life_time = p->totalTimeToLive;
Ogre::Real particle_time = p->timeToLive;
#endif
Ogre::Real width = mParent->getDefaultWidth();
Ogre::Real height = mParent->getDefaultHeight();
if(life_time-particle_time < mGrowTime)
@ -554,47 +532,11 @@ class GravityAffector : public Ogre::ParticleAffector
};
public:
std::string mSkelBaseName;
Ogre::Bone* mBone;
Ogre::Bone* mEmitterBone;
Ogre::Bone* mParticleBone;
Ogre::ParticleSystem* getPartSys() { return mParent; }
class CmdSkelBase : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector* affector = static_cast<GravityAffector*>(target);
affector->mSkelBaseName = val;
}
};
class CmdBone : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector* affector = static_cast<GravityAffector*>(target);
assert(!affector->mSkelBaseName.empty() && "Base entity needs to be set first");
Ogre::ParticleSystem* partsys = affector->getPartSys();
Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(affector->mSkelBaseName);
Ogre::Bone* bone = ent->getSkeleton()->getBone(val);
assert(bone);
affector->mBone = bone;
}
};
/** Command object for force (see Ogre::ParamCommand).*/
class CmdForce : public Ogre::ParamCommand
{
@ -688,8 +630,11 @@ public:
, mForceType(Type_Wind)
, mPosition(0.0f)
, mDirection(0.0f)
, mBone(NULL)
{
mEmitterBone = Ogre::any_cast<Ogre::Bone*>(psys->getUserObjectBindings().getUserAny());
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
mType = "Gravity";
// Init parameters
@ -710,16 +655,6 @@ public:
dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd);
dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd);
dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd);
dict->addParameter(Ogre::ParameterDef("bone",
"The bone where the particles should be spawned",
Ogre::PT_STRING),
&msBoneCmd);
dict->addParameter(Ogre::ParameterDef("skelbase",
"The name of the entity containing the bone (see 'bone' parameter)",
Ogre::PT_STRING),
&msSkelBaseCmd);
}
}
@ -761,18 +696,20 @@ public:
static CmdForceType msForceTypeCmd;
static CmdDirection msDirectionCmd;
static CmdPosition msPositionCmd;
static CmdBone msBoneCmd;
static CmdSkelBase msSkelBaseCmd;
protected:
void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
{
const Ogre::Vector3 vec = mBone->_getDerivedOrientation() * mDirection * mForce * timeElapsed;
const Ogre::Vector3 vec = mDirection * mForce * timeElapsed;
Ogre::ParticleIterator pi = psys->_getIterator();
while (!pi.end())
{
Ogre::Particle *p = pi.getNext();
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
p->mDirection += vec;
#else
p->direction += vec;
#endif
}
}
@ -783,9 +720,18 @@ protected:
while (!pi.end())
{
Ogre::Particle *p = pi.getNext();
const Ogre::Vector3 vec = (
(mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - p->position).normalisedCopy() * force;
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
Ogre::Vector3 position = p->mPosition;
#else
Ogre::Vector3 position = p->position;
#endif
Ogre::Vector3 vec = (mPosition - position).normalisedCopy() * force;
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
p->mDirection += vec;
#else
p->direction += vec;
#endif
}
}
@ -801,8 +747,6 @@ GravityAffector::CmdForce GravityAffector::msForceCmd;
GravityAffector::CmdForceType GravityAffector::msForceTypeCmd;
GravityAffector::CmdDirection GravityAffector::msDirectionCmd;
GravityAffector::CmdPosition GravityAffector::msPositionCmd;
GravityAffector::CmdBone GravityAffector::msBoneCmd;
GravityAffector::CmdSkelBase GravityAffector::msSkelBaseCmd;
Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys)
{

@ -30,6 +30,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */
node->recType == Nif::RC_NiBillboardNode || /* Handled in the object loader */
node->recType == Nif::RC_NiBSParticleNode ||
node->recType == Nif::RC_NiCamera ||
node->recType == Nif::RC_NiAutoNormalParticles ||

@ -259,9 +259,8 @@ namespace sh
Platform* mPlatform;
MaterialInstance* findInstance (const std::string& name);
public:
MaterialInstance* searchInstance (const std::string& name);
private:
MaterialInstance* searchInstance (const std::string& name);
/// @return was anything removed?
bool removeCache (const std::string& pattern);

@ -8,10 +8,15 @@ material openmw_objects_base
diffuseMap black.png
normalMap
emissiveMap
darkMap
use_emissive_map false
use_detail_map false
use_diffuse_map false
use_dark_map false
emissiveMapUVSet 0
detailMapUVSet 0
diffuseMapUVSet 0
darkMapUVSet 0
use_parallax false
scene_blend default
@ -34,8 +39,12 @@ material openmw_objects_base
normalMap $normalMap
emissiveMapUVSet $emissiveMapUVSet
detailMapUVSet $detailMapUVSet
diffuseMapUVSet $diffuseMapUVSet
darkMapUVSet $darkMapUVSet
emissiveMap $emissiveMap
detailMap $detailMap
diffuseMap $diffuseMap
darkMap $darkMap
env_map $env_map
env_map_color $env_map_color
use_parallax $use_parallax
@ -55,8 +64,8 @@ material openmw_objects_base
texture_unit diffuseMap
{
direct_texture $diffuseMap
create_in_ffp true
tex_coord_set $emissiveMapUVSet
create_in_ffp $use_diffuse_map
tex_coord_set $diffuseMapUVSet
}
texture_unit normalMap
@ -66,12 +75,13 @@ material openmw_objects_base
num_mipmaps 4
}
texture_unit emissiveMap
texture_unit darkMap
{
create_in_ffp $use_emissive_map
colour_op add
direct_texture $emissiveMap
tex_coord_set $emissiveMapUVSet
create_in_ffp $use_dark_map
colour_op_ex modulate src_current src_texture
alpha_op_ex modulate src_current src_texture
direct_texture $darkMap
tex_coord_set $darkMapUVSet
}
texture_unit detailMap
@ -82,6 +92,14 @@ material openmw_objects_base
tex_coord_set $detailMapUVSet
}
texture_unit emissiveMap
{
create_in_ffp $use_emissive_map
colour_op add
direct_texture $emissiveMap
tex_coord_set $emissiveMapUVSet
}
texture_unit envMap
{
create_in_ffp $env_map

@ -17,13 +17,15 @@
#define NORMAL_MAP @shPropertyHasValue(normalMap)
#define EMISSIVE_MAP @shPropertyHasValue(emissiveMap)
#define DETAIL_MAP @shPropertyHasValue(detailMap)
#define DIFFUSE_MAP @shPropertyHasValue(diffuseMap)
#define DARK_MAP @shPropertyHasValue(darkMap)
#define PARALLAX @shPropertyBool(use_parallax)
#define PARALLAX_SCALE 0.04
#define PARALLAX_BIAS -0.02
// right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more
#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet))
#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet) || @shPropertyString(darkMapUVSet))
// if normal mapping is enabled, we force pixel lighting
#define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap))
@ -246,20 +248,26 @@
#endif
SH_BEGIN_PROGRAM
#if DIFFUSE_MAP
shSampler2D(diffuseMap)
#endif
#if NORMAL_MAP
shSampler2D(normalMap)
#endif
#if EMISSIVE_MAP
shSampler2D(emissiveMap)
#if DARK_MAP
shSampler2D(darkMap)
#endif
#if DETAIL_MAP
shSampler2D(detailMap)
#endif
#if EMISSIVE_MAP
shSampler2D(emissiveMap)
#endif
#if ENV_MAP
shSampler2D(envMap)
shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color)
@ -376,17 +384,33 @@
newUV += (TSeyeDir.xyxy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS )).xyxy;
#endif
#if DIFFUSE_MAP
#if @shPropertyString(diffuseMapUVSet)
float4 diffuse = shSample(diffuseMap, newUV.zw);
#else
float4 diffuse = shSample(diffuseMap, newUV.xy);
shOutputColour(0) = diffuse;
#endif
#else
float4 diffuse = float4(1,1,1,1);
#endif
#if DETAIL_MAP
#if @shPropertyString(detailMapUVSet)
shOutputColour(0) *= shSample(detailMap, newUV.zw)*2;
diffuse *= shSample(detailMap, newUV.zw)*2;
#else
diffuse *= shSample(detailMap, newUV.xy)*2;
#endif
#endif
#if DARK_MAP
#if @shPropertyString(darkMapUVSet)
diffuse *= shSample(darkMap, newUV.zw);
#else
shOutputColour(0) *= shSample(detailMap, newUV.xy)*2;
diffuse *= shSample(darkMap, newUV.xy);
#endif
#endif
shOutputColour(0) = diffuse;
#if !VERTEX_LIGHTING
float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough.xyz,1)).xyz;

@ -345,6 +345,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
#endif
@shForeach(@shPropertyString(num_layers))
thisLayerUV = layerUV;
#if @shPropertyBool(use_normal_map_@shIterator)
normalTex = shSample(normalMap@shIterator, thisLayerUV);
#if @shIterator == 0 && IS_FIRST_PASS
@ -354,7 +355,6 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
#endif
#endif
thisLayerUV = layerUV;
#if @shPropertyBool(use_parallax_@shIterator)
thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS );
#endif

@ -11,6 +11,8 @@
#include <extern/sdl4ogre/sdlwindowhelper.hpp>
#include <components/ogreinit/ogreinit.hpp>
#include <cassert>
#include <stdexcept>
@ -23,6 +25,12 @@ void OgreRenderer::cleanup()
delete mFader;
mFader = NULL;
Ogre::Root::getSingleton().destroyRenderTarget(mWindow);
mWindow = NULL;
delete mOgreInit;
mOgreInit = NULL;
// If we don't do this, the desktop resolution is not restored on exit
SDL_SetWindowFullscreen(mSDLWindow, 0);
@ -50,7 +58,8 @@ void OgreRenderer::configure(const std::string &logPath,
const std::string& rttMode
)
{
mRoot = mOgreInit.init(logPath + "/ogre.log");
mOgreInit = new OgreInit::OgreInit();
mRoot = mOgreInit->init(logPath + "/ogre.log");
RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem);
if (rs == 0)

@ -9,8 +9,6 @@
#include <OgreTexture.h>
#include <components/ogreinit/ogreinit.hpp>
struct SDL_Window;
struct SDL_Surface;
@ -26,6 +24,11 @@ namespace Ogre
class ParticleAffectorFactory;
}
namespace OgreInit
{
class OgreInit;
}
namespace OEngine
{
namespace Render
@ -57,7 +60,7 @@ namespace OEngine
Ogre::Camera *mCamera;
Ogre::Viewport *mView;
OgreInit::OgreInit mOgreInit;
OgreInit::OgreInit* mOgreInit;
Fader* mFader;

@ -36,49 +36,58 @@ https://wiki.openmw.org/index.php?title=Development_Environment_Setup
THE DATA PATH
The data path tells OpenMW where to find your Morrowind files. From 0.12.0 on OpenMW should be able to
The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to
pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly
(installing Morrowind under WINE is considered a proper install).
If that does not work for you, please check if you have any leftover openmw.cfg files from versions earlier than 0.12.0. These can interfere with the configuration process, so try to remove then.
If you are running OpenMW without installing it, you still need to manually adjust the data path. Create a text file named openmw.cfg in the location of the binary and enter the following line:
data=path to your data directory
(where you replace "path to your data directory" with the actual location of your data directory)
COMMAND LINE OPTIONS
Syntax: openmw <options>
Allowed options:
--help print help message
--version print version information and quit
--data arg (=data) set data directories (later directories have higher priority)
--data-local arg set local data directory (highest priority)
--resources arg (=resources) set resources directory
--start arg (=Beshara) set initial cell
--master arg master file(s)
--plugin arg plugin file(s)
--anim-verbose [=arg(=1)] (=0) output animation indices files
--debug [=arg(=1)] (=0) debug mode
--nosound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup
--script-console [=arg(=1)] (=0) enable console-only script functionality
--script-run arg select a file containing a list of console commands that is executed on startup
--new-game [=arg(=1)] (=0) activate char gen/new game mechanics
--fs-strict [=arg(=1)] (=0) strict file system handling (no case folding)
--encoding arg (=win1252) Character encoding used in OpenMW game messages:
win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages
win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages
win1252 - Western European (Latin) alphabet, used by default
--fallback arg fallback values
--help print help message
--version print version information and quit
--data arg (=data) set data directories (later directories
have higher priority)
--data-local arg set local data directory (highest
priority)
--fallback-archive arg (=fallback-archive)
set fallback BSA archives (later
archives have higher priority)
--resources arg (=resources) set resources directory
--start arg (=Beshara) set initial cell
--content arg content file(s): esm/esp, or
omwgame/omwaddon
--anim-verbose [=arg(=1)] (=0) output animation indices files
--no-sound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
scripts) at startup
--script-console [=arg(=1)] (=0) enable console-only script
functionality
--script-run arg select a file containing a list of
console commands that is executed on
startup
--new-game [=arg(=1)] (=0) activate char gen/new game mechanics
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
folding)
--encoding arg (=win1252) Character encoding used in OpenMW game
messages:
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
Albanian languages
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
and other languages
win1252 - Western European (Latin)
alphabet, used by default
--fallback arg fallback values
--no-grab Don't grab mouse cursor
--activate-dist arg (=-1) activation distance override
CHANGELOG

Loading…
Cancel
Save