1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-20 20:09:41 +00:00
This commit is contained in:
Stanislav 2014-08-26 22:14:50 +06:00
commit 4d2bdda22f
90 changed files with 2910 additions and 7407 deletions

1
.gitignore vendored
View file

@ -37,6 +37,7 @@ resources
/omwlauncher
/openmw
/opencs
/niftest
## generated objects
apps/openmw/config.hpp

139
.mailmap Normal file
View file

@ -0,0 +1,139 @@
Adam Hogan <comrade@comrade-desktop.(none)>
Aleksandar Jovanov <ajovanov93@yahoo.com>
Alexander Olofsson <ace@haxalot.com>
Alex McKibben <mckibbenta@gmail.com>
Alex "rainChu" Haddad <alx1213@gmail.com>
Ardekantur <greystone@ardekantur.com>
Armin Preiml <b.nutzer@gmail.com>
Artem Kotsynyak <greye@carceri>
Arthur Moore <arthur@Behemoth>
Arthur Moore <Arthur.Moore.git@cd-net.net>
athile <athile@athile.net>
athile <athile.g@gmail.com>
Stefan Galowicz <bogglez@the.mind>
Bret Curtis <psi29a@gmail.com>
Britt Mathis <britt.mathis@gmail.com>
Sandy Carter <bwrsandman@gmail.com>
Sandy Carter <mr.sandy.carter@gmail.com>
Carl Maxwell <carl.maxwell@gmail.com>
cc9cii <cc9c@iinet.net.au>
Cory F. Cohen <cfcohen@verizon.net>
Chris Robinson <chris.kcat@gmail.com>
Cris Mihalache <mirceam94@hotmail.com>
darkf <lw9k123@gmail.com>
Diggory Hardy <diggory.hardy@gmail.com>
Thomas Luppi <ThomasLuppi@gmail.com>
Thomas Luppi <tluppi@thomas-GE60.(none)>
Dmitriy 'Endorph' Shkurskiy <end0rph@hotmail.com>
Dmitry Marakasov <amdmi3@amdmi3.ru>
Douglas Diniz <dgdiniz@gmail.com>
Douglas Mencken <dougmencken@gmail.com>
Edmondo Tommasina <edmondo.tommasina@gmail.com>
Eduard Cot <eduard@eduard-iMac.(none)>
Eli2 <fabian@fabian-desktop.(none)>
Emanuel Guével <guevel.emanuel@gmail.com>
Leon Saunders <LeonDavidSaunders@gmail.com>
Fil Krynicki <filipkrynicki@gmail.com>
John Blomberg <johnblo@kth.se>
Gašper Sedej <gsedej@gmail.com>
Michał Bień <michal1.bien@gmail.com>
Joel Graff <monograff76@gmail.com>
Paul McElroy <pcm1123@gmail.com>
Artem Kotsynyak <greye@carceri>
Artem Kotsynyak <greye@null.net>
gugus <gus_512@hotmail.com>
guidoj <guido@thuisbasis.net>
gus <gus_512@hotmail.com>
Hallfaer Tuilinn <gijsbertth@gmail.com>
Julian Ospald <julian.ospald@googlemail.com>
Jacob Essex <jacob@jacobessex.com>
Jan Borsodi <jborsodi@gmail.com>
Jan-Peter Nilsson <peppe@pappkartong.se>
Jason Hooks <Hooks@.(none)>
Jason Hooks <jason@Jason-ThinkPad-R61.(none)>
Jason Hooks <jhooks1@mix.wvu.edu>
Jeffrey Haines <jeffhaines@me.com>
Jeffrey Haines <jib-y@users.noreply.github.com>
Jordan Ayers <jordan.ayers@gmail.com>
Jordan Milne <jordan.milne@saynotolinux.com>
Josua Grawitter <josh@greyage.org>
Julien Voisin <pouicpouicpouic@gmail.com>
Karl-Felix Glatzer <karl.glatzer@gmx.de>
Chris Robinson <chris.kcat@gmail.com>
Kevin Poitra <pupkev@yahoo.com>
Roman Proskuryakov <humbug@deeptown.org>
Lars Söderberg <lazze_1983@hotmail.com>
lazydev <lazydev@homecomp>
lazydev <lazydev@nomail>
Lukasz Gromanowski <lgromanowski@gmail.com>
Marc Bouvier <marcrbouvier@gmail.com>
Marcin Hulist <Gohan1989@gmail.com>
Marc Zinnschlag <marc@zpages.de>
Marek Kochanowicz <herr@mikrus.pl>
Marek Kochanowicz <marek@localhost.localdomain>
Marek Kochanowicz <sirherrbatka@gmail.com>
Mark Siewert <mark.siewert@t-online.de>
Mark Siewert <ms@cerebra.localdomain>
megaton <9megaton6@gmail.com>
Michael Mc Donnell <michael@mcdonnell.dk>
Michael Papageorgiou <werdanith@yahoo.gr>
Michal Sciubidlo <michal.sciubidlo@gmail.com>
Michał Ściubidło <michal.sciubidlo@gmail.com>
Nathan Jeffords <blunted2night@gmail.com>
Nicolay Korslund <korslund@gmail.com>
Nicolay Korslund <nicolayk@met.no>
Nikolay Kasyanov <corrmage@gmail.com>
pchan3 <chantlerpeter@gmail.com>
Pieter van der Kloet <pvdkloet@gmail.com>
Mateusz Kołaczek <mateusz.kolaczek@gmail.com>
Bret Curtis <psi29a@gmail.com>
Pieter van der Kloet <pvdkloet@gmail.com>
Rohit Nirmal <rohitnirmal9@gmail.com>
Roman Melnik <kromgart@gmail.com>
Radu-Marius Popovici <rpopovici@github.com>
Sandy Carter <bwrsandman@gmail.com>
Scott Howard <showard314@gmail.com>
Jannik Heller <scrawl@baseoftrash.de>
Jannik Heller <scrawl@scrawl-laptop.(none)>
Sebastian Wick <sebastian@sebastianwick.net>
Sebastian Wick <wick.sebastian@gmail.com>
Sergey Shambir <sergey.shambir.auto@gmail.com>
sergoz <parapvr@yandex.ru>
Chris Boyce <slothlife@users.noreply.github.com>
Star-Demon <starsickle@yahoo.com>
Sylvain Thesnieres <garvek@gmail.com>
Thomas Luppi <digrules@gmail.com>
Thomas Luppi <tluppi@thomas-GE60.(none)>
Thoronador <thoronador@users.sourceforge.net>
TomKoenderink <tom_koenderink-github@omniadicta.net>
Tom Mason <wheybags@wheybags.com>
Torben Carrington <torbenlcarrington@Gmail.com>
Vincent Heuken <vincent@vincentheuken.com>
Manuel Edelmann <edelmann.manuel@gmail.com>
Manuel Edelmann <vorenon@hotmail.com>
Alexander Nadeau <wareya@gmail.com>
Michael Hogan <mr.michaelhogan@gmail.com>
Jacob Essex <github@JacobEssex.com>
Yuri Krupenin <yuri.krupenin@gmail.com>
Bret Curtis <noone@your.box>
sirherrbatka <herr@localhost.localdomain>
sirherrbatka <sirherrbatka@myopera.com>
sergei <sergei@ubuntu.(none)>
riothamus <digimars@gmail.com>
nobrakal <nobrakal@gmail.com>
nkorslund <nkorslund@ea6a568a-9f4f-0410-981a-c910a81bb256>
mrcheko <cheko@sevas.ua>
Miroslav Puda <pakanek@gmail.com>
MiroslavR <miroslavr256@gmail.com>
mckibbenta <mckibbenta@gmail.com>
jeaye <jeaye@arrownext.com>
eroen <eroen@falcon.eroen.eu>
eroen <eroen@occam.eroen.eu>
dreamer-dead <dreamer.dead@gmail.com>
crysthala <crystalsoulslayer@gmail.com>
Berulacks <beru@eml.cc>
Axujen <axujen@gmail.com>
root <root@debian>
unknown <Hooks@.(none)>

View file

@ -77,6 +77,7 @@ option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF)
option(BUILD_NIFTEST "build nif file tester" OFF)
# OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF)
@ -396,6 +397,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
ENDIF(BUILD_NIFTEST)
# Install licenses
INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
@ -512,6 +516,11 @@ add_subdirectory (extern/sdl4ogre)
# Components
add_subdirectory (components)
#Testing
if (BUILD_NIFTEST)
add_subdirectory(components/nif/tests/)
endif(BUILD_NIFTEST)
# Apps and tools
add_subdirectory( apps/openmw )

View file

@ -950,9 +950,9 @@ void Record<ESM::MagicEffect>::print()
std::cout << " School: " << schoolLabel(mData.mData.mSchool)
<< " (" << mData.mData.mSchool << ")" << std::endl;
std::cout << " Base Cost: " << mData.mData.mBaseCost << std::endl;
std::cout << " Unknown 1: " << mData.mData.mUnknown1 << std::endl;
std::cout << " Speed: " << mData.mData.mSpeed << std::endl;
std::cout << " Size: " << mData.mData.mSize << std::endl;
std::cout << " Size Cap: " << mData.mData.mSizeCap << std::endl;
std::cout << " Unknown 2: " << mData.mData.mUnknown2 << std::endl;
std::cout << " RGB Color: " << "("
<< mData.mData.mRed << ","
<< mData.mData.mGreen << ","

View file

@ -16,6 +16,8 @@
#include <components/files/multidircollection.hpp>
#include <components/nifcache/nifcache.hpp>
#include "model/settings/usersettings.hpp"
#include "model/doc/documentmanager.hpp"
@ -37,6 +39,7 @@ namespace CS
{
Q_OBJECT
Nif::Cache mNifCache;
Files::ConfigurationManager mCfgMgr;
CSMSettings::UserSettings mUserSettings;
CSMDoc::DocumentManager mDocumentManager;

View file

@ -37,6 +37,10 @@ static const char pipe_err[] = "!!! Failed to create pipe\n";
static const char fork_err[] = "!!! Failed to fork debug process\n";
static const char exec_err[] = "!!! Failed to exec debug process\n";
#ifndef PATH_MAX /* Not all platforms (GNU Hurd) have this. */
# define PATH_MAX 256
#endif
static char argv0[PATH_MAX];
static char altstack[SIGSTKSZ];
@ -66,7 +70,7 @@ static const struct {
int code;
const char *name;
} sigill_codes[] = {
#ifndef __FreeBSD__
#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
{ ILL_ILLOPC, "Illegal opcode" },
{ ILL_ILLOPN, "Illegal operand" },
{ ILL_ILLADR, "Illegal addressing mode" },

View file

@ -15,7 +15,6 @@
#include <components/bsa/resources.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/translation/translation.hpp>
#include <components/nif/niffile.hpp>
#include <components/nifoverrides/nifoverrides.hpp>
#include <components/nifbullet/bulletnifloader.hpp>
@ -315,8 +314,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video");
if (renderSystem == "")
{

View file

@ -7,6 +7,8 @@
#include <components/files/collections.hpp>
#include <components/translation/translation.hpp>
#include <components/settings/settings.hpp>
#include <components/nifcache/nifcache.hpp>
#include "mwbase/environment.hpp"
@ -94,6 +96,8 @@ namespace OMW
std::vector<std::string> mScriptBlacklist;
bool mScriptBlacklistUse;
Nif::Cache mNifCache;
// not implemented
Engine (const Engine&);
Engine& operator= (const Engine&);

View file

@ -863,9 +863,9 @@ namespace MWClass
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak))
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)
|| ptr.getClass().getCreatureStats(ptr).getKnockedDown())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
// Can't talk to werewolfs
if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf())
return boost::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));

View file

@ -244,7 +244,8 @@ namespace MWGui
{
// we are stealing stuff
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
mModel = new PickpocketItemModel(player, new InventoryItemModel(container));
mModel = new PickpocketItemModel(player, new InventoryItemModel(container),
!mPtr.getClass().getCreatureStats(mPtr).getKnockedDown());
}
else
mModel = new InventoryItemModel(container);
@ -368,7 +369,9 @@ namespace MWGui
bool ContainerWindow::onTakeItem(const ItemStack &item, int count)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (dynamic_cast<PickpocketItemModel*>(mModel))
// TODO: move to ItemModels
if (dynamic_cast<PickpocketItemModel*>(mModel)
&& !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown())
{
MWMechanics::Pickpocket pickpocket(player, mPtr);
if (pickpocket.pick(item.mBase, count))

View file

@ -24,7 +24,7 @@ namespace MWGui
EnchantingDialog::EnchantingDialog()
: WindowBase("openmw_enchanting_dialog.layout")
, EffectEditorBase()
, EffectEditorBase(EffectEditorBase::Enchanting)
, mItemSelectionDialog(NULL)
{
getWidget(mName, "NameEdit");
@ -87,6 +87,7 @@ namespace MWGui
}
else
{
mName->setCaption(item.getClass().getName(item));
mItemBox->setItem(item);
mItemBox->setUserString ("ToolTipType", "ItemPtr");
mItemBox->setUserData(item);
@ -208,6 +209,7 @@ namespace MWGui
else
{
setItem(MWWorld::Ptr());
updateLabels();
}
}
@ -216,6 +218,7 @@ namespace MWGui
mItemSelectionDialog->setVisible(false);
setItem(item);
MWBase::Environment::get().getSoundManager()->playSound(item.getClass().getDownSoundId(item), 1, 1);
mEnchanting.nextCastStyle();
updateLabels();
}
@ -237,6 +240,7 @@ namespace MWGui
}
setSoulGem(item);
MWBase::Environment::get().getSoundManager()->playSound(item.getClass().getDownSoundId(item), 1, 1);
updateLabels();
}

View file

@ -471,6 +471,7 @@ namespace MWGui
mWeaponSpellBox->setVisible(true);
}
mWeapBox->clearUserStrings();
mWeapBox->setUserString("ToolTipType", "ItemPtr");
mWeapBox->setUserData(item);
@ -515,12 +516,14 @@ namespace MWGui
MWWorld::Ptr player = world->getPlayerPtr();
mWeapImage->setItem(MWWorld::Ptr());
if (player.getClass().getNpcStats(player).isWerewolf())
mWeapImage->setIcon("icons\\k\\tx_werewolf_hand.dds");
else
mWeapImage->setIcon("icons\\k\\stealth_handtohand.dds");
std::string icon = (player.getClass().getNpcStats(player).isWerewolf()) ? "icons\\k\\tx_werewolf_hand.dds" : "icons\\k\\stealth_handtohand.dds";
mWeapImage->setIcon(icon);
mWeapBox->clearUserStrings();
mWeapBox->setUserString("ToolTipType", "Layout");
mWeapBox->setUserString("ToolTipLayout", "HandToHandToolTip");
mWeapBox->setUserString("Caption_HandToHandText", itemName);
mWeapBox->setUserString("ImageTexture_HandToHandImage", icon);
}
void HUD::setCrosshairVisible(bool visible)

View file

@ -493,6 +493,7 @@ namespace MWGui
float capacity = player.getClass().getCapacity(player);
float encumbrance = player.getClass().getEncumbrance(player);
mTradeModel->adjustEncumbrance(encumbrance);
mEncumbranceBar->setValue(encumbrance, capacity);
}

View file

@ -6,16 +6,19 @@
namespace MWGui
{
PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel)
PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel, bool hideItems)
{
mSourceModel = sourceModel;
int chance = thief.getClass().getSkill(thief, ESM::Skill::Sneak);
mSourceModel->update();
for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)
if (hideItems)
{
if (std::rand() / static_cast<float>(RAND_MAX) * 100 > chance)
mHiddenItems.push_back(mSourceModel->getItem(i));
for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)
{
if (std::rand() / static_cast<float>(RAND_MAX) * 100 > chance)
mHiddenItems.push_back(mSourceModel->getItem(i));
}
}
}

View file

@ -10,7 +10,7 @@ namespace MWGui
class PickpocketItemModel : public ProxyItemModel
{
public:
PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel);
PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true);
virtual ItemStack getItem (ModelIndex index);
virtual size_t getItemCount();
virtual void update();

View file

@ -7,6 +7,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -293,12 +294,18 @@ namespace MWGui
return;
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
else if (type == Type_Item)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item);
MWWorld::ContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand
if (rightHand != store.end() && item == *rightHand)
{
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
}
}
else if (type == Type_MagicItem)
{
@ -322,6 +329,7 @@ namespace MWGui
}
store.setSelectedEnchantItem(it);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
}

View file

@ -37,10 +37,11 @@ namespace MWGui
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked);
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
mSaveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &SaveGameDialog::onKeyButtonPressed);
mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept);
mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged);
}
@ -247,6 +248,12 @@ namespace MWGui
}
}
void SaveGameDialog::onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character)
{
if (key == MyGUI::KeyCode::Delete && mCurrentSlot)
confirmDeleteSave();
}
void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender)
{
accept();

View file

@ -26,6 +26,7 @@ namespace MWGui
private:
void confirmDeleteSave();
void onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character);
void onCancelButtonClicked (MyGUI::Widget* sender);
void onOkButtonClicked (MyGUI::Widget* sender);
void onDeleteButtonClicked (MyGUI::Widget* sender);

View file

@ -287,7 +287,7 @@ namespace MWGui
SpellCreationDialog::SpellCreationDialog()
: WindowBase("openmw_spellcreation_dialog.layout")
, EffectEditorBase()
, EffectEditorBase(EffectEditorBase::Spellmaking)
{
getWidget(mNameEdit, "NameEdit");
getWidget(mMagickaCost, "MagickaCost");
@ -444,10 +444,11 @@ namespace MWGui
// ------------------------------------------------------------------------------------------------
EffectEditorBase::EffectEditorBase()
EffectEditorBase::EffectEditorBase(Type type)
: mAddEffectDialog()
, mSelectAttributeDialog(NULL)
, mSelectSkillDialog(NULL)
, mType(type)
{
mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded);
mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified);
@ -482,6 +483,13 @@ namespace MWGui
const std::vector<ESM::ENAMstruct>& list = spell->mEffects.mList;
for (std::vector<ESM::ENAMstruct>::const_iterator it2 = list.begin(); it2 != list.end(); ++it2)
{
const ESM::MagicEffect * effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(it2->mEffectID);
// skip effects that do not allow spellmaking/enchanting
int requiredFlags = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting;
if (!(effect->mData.mFlags & requiredFlags))
continue;
if (std::find(knownEffects.begin(), knownEffects.end(), it2->mEffectID) == knownEffects.end())
knownEffects.push_back(it2->mEffectID);
}

View file

@ -85,7 +85,13 @@ namespace MWGui
class EffectEditorBase
{
public:
EffectEditorBase();
enum Type
{
Spellmaking,
Enchanting
};
EffectEditorBase(Type type);
virtual ~EffectEditorBase();
protected:
@ -121,6 +127,9 @@ namespace MWGui
void setWidgets (Widgets::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView);
virtual void notifyEffectsChanged () {}
private:
Type mType;
};
class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase

View file

@ -11,6 +11,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "mapwindow.hpp"
#include "inventorywindow.hpp"
@ -19,6 +20,7 @@
namespace MWGui
{
std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"};
ToolTips::ToolTips() :
Layout("openmw_tooltips.layout")
@ -220,6 +222,12 @@ namespace MWGui
params.mNoTarget = false;
effects.push_back(params);
}
if (MWMechanics::spellIncreasesSkill(spell)) // display school of spells that contribute to skill progress
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
int school = MWMechanics::getSpellSchool(spell, player);
info.text = "#{sSchool}: " + sSchoolNames[school];
}
info.effects = effects;
tooltipSize = createToolTip(info);
}
@ -739,19 +747,11 @@ namespace MWGui
icon.insert(slashPos+1, "b_");
icon = Misc::ResourceHelpers::correctIconPath(icon);
std::vector<std::string> schools;
schools.push_back ("#{sSchoolAlteration}");
schools.push_back ("#{sSchoolConjuration}");
schools.push_back ("#{sSchoolDestruction}");
schools.push_back ("#{sSchoolIllusion}");
schools.push_back ("#{sSchoolMysticism}");
schools.push_back ("#{sSchoolRestoration}");
widget->setUserString("ToolTipType", "Layout");
widget->setUserString("ToolTipLayout", "MagicEffectToolTip");
widget->setUserString("Caption_MagicEffectName", "#{" + name + "}");
widget->setUserString("Caption_MagicEffectDescription", effect->mDescription);
widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]);
widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + sSchoolNames[effect->mData.mSchool]);
widget->setUserString("ImageTexture_MagicEffectImage", icon);
}

View file

@ -96,6 +96,8 @@ namespace MWGui
/// Adjust position for a tooltip so that it doesn't leave the screen and does not obscure the mouse cursor
void position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize);
static std::string sSchoolNames[6];
int mHorizontalScrollIndex;

View file

@ -93,6 +93,21 @@ namespace MWGui
unborrowImpl(item, count, mBorrowedFromUs);
}
void TradeItemModel::adjustEncumbrance(float &encumbrance)
{
for (std::vector<ItemStack>::iterator it = mBorrowedToUs.begin(); it != mBorrowedToUs.end(); ++it)
{
MWWorld::Ptr item = it->mBase;
encumbrance += item.getClass().getWeight(item) * it->mCount;
}
for (std::vector<ItemStack>::iterator it = mBorrowedFromUs.begin(); it != mBorrowedFromUs.end(); ++it)
{
MWWorld::Ptr item = it->mBase;
encumbrance -= item.getClass().getWeight(item) * it->mCount;
}
encumbrance = std::max(0.f, encumbrance);
}
void TradeItemModel::abort()
{
mBorrowedFromUs.clear();

View file

@ -34,6 +34,10 @@ namespace MWGui
/// Aborts trade
void abort();
/// Adjusts the given encumbrance by adding weight for items that have been lent to us,
/// and removing weight for items we've lent to someone else.
void adjustEncumbrance (float& encumbrance);
std::vector<ItemStack> getItemsBorrowedToUs();
private:

View file

@ -206,6 +206,15 @@ namespace MWGui
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
mVideoWidget->setNeedMouseFocus(true);
mVideoWidget->setNeedKeyFocus(true);
#if MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3,2,1)
// Removes default MyGUI system clipboard implementation, which supports windows only
MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear();
MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear();
MyGUI::ClipboardManager::getInstance().eventClipboardChanged += MyGUI::newDelegate(this, &WindowManager::onClipboardChanged);
MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);
#endif
}
void WindowManager::initUI()
@ -1699,4 +1708,27 @@ namespace MWGui
{
mScreenFader->setFactor(factor);
}
void WindowManager::onClipboardChanged(const std::string &_type, const std::string &_data)
{
if (_type == "Text")
SDL_SetClipboardText(MyGUI::TextIterator::getOnlyText(MyGUI::UString(_data)).asUTF8().c_str());
}
void WindowManager::onClipboardRequested(const std::string &_type, std::string &_data)
{
if (_type != "Text")
return;
char* text=0;
text = SDL_GetClipboardText();
if (text)
{
// MyGUI's clipboard might still have color information, to retain that information, only set the new text
// if it actually changed (clipboard inserted by an external application)
if (MyGUI::TextIterator::getOnlyText(_data) != text)
_data = text;
}
SDL_free(text);
}
}

View file

@ -448,6 +448,9 @@ namespace MWGui
void onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char);
void sizeVideo(int screenWidth, int screenHeight);
void onClipboardChanged(const std::string& _type, const std::string& _data);
void onClipboardRequested(const std::string& _type, std::string& _data);
};
}

View file

@ -498,6 +498,7 @@ namespace MWInput
void InputManager::keyPressed( const SDL_KeyboardEvent &arg )
{
#if MYGUI_VERSION <= MYGUI_DEFINE_VERSION(3,2,0)
// Cut, copy & paste
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus)
@ -537,20 +538,26 @@ namespace MWInput
}
}
}
#endif
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
bool consumed = false;
if (kc != OIS::KC_UNASSIGNED)
{
consumed = SDL_IsTextInputActive() &&
( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym)); // Little trick to check if key is printable
bool guiFocus = MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0);
setPlayerControlsEnabled(!guiFocus);
}
if (!mControlsDisabled)
if (!mControlsDisabled && !consumed)
mInputBinder->keyPressed (arg);
// Clear MyGUI's clipboard, so it doesn't interfere with our own clipboard implementation.
// We do not use MyGUI's clipboard manager because it doesn't support system clipboard integration with SDL.
#if MYGUI_VERSION <= MYGUI_DEFINE_VERSION(3,2,0)
MyGUI::ClipboardManager::getInstance().clearClipboardData("Text");
#endif
}
void InputManager::textInput(const SDL_TextInputEvent &arg)
@ -804,8 +811,7 @@ namespace MWInput
return;
if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed()
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console)
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
{
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal);

View file

@ -1402,6 +1402,7 @@ namespace MWMechanics
if (stats.isDead())
continue;
// An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
{
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow)
@ -1412,6 +1413,8 @@ namespace MWMechanics
if (followTarget == actor)
list.push_back(iter->first);
}
else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat)
break;
}
}
return list;

View file

@ -32,6 +32,15 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded)
{
}
MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration)
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mActorRefId(follow->mTargetId), mActorId(-1), mCellId(follow->mCellId)
, mCommanded(follow->mCommanded)
{
}
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{
MWWorld::Ptr target = getTarget();
@ -131,15 +140,6 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co
sequence.mPackages.push_back(package);
}
MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration)
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mActorRefId(follow->mTargetId), mCellId(follow->mCellId)
, mCommanded(follow->mCommanded)
{
}
MWWorld::Ptr MWMechanics::AiFollow::getTarget()
{
if (mActorId == -2)

View file

@ -32,12 +32,18 @@ AiPursue *MWMechanics::AiPursue::clone() const
}
bool AiPursue::execute (const MWWorld::Ptr& actor, float duration)
{
if(actor.getClass().getCreatureStats(actor).isDead())
return true;
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow
if(target == MWWorld::Ptr())
return true; //Target doesn't exist
if(target.getClass().getCreatureStats(target).isDead())
return true;
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
//Set the target desition from the actor

View file

@ -196,7 +196,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();
if(mHitState == CharState_None)
{
if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0)
if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0)
{
mHitState = CharState_KnockOut;
mCurrentHit = "knockout";
@ -1663,6 +1664,9 @@ void CharacterController::updateVisibility()
}
mAnimation->setAlpha(alpha);
float light = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Light).getMagnitude();
mAnimation->setLightEffect(light);
}
void CharacterController::determineAttackType()
@ -1671,9 +1675,9 @@ void CharacterController::determineAttackType()
if(mPtr.getClass().hasInventoryStore(mPtr))
{
if (move[1]) // forward-backward
if (move[1] && !move[0]) // forward-backward
mAttackType = "thrust";
else if (move[0]) //sideway
else if (move[0] && !move[1]) //sideway
mAttackType = "slash";
else
mAttackType = "chop";

View file

@ -19,9 +19,6 @@ namespace ESM
namespace MWMechanics
{
/// \brief Additional stats for NPCs
///
/// \note For technical reasons the spell list and the currently selected spell is also handled by
/// CreatureStats, even though they are actually NPC stats.
class NpcStats : public CreatureStats
{

View file

@ -151,6 +151,20 @@ namespace MWMechanics
return school;
}
bool spellIncreasesSkill(const ESM::Spell *spell)
{
if (spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always))
return true;
return false;
}
bool spellIncreasesSkill(const std::string &spellId)
{
const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
return spellIncreasesSkill(spell);
}
float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects)
{
short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
@ -775,7 +789,7 @@ namespace MWMechanics
}
}
if (mCaster.getRefData().getHandle() == "player" && spell->mData.mType == ESM::Spell::ST_Spell)
if (mCaster.getRefData().getHandle() == "player" && spellIncreasesSkill(spell))
mCaster.getClass().skillUsageSucceeded(mCaster,
spellSchoolToSkill(school), 0);
@ -874,5 +888,4 @@ namespace MWMechanics
return true;
}
}

View file

@ -36,6 +36,10 @@ namespace MWMechanics
int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
/// Get whether or not the given spell contributes to skill progress.
bool spellIncreasesSkill(const ESM::Spell* spell);
bool spellIncreasesSkill(const std::string& spellId);
/// Get the resistance attribute against an effect for a given actor. This will add together
/// ResistX and Weakness to X effects relevant against the given effect.
float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects);

View file

@ -63,12 +63,26 @@ namespace MWMechanics
TContainer::iterator iter = mSpells.find (lower);
std::map<std::string, CorprusStats>::iterator corprusIt = mCorprusSpells.find(lower);
// if it's corprus, remove negative and keep positive effects
if (corprusIt != mCorprusSpells.end())
{
worsenCorprus(lower);
if (mPermanentSpellEffects.find(lower) != mPermanentSpellEffects.end())
{
MagicEffects & effects = mPermanentSpellEffects[lower];
for (MagicEffects::Collection::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->first.mId);
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
effects.remove(effectIt->first);
}
}
mCorprusSpells.erase(corprusIt);
}
if (iter!=mSpells.end())
mSpells.erase (iter);
if (corprusIt != mCorprusSpells.end())
mCorprusSpells.erase(corprusIt);
if (spellId==mSelectedSpell)
mSelectedSpell.clear();
}
@ -94,20 +108,17 @@ namespace MWMechanics
if (iter->second.find(i) != iter->second.end())
random = iter->second.at(i);
int magnMult = 1;
if (mCorprusSpells.find(spell->mId) != mCorprusSpells.end())
{
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(spell->mEffects.mList.front().mEffectID);
if ((it->mEffectID != ESM::MagicEffect::Corprus) && (effect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) // APPLIED_ONCE
magnMult += mCorprusSpells.at(spell->mId).mWorsenings;
}
effects.add (*it, (it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random) * magnMult);
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random);
++i;
}
}
}
for (std::map<std::string, MagicEffects>::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it)
{
effects += it->second;
}
return effects;
}
@ -241,6 +252,25 @@ namespace MWMechanics
{
mCorprusSpells[corpSpellId].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod;
mCorprusSpells[corpSpellId].mWorsenings++;
// update worsened effects
mPermanentSpellEffects[corpSpellId] = MagicEffects();
const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(corpSpellId);
int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i)
{
const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) // APPLIED_ONCE
{
float random = 1.f;
if (mSpells[corpSpellId].find(i) != mSpells[corpSpellId].end())
random = mSpells[corpSpellId].at(i);
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
magnitude *= std::max(1, mCorprusSpells[corpSpellId].mWorsenings);
mPermanentSpellEffects[corpSpellId].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude));
}
}
}
bool Spells::hasCorprusEffect(const ESM::Spell *spell)
@ -297,6 +327,20 @@ namespace MWMechanics
for (std::map<std::string, ESM::TimeStamp>::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it)
mUsedPowers[it->first] = MWWorld::TimeStamp(it->second);
for (std::map<std::string, std::vector<ESM::SpellState::PermanentSpellEffectInfo> >::const_iterator it =
state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it)
{
const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);
if (!spell)
continue;
mPermanentSpellEffects[it->first] = MagicEffects();
for (std::vector<ESM::SpellState::PermanentSpellEffectInfo>::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt)
{
mPermanentSpellEffects[it->first].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude);
}
}
mCorprusSpells.clear();
for (std::map<std::string, ESM::SpellState::CorprusStats>::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it)
{
@ -316,6 +360,21 @@ namespace MWMechanics
for (std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
state.mUsedPowers[it->first] = it->second.toEsm();
for (std::map<std::string, MagicEffects>::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it)
{
std::vector<ESM::SpellState::PermanentSpellEffectInfo> effectList;
for (MagicEffects::Collection::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt)
{
ESM::SpellState::PermanentSpellEffectInfo info;
info.mId = effectIt->first.mId;
info.mArg = effectIt->first.mArg;
info.mMagnitude = effectIt->second.getModifier();
effectList.push_back(info);
}
state.mPermanentSpellEffects[it->first] = effectList;
}
for (std::map<std::string, CorprusStats>::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it)
{
state.mCorprusSpells[it->first].mWorsenings = mCorprusSpells.at(it->first).mWorsenings;

View file

@ -46,6 +46,9 @@ namespace MWMechanics
TContainer mSpells;
// spell-tied effects that will be applied even after removing the spell (currently used to keep positive effects when corprus is removed)
std::map<std::string, MagicEffects> mPermanentSpellEffects;
// Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)
std::string mSelectedSpell;

View file

@ -71,6 +71,7 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
, mNonAccumCtrl(NULL)
, mAccumulate(0.0f)
, mNullAnimationTimePtr(OGRE_NEW NullAnimationTime)
, mGlowLight(NULL)
{
for(size_t i = 0;i < sNumGroups;i++)
mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this));
@ -78,6 +79,8 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
Animation::~Animation()
{
setLightEffect(0);
mEffects.clear();
mAnimSources.clear();
@ -110,6 +113,11 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly)
mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) :
NifOgre::Loader::createObjectBase(mInsert, mdlname));
// Fast forward auto-play particles, which will have been set up as Emitting by the loader.
for (unsigned int i=0; i<mObjectRoot->mParticles.size(); ++i)
mObjectRoot->mParticles[i]->fastForward(1, 0.1);
if(mObjectRoot->mSkelBase)
{
mSkelBase = mObjectRoot->mSkelBase;
@ -1194,8 +1202,9 @@ bool Animation::allowSwitchViewMode() const
{
for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)
{
if(stateiter->second.mPriority > MWMechanics::Priority_Movement
if((stateiter->second.mPriority > MWMechanics::Priority_Movement
&& stateiter->second.mPriority < MWMechanics::Priority_Torch)
|| stateiter->second.mPriority == MWMechanics::Priority_Death)
return false;
}
return true;
@ -1391,6 +1400,37 @@ Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item)
return result;
}
void Animation::setLightEffect(float effect)
{
if (effect == 0)
{
if (mGlowLight)
{
mInsert->getCreator()->destroySceneNode(mGlowLight->getParentSceneNode());
mInsert->getCreator()->destroyLight(mGlowLight);
mGlowLight = NULL;
}
}
else
{
if (!mGlowLight)
{
mGlowLight = mInsert->getCreator()->createLight();
Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL;
for(size_t i = 0;i < mObjectRoot->mEntities.size();i++)
{
Ogre::Entity *ent = mObjectRoot->mEntities[i];
bounds.merge(ent->getBoundingBox());
}
mInsert->createChildSceneNode(bounds.getCenter())->attachObject(mGlowLight);
}
mGlowLight->setType(Ogre::Light::LT_POINT);
effect += 3;
mGlowLight->setAttenuation(1.0f / (0.03 * (0.5/effect)), 0, 0.5/effect, 0);
}
}
ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model)
: Animation(ptr, ptr.getRefData().getBaseNode())

View file

@ -126,6 +126,8 @@ protected:
MWWorld::Ptr mPtr;
Ogre::Light* mGlowLight;
Ogre::SceneNode *mInsert;
Ogre::Entity *mSkelBase;
NifOgre::ObjectScenePtr mObjectRoot;
@ -301,6 +303,11 @@ public:
/// This is typically called as part of runAnimation, but may be called manually if needed.
void updateEffects(float duration);
// TODO: move outside of this class
/// Makes this object glow, by placing a Light in its center.
/// @param effect Controls the radius and intensity of the light.
void setLightEffect(float effect);
virtual void showWeapons(bool showWeapon);
virtual void showCarriedLeft(bool show) {}
virtual void attachArrow() {}

View file

@ -552,6 +552,10 @@ NifOgre::ObjectScenePtr NpcAnimation::insertBoundedPart(const std::string &model
std::for_each(objects->mEntities.begin(), objects->mEntities.end(), SetObjectGroup(group));
std::for_each(objects->mParticles.begin(), objects->mParticles.end(), SetObjectGroup(group));
// Fast forward auto-play particles, which will have been set up as Emitting by the loader.
for (unsigned int i=0; i<objects->mParticles.size(); ++i)
objects->mParticles[i]->fastForward(1, 0.1);
if(objects->mSkelBase)
{
Ogre::AnimationStateSet *aset = objects->mSkelBase->getAllAnimationStates();

View file

@ -111,7 +111,7 @@ namespace MWScript
const std::string& name, char type) const
{
int index = MWBase::Environment::get().getScriptManager()->getLocals (scriptId).
search (type, name);
searchIndex (type, name);
if (index!=-1)
return index;

View file

@ -528,11 +528,12 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr actor = R()(runtime, false);
std::string factionID = "";
if(arg0==0)
{
MWWorld::Ptr actor = R()(runtime);
factionID = getDialogueActorFaction(actor);
}
else
@ -562,11 +563,12 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr actor = R()(runtime, false);
std::string factionID = "";
if(arg0==0)
{
MWWorld::Ptr actor = R()(runtime);
factionID = getDialogueActorFaction(actor);
}
else
@ -602,11 +604,12 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr actor = R()(runtime, false);
std::string factionID = "";
if(arg0==0)
{
MWWorld::Ptr actor = R()(runtime);
factionID = getDialogueActorFaction(actor);
}
else
@ -637,6 +640,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
std::string factionID = "";
if(arg0 >0)
{
@ -645,8 +650,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
@ -750,6 +753,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
std::string factionId;
if (arg0==1)
@ -759,8 +764,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
}
@ -783,6 +786,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
Interpreter::Type_Integer value = runtime[0].mInteger;
runtime.pop();
@ -795,8 +800,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
}
@ -818,6 +821,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
Interpreter::Type_Integer value = runtime[0].mInteger;
runtime.pop();
@ -830,8 +835,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
}
@ -913,6 +916,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
std::string factionID = "";
if(arg0 >0 )
{
@ -921,8 +926,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
@ -952,6 +955,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
std::string factionID = "";
if(arg0 >0 )
{
@ -960,7 +965,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
@ -985,6 +989,8 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime, false);
std::string factionID = "";
if(arg0 >0 )
{
@ -993,7 +999,6 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = R()(runtime);
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";

View file

@ -1,4 +1,3 @@
#include "actionteleport.hpp"
#include "../mwbase/environment.hpp"
@ -16,15 +15,19 @@ namespace MWWorld
void ActionTeleport::executeImp (const Ptr& actor)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
//find any NPC that is following the actor and teleport him too
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
{
executeImp(*it);
teleport(*it);
}
teleport(actor);
}
void ActionTeleport::teleport(const Ptr &actor)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
if(actor == world->getPlayerPtr())
{
world->getPlayer().setTeleported(true);

View file

@ -14,12 +14,16 @@ namespace MWWorld
std::string mCellName;
ESM::Position mPosition;
/// Teleports this actor and also teleports anyone following that actor.
virtual void executeImp (const Ptr& actor);
/// Teleports only the given actor (internal use).
void teleport(const Ptr &actor);
public:
ActionTeleport (const std::string& cellName, const ESM::Position& position);
///< If cellName is empty, an exterior cell is asumed.
///< If cellName is empty, an exterior cell is assumed.
};
}

View file

@ -20,6 +20,8 @@
#include "../mwmechanics/spellcasting.hpp"
#include "../mwrender/effectmanager.hpp"
#include "../mwrender/animation.hpp"
#include "../mwrender/renderconst.hpp"
#include "../mwsound/sound.hpp"
@ -41,6 +43,9 @@ namespace MWWorld
if(state.mObject->mControllers[i].getSource().isNull())
state.mObject->mControllers[i].setSource(Ogre::SharedPtr<MWRender::EffectAnimationTime> (new MWRender::EffectAnimationTime()));
}
MWRender::Animation::setRenderProperties(state.mObject, MWRender::RV_Misc,
MWRender::RQG_Main, MWRender::RQG_Alpha, 0.f, false, NULL);
}
void ProjectileManager::update(NifOgre::ObjectScenePtr object, float duration)

View file

@ -259,8 +259,6 @@ namespace MWWorld
void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos)
{
Nif::NIFFile::CacheLock cachelock;
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
@ -271,24 +269,6 @@ namespace MWWorld
CellStoreCollection::iterator active = mActiveCells.begin();
// get the number of cells to unload
int numUnload = 0;
while (active!=mActiveCells.end())
{
if ((*active)->getCell()->isExterior())
{
if (std::abs (X-(*active)->getCell()->getGridX())<=1 &&
std::abs (Y-(*active)->getCell()->getGridY())<=1)
{
// keep cells within the new 3x3 grid
++active;
continue;
}
}
++active;
++numUnload;
}
active = mActiveCells.begin();
while (active!=mActiveCells.end())
{
@ -408,7 +388,6 @@ namespace MWWorld
if(!loadcell)
loadcell = *mCurrentCell != *cell;
Nif::NIFFile::CacheLock lock;
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
@ -438,14 +417,6 @@ namespace MWWorld
// remove active
CellStoreCollection::iterator active = mActiveCells.begin();
// count number of cells to unload
int numUnload = 0;
while (active!=mActiveCells.end())
{
++active;
++numUnload;
}
// unload
int current = 0;
active = mActiveCells.begin();

View file

@ -19,7 +19,11 @@ add_component_dir (bsa
)
add_component_dir (nif
controlled effect niftypes record controller extra node record_ptr data niffile property
controlled effect niftypes record controller extra node record_ptr data niffile property nifkey data node
)
add_component_dir (nifcache
nifcache
)
add_component_dir (nifogre

View file

@ -1,6 +1,8 @@
#include "lineparser.hpp"
#include <memory>
#include <components/misc/stringops.hpp>
#include "scanner.hpp"
@ -120,7 +122,7 @@ namespace Compiler
if (mState==SetMemberVarState)
{
mMemberName = name;
mMemberName = Misc::StringUtils::lowerCase (name);
std::pair<char, bool> type = getContext().getMemberType (mMemberName, mName);
if (type.first!=' ')
@ -297,7 +299,12 @@ namespace Compiler
try
{
ErrorDowngrade errorDowngrade (getErrorHandler());
// workaround for broken positioncell instructions.
/// \todo add option to disable this
std::auto_ptr<ErrorDowngrade> errorDowngrade (0);
if (Misc::StringUtils::lowerCase (loc.mLiteral)=="positioncell")
errorDowngrade.reset (new ErrorDowngrade (getErrorHandler()));
std::vector<Interpreter::Type_Code> code;
optionals = mExprParser.parseArguments (argumentType, scanner, code);
mCode.insert (mCode.begin(), code.begin(), code.end());

View file

@ -15,8 +15,6 @@ namespace Compiler
std::vector<std::string> mLongs;
std::vector<std::string> mFloats;
int searchIndex (char type, const std::string& name) const;
std::vector<std::string>& get (char type);
public:
@ -27,9 +25,11 @@ namespace Compiler
int getIndex (const std::string& name) const;
///< return index for local variable \a name (-1: does not exist).
bool search (char type, const std::string& name) const;
/// Return index for local variable \a name of type \a type (-1: variable does not
/// exit).
bool search (char type, const std::string& name) const;
int searchIndex (char type, const std::string& name) const;
const std::vector<std::string>& get (char type) const;

View file

@ -42,8 +42,13 @@ void MagicEffect::load(ESMReader &esm)
esm.getHNT(mIndex, "INDX");
esm.getHNT(mData, "MEDT", 36);
if (mIndex>=0 && mIndex<NumberOfHardcodedFlags)
mData.mFlags |= HardcodedFlags[mIndex];
if (esm.getFormat() == 0)
{
// don't allow mods to change fixed flags in the legacy format
mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight);
if (mIndex>=0 && mIndex<NumberOfHardcodedFlags)
mData.mFlags |= HardcodedFlags[mIndex];
}
mIcon = esm.getHNOString("ITEX");
mParticle = esm.getHNOString("PTEX");

View file

@ -16,6 +16,7 @@ struct MagicEffect
enum Flags
{
// Originally fixed flags (HardcodedFlags array consists of just these)
TargetSkill = 0x1, // Affects a specific skill, which is specified elsewhere in the effect structure.
TargetAttribute = 0x2, // Affects a specific attribute, which is specified elsewhere in the effect structure.
NoDuration = 0x4, // Has no duration. Only runs effect once on cast.
@ -28,8 +29,14 @@ struct MagicEffect
UncappedDamage = 0x1000, // Negates multiple cap behaviours. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second.
NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target.
Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally.
CasterLinked = 0x20000 // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells.
CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells.
// Originally modifiable flags
AllowSpellmaking = 0x200, // Can be used for spellmaking
AllowEnchanting = 0x400, // Can be used for enchanting
NegativeLight = 0x800 // Negative light source
};
enum MagnitudeDisplayType
{
MDT_None,
@ -47,8 +54,10 @@ struct MagicEffect
int mFlags;
// Glow color for enchanted items with this effect
int mRed, mGreen, mBlue;
// Properties of the fired magic 'ball'
float mSpeed, mSize, mSizeCap;
float mUnknown1;
float mSpeed; // Speed of fired projectile
float mUnknown2;
}; // 36 bytes
static const std::map<short,std::string> sNames;

View file

@ -27,6 +27,23 @@ namespace ESM
mSpells[id] = random;
}
while (esm.isNextSub("PERM"))
{
std::string spellId = esm.getHString();
std::vector<PermanentSpellEffectInfo> permEffectList;
while (esm.isNextSub("EFID"))
{
PermanentSpellEffectInfo info;
esm.getHT(info.mId);
esm.getHNT(info.mArg, "ARG_");
esm.getHNT(info.mMagnitude, "MAGN");
permEffectList.push_back(info);
}
mPermanentSpellEffects[spellId] = permEffectList;
}
while (esm.isNextSub("CORP"))
{
std::string id = esm.getHString();
@ -64,6 +81,19 @@ namespace ESM
}
}
for (std::map<std::string, std::vector<PermanentSpellEffectInfo> >::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it)
{
esm.writeHNString("PERM", it->first);
const std::vector<PermanentSpellEffectInfo> & effects = it->second;
for (std::vector<PermanentSpellEffectInfo>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
esm.writeHNT("EFID", effectIt->mId);
esm.writeHNT("ARG_", effectIt->mArg);
esm.writeHNT("MAGN", effectIt->mMagnitude);
}
}
for (std::map<std::string, CorprusStats>::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it)
{
esm.writeHNString("CORP", it->first);

View file

@ -2,6 +2,7 @@
#define OPENMW_ESM_SPELLSTATE_H
#include <map>
#include <vector>
#include <string>
#include "defs.hpp"
@ -19,9 +20,18 @@ namespace ESM
TimeStamp mNextWorsening;
};
struct PermanentSpellEffectInfo
{
int mId;
int mArg;
float mMagnitude;
};
typedef std::map<std::string, std::map<const int, float> > TContainer;
TContainer mSpells;
std::map<std::string, std::vector<PermanentSpellEffectInfo> > mPermanentSpellEffects;
std::map<std::string, CorprusStats> mCorprusSpells;
std::map<std::string, TimeStamp> mUsedPowers;

View file

@ -4,7 +4,7 @@
#include <string>
#include <boost/filesystem.hpp>
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#ifndef ANDROID
#include <components/files/linuxpath.hpp>
namespace Files { typedef LinuxPath TargetPathType; }

View file

@ -1,6 +1,6 @@
#include "linuxpath.hpp"
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <cstdlib>
#include <cstring>
@ -157,4 +157,4 @@ boost::filesystem::path LinuxPath::getInstallPath() const
} /* namespace Files */
#endif /* defined(__linux__) || defined(__FreeBSD__) */
#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */

View file

@ -1,7 +1,7 @@
#ifndef COMPONENTS_FILES_LINUXPATH_H
#define COMPONENTS_FILES_LINUXPATH_H
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <boost/filesystem.hpp>
@ -56,6 +56,6 @@ struct LinuxPath
} /* namespace Files */
#endif /* defined(__linux__) || defined(__FreeBSD__) */
#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
#endif /* COMPONENTS_FILES_LINUXPATH_H */

29
components/nif/data.cpp Normal file
View file

@ -0,0 +1,29 @@
#include "data.hpp"
#include "node.hpp"
namespace Nif
{
void NiSkinInstance::post(NIFFile *nif)
{
data.post(nif);
root.post(nif);
bones.post(nif);
if(data.empty() || root.empty())
nif->fail("NiSkinInstance missing root or data");
size_t bnum = bones.length();
if(bnum != data->bones.size())
nif->fail("Mismatch in NiSkinData bone count");
root->makeRootBone(&data->trafo);
for(size_t i=0; i<bnum; i++)
{
if(bones[i].empty())
nif->fail("Oops: Missing bone! Don't know how to handle this.");
bones[i]->makeBone(i, data->bones[i]);
}
}
} // Namespace

View file

@ -25,9 +25,8 @@
#define OPENMW_COMPONENTS_NIF_DATA_HPP
#include "controlled.hpp"
#include <OgreQuaternion.h>
#include <OgreVector3.h>
#include "nifstream.hpp"
#include "nifkey.hpp"
namespace Nif
{

View file

@ -1,177 +1,15 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (nif_file.cpp) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
#include "niffile.hpp"
#include "record.hpp"
#include "components/misc/stringops.hpp"
#include "extra.hpp"
#include "controlled.hpp"
#include "node.hpp"
#include "property.hpp"
#include "data.hpp"
#include "effect.hpp"
#include "controller.hpp"
#include <iostream>
#include <map>
//TODO: when threading is needed, enable these
//#include <boost/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <OgreResourceGroupManager.h>
namespace Nif
{
class NIFFile::LoadedCache
{
//TODO: enable this to make cache thread safe...
//typedef boost::mutex mutex;
struct mutex
{
void lock () {};
void unlock () {}
};
typedef boost::lock_guard <mutex> lock_guard;
typedef std::map < std::string, boost::weak_ptr <NIFFile> > loaded_map;
typedef std::vector < boost::shared_ptr <NIFFile> > locked_files;
static int sLockLevel;
static mutex sProtector;
static loaded_map sLoadedMap;
static locked_files sLockedFiles;
public:
static ptr create (const std::string &name)
{
lock_guard _ (sProtector);
ptr result;
// lookup the resource
loaded_map::iterator i = sLoadedMap.find (name);
if (i == sLoadedMap.end ()) // it doesn't existing currently,
{ // or hasn't in the very near past
// create it now, for smoother threading if needed, the
// loading should be performed outside of the sLoaderMap
// lock and an alternate mechanism should be used to
// synchronize threads competing to load the same resource
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
// if we are locking the cache add an extra reference
// to keep the file in memory
if (sLockLevel > 0)
sLockedFiles.push_back (result);
// stash a reference to the resource so that future
// calls can benefit
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
}
else // it may (probably) still exists
{
// attempt to get the reference
result = i->second.lock ();
if (!result) // resource is in the process of being destroyed
{
// create a new instance, to replace the one that has
// begun the irreversible process of being destroyed
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
// respect the cache lock...
if (sLockLevel > 0)
sLockedFiles.push_back (result);
// we potentially overwrite an expired pointer here
// but the other thread performing the delete on
// the previous copy of this resource will detect it
// and make sure not to erase the new reference
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
}
}
// we made it!
return result;
}
static void release (NIFFile * file)
{
lock_guard _ (sProtector);
loaded_map::iterator i = sLoadedMap.find (file->filename);
// its got to be in here, it just might not be us...
assert (i != sLoadedMap.end ());
// if weak_ptr is still expired, this resource hasn't been recreated
// between the initiation of the final release due to destruction
// of the last shared pointer and this thread acquiring the lock on
// the loader map
if (i->second.expired ())
sLoadedMap.erase (i);
}
static void lockCache ()
{
lock_guard _ (sProtector);
sLockLevel++;
}
static void unlockCache ()
{
locked_files resetList;
{
lock_guard _ (sProtector);
if (--sLockLevel)
sLockedFiles.swap(resetList);
}
// this not necessary, but makes it clear that the
// deletion of the locked cache entries is being done
// outside the protection of sProtector
resetList.clear ();
}
};
int NIFFile::LoadedCache::sLockLevel = 0;
NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector;
NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap;
NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles;
// these three calls are forwarded to the cache implementation...
void NIFFile::lockCache () { LoadedCache::lockCache (); }
void NIFFile::unlockCache () { LoadedCache::unlockCache (); }
NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); }
/// Open a NIF stream. The name is used for error messages.
NIFFile::NIFFile(const std::string &name, psudo_private_modifier)
NIFFile::NIFFile(const std::string &name)
: ver(0)
, filename(name)
{
@ -180,10 +18,10 @@ NIFFile::NIFFile(const std::string &name, psudo_private_modifier)
NIFFile::~NIFFile()
{
LoadedCache::release (this);
for(std::size_t i=0; i<records.size(); i++)
delete records[i];
for (std::vector<Record*>::iterator it = records.begin() ; it != records.end(); it++)
{
delete *it;
}
}
template <typename NodeType> static Record* construct() { return new NodeType; }
@ -192,100 +30,84 @@ struct RecordFactoryEntry {
typedef Record* (*create_t) ();
char const * mName;
create_t mCreate;
RecordType mType;
};
/* These are all the record types we know how to read.
This can be heavily optimized later if needed. For example, a
hash table or a FSM-based parser could be used to look up
node names.
*/
static const RecordFactoryEntry recordFactories [] = {
{ "NiNode", &construct <NiNode >, RC_NiNode },
{ "AvoidNode", &construct <NiNode >, RC_AvoidNode },
{ "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiBillboardNode },
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
{ "NiRotatingParticles", &construct <NiRotatingParticles >, RC_NiRotatingParticles },
{ "NiAutoNormalParticles", &construct <NiAutoNormalParticles >, RC_NiAutoNormalParticles },
{ "NiCamera", &construct <NiCamera >, RC_NiCamera },
{ "RootCollisionNode", &construct <NiNode >, RC_RootCollisionNode },
{ "NiTexturingProperty", &construct <NiTexturingProperty >, RC_NiTexturingProperty },
{ "NiMaterialProperty", &construct <NiMaterialProperty >, RC_NiMaterialProperty },
{ "NiZBufferProperty", &construct <NiZBufferProperty >, RC_NiZBufferProperty },
{ "NiAlphaProperty", &construct <NiAlphaProperty >, RC_NiAlphaProperty },
{ "NiVertexColorProperty", &construct <NiVertexColorProperty >, RC_NiVertexColorProperty },
{ "NiShadeProperty", &construct <NiShadeProperty >, RC_NiShadeProperty },
{ "NiDitherProperty", &construct <NiDitherProperty >, RC_NiDitherProperty },
{ "NiWireframeProperty", &construct <NiWireframeProperty >, RC_NiWireframeProperty },
{ "NiSpecularProperty", &construct <NiSpecularProperty >, RC_NiSpecularProperty },
{ "NiStencilProperty", &construct <NiStencilProperty >, RC_NiStencilProperty },
{ "NiVisController", &construct <NiVisController >, RC_NiVisController },
{ "NiGeomMorpherController", &construct <NiGeomMorpherController >, RC_NiGeomMorpherController },
{ "NiKeyframeController", &construct <NiKeyframeController >, RC_NiKeyframeController },
{ "NiAlphaController", &construct <NiAlphaController >, RC_NiAlphaController },
{ "NiUVController", &construct <NiUVController >, RC_NiUVController },
{ "NiPathController", &construct <NiPathController >, RC_NiPathController },
{ "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController },
{ "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController },
{ "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController },
{ "NiFlipController", &construct <NiFlipController >, RC_NiFlipController },
{ "NiAmbientLight", &construct <NiLight >, RC_NiLight },
{ "NiDirectionalLight", &construct <NiLight >, RC_NiLight },
{ "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect },
{ "NiVertWeightsExtraData", &construct <NiVertWeightsExtraData >, RC_NiVertWeightsExtraData },
{ "NiTextKeyExtraData", &construct <NiTextKeyExtraData >, RC_NiTextKeyExtraData },
{ "NiStringExtraData", &construct <NiStringExtraData >, RC_NiStringExtraData },
{ "NiGravity", &construct <NiGravity >, RC_NiGravity },
{ "NiPlanarCollider", &construct <NiPlanarCollider >, RC_NiPlanarCollider },
{ "NiParticleGrowFade", &construct <NiParticleGrowFade >, RC_NiParticleGrowFade },
{ "NiParticleColorModifier", &construct <NiParticleColorModifier >, RC_NiParticleColorModifier },
{ "NiParticleRotation", &construct <NiParticleRotation >, RC_NiParticleRotation },
{ "NiFloatData", &construct <NiFloatData >, RC_NiFloatData },
{ "NiTriShapeData", &construct <NiTriShapeData >, RC_NiTriShapeData },
{ "NiVisData", &construct <NiVisData >, RC_NiVisData },
{ "NiColorData", &construct <NiColorData >, RC_NiColorData },
{ "NiPixelData", &construct <NiPixelData >, RC_NiPixelData },
{ "NiMorphData", &construct <NiMorphData >, RC_NiMorphData },
{ "NiKeyframeData", &construct <NiKeyframeData >, RC_NiKeyframeData },
{ "NiSkinData", &construct <NiSkinData >, RC_NiSkinData },
{ "NiUVData", &construct <NiUVData >, RC_NiUVData },
{ "NiPosData", &construct <NiPosData >, RC_NiPosData },
{ "NiRotatingParticlesData", &construct <NiRotatingParticlesData >, RC_NiRotatingParticlesData },
{ "NiAutoNormalParticlesData", &construct <NiAutoNormalParticlesData >, RC_NiAutoNormalParticlesData },
{ "NiSequenceStreamHelper", &construct <NiSequenceStreamHelper >, RC_NiSequenceStreamHelper },
{ "NiSourceTexture", &construct <NiSourceTexture >, RC_NiSourceTexture },
{ "NiSkinInstance", &construct <NiSkinInstance >, RC_NiSkinInstance },
};
static RecordFactoryEntry const * recordFactories_begin = &recordFactories [0];
static RecordFactoryEntry const * recordFactories_end = &recordFactories [sizeof (recordFactories) / sizeof (recordFactories[0])];
RecordFactoryEntry const * lookupRecordFactory (char const * name)
///Helper function for adding records to the factory map
static std::pair<std::string,RecordFactoryEntry> makeEntry(std::string recName, Record* (*create_t) (), RecordType type)
{
RecordFactoryEntry const * i;
for (i = recordFactories_begin; i != recordFactories_end; ++i)
if (strcmp (name, i->mName) == 0)
break;
if (i == recordFactories_end)
return NULL;
return i;
RecordFactoryEntry anEntry = {create_t,type};
return std::make_pair<std::string,RecordFactoryEntry>(recName, anEntry);
}
/* This file implements functions from the NIFFile class. It is also
where we stash all the functions we couldn't add as inline
definitions in the record types.
*/
///These are all the record types we know how to read.
static std::map<std::string,RecordFactoryEntry> makeFactory()
{
std::map<std::string,RecordFactoryEntry> newFactory;
newFactory.insert(makeEntry("NiNode", &construct <NiNode> , RC_NiNode ));
newFactory.insert(makeEntry("AvoidNode", &construct <NiNode> , RC_AvoidNode ));
newFactory.insert(makeEntry("NiBSParticleNode", &construct <NiNode> , RC_NiBSParticleNode ));
newFactory.insert(makeEntry("NiBSAnimationNode", &construct <NiNode> , RC_NiBSAnimationNode ));
newFactory.insert(makeEntry("NiBillboardNode", &construct <NiNode> , RC_NiBillboardNode ));
newFactory.insert(makeEntry("NiTriShape", &construct <NiTriShape> , RC_NiTriShape ));
newFactory.insert(makeEntry("NiRotatingParticles", &construct <NiRotatingParticles> , RC_NiRotatingParticles ));
newFactory.insert(makeEntry("NiAutoNormalParticles", &construct <NiAutoNormalParticles> , RC_NiAutoNormalParticles ));
newFactory.insert(makeEntry("NiCamera", &construct <NiCamera> , RC_NiCamera ));
newFactory.insert(makeEntry("RootCollisionNode", &construct <NiNode> , RC_RootCollisionNode ));
newFactory.insert(makeEntry("NiTexturingProperty", &construct <NiTexturingProperty> , RC_NiTexturingProperty ));
newFactory.insert(makeEntry("NiMaterialProperty", &construct <NiMaterialProperty> , RC_NiMaterialProperty ));
newFactory.insert(makeEntry("NiZBufferProperty", &construct <NiZBufferProperty> , RC_NiZBufferProperty ));
newFactory.insert(makeEntry("NiAlphaProperty", &construct <NiAlphaProperty> , RC_NiAlphaProperty ));
newFactory.insert(makeEntry("NiVertexColorProperty", &construct <NiVertexColorProperty> , RC_NiVertexColorProperty ));
newFactory.insert(makeEntry("NiShadeProperty", &construct <NiShadeProperty> , RC_NiShadeProperty ));
newFactory.insert(makeEntry("NiDitherProperty", &construct <NiDitherProperty> , RC_NiDitherProperty ));
newFactory.insert(makeEntry("NiWireframeProperty", &construct <NiWireframeProperty> , RC_NiWireframeProperty ));
newFactory.insert(makeEntry("NiSpecularProperty", &construct <NiSpecularProperty> , RC_NiSpecularProperty ));
newFactory.insert(makeEntry("NiStencilProperty", &construct <NiStencilProperty> , RC_NiStencilProperty ));
newFactory.insert(makeEntry("NiVisController", &construct <NiVisController> , RC_NiVisController ));
newFactory.insert(makeEntry("NiGeomMorpherController", &construct <NiGeomMorpherController> , RC_NiGeomMorpherController ));
newFactory.insert(makeEntry("NiKeyframeController", &construct <NiKeyframeController> , RC_NiKeyframeController ));
newFactory.insert(makeEntry("NiAlphaController", &construct <NiAlphaController> , RC_NiAlphaController ));
newFactory.insert(makeEntry("NiUVController", &construct <NiUVController> , RC_NiUVController ));
newFactory.insert(makeEntry("NiPathController", &construct <NiPathController> , RC_NiPathController ));
newFactory.insert(makeEntry("NiMaterialColorController", &construct <NiMaterialColorController> , RC_NiMaterialColorController ));
newFactory.insert(makeEntry("NiBSPArrayController", &construct <NiBSPArrayController> , RC_NiBSPArrayController ));
newFactory.insert(makeEntry("NiParticleSystemController", &construct <NiParticleSystemController> , RC_NiParticleSystemController ));
newFactory.insert(makeEntry("NiFlipController", &construct <NiFlipController> , RC_NiFlipController ));
newFactory.insert(makeEntry("NiAmbientLight", &construct <NiLight> , RC_NiLight ));
newFactory.insert(makeEntry("NiDirectionalLight", &construct <NiLight> , RC_NiLight ));
newFactory.insert(makeEntry("NiTextureEffect", &construct <NiTextureEffect> , RC_NiTextureEffect ));
newFactory.insert(makeEntry("NiVertWeightsExtraData", &construct <NiVertWeightsExtraData> , RC_NiVertWeightsExtraData ));
newFactory.insert(makeEntry("NiTextKeyExtraData", &construct <NiTextKeyExtraData> , RC_NiTextKeyExtraData ));
newFactory.insert(makeEntry("NiStringExtraData", &construct <NiStringExtraData> , RC_NiStringExtraData ));
newFactory.insert(makeEntry("NiGravity", &construct <NiGravity> , RC_NiGravity ));
newFactory.insert(makeEntry("NiPlanarCollider", &construct <NiPlanarCollider> , RC_NiPlanarCollider ));
newFactory.insert(makeEntry("NiParticleGrowFade", &construct <NiParticleGrowFade> , RC_NiParticleGrowFade ));
newFactory.insert(makeEntry("NiParticleColorModifier", &construct <NiParticleColorModifier> , RC_NiParticleColorModifier ));
newFactory.insert(makeEntry("NiParticleRotation", &construct <NiParticleRotation> , RC_NiParticleRotation ));
newFactory.insert(makeEntry("NiFloatData", &construct <NiFloatData> , RC_NiFloatData ));
newFactory.insert(makeEntry("NiTriShapeData", &construct <NiTriShapeData> , RC_NiTriShapeData ));
newFactory.insert(makeEntry("NiVisData", &construct <NiVisData> , RC_NiVisData ));
newFactory.insert(makeEntry("NiColorData", &construct <NiColorData> , RC_NiColorData ));
newFactory.insert(makeEntry("NiPixelData", &construct <NiPixelData> , RC_NiPixelData ));
newFactory.insert(makeEntry("NiMorphData", &construct <NiMorphData> , RC_NiMorphData ));
newFactory.insert(makeEntry("NiKeyframeData", &construct <NiKeyframeData> , RC_NiKeyframeData ));
newFactory.insert(makeEntry("NiSkinData", &construct <NiSkinData> , RC_NiSkinData ));
newFactory.insert(makeEntry("NiUVData", &construct <NiUVData> , RC_NiUVData ));
newFactory.insert(makeEntry("NiPosData", &construct <NiPosData> , RC_NiPosData ));
newFactory.insert(makeEntry("NiRotatingParticlesData", &construct <NiRotatingParticlesData> , RC_NiRotatingParticlesData ));
newFactory.insert(makeEntry("NiAutoNormalParticlesData", &construct <NiAutoNormalParticlesData> , RC_NiAutoNormalParticlesData ));
newFactory.insert(makeEntry("NiSequenceStreamHelper", &construct <NiSequenceStreamHelper> , RC_NiSequenceStreamHelper ));
newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture ));
newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance ));
return newFactory;
}
///Make the factory map used for parsing the file
static const std::map<std::string,RecordFactoryEntry> factories = makeFactory();
void NIFFile::parse()
{
@ -322,12 +144,13 @@ void NIFFile::parse()
if(rec.empty())
fail("Record number " + Ogre::StringConverter::toString(i) + " out of " + Ogre::StringConverter::toString(recNum) + " is blank.");
RecordFactoryEntry const * entry = lookupRecordFactory (rec.c_str ());
if (entry != NULL)
std::map<std::string,RecordFactoryEntry>::const_iterator entry = factories.find(rec);
if (entry != factories.end())
{
r = entry->mCreate ();
r->recType = entry->mType;
r = entry->second.mCreate ();
r->recType = entry->second.mType;
}
else
fail("Unknown record type " + rec);
@ -338,24 +161,24 @@ void NIFFile::parse()
r->recIndex = i;
records[i] = r;
r->read(&nif);
// Discard tranformations for the root node, otherwise some meshes
// occasionally get wrong orientation. Only for NiNode-s for now, but
// can be expanded if needed.
// This should be rewritten when the method is cleaned up.
if (0 == i && rec == "NiNode")
{
static_cast<Nif::Node*>(r)->trafo = Nif::Transformation::getIdentity();
}
}
size_t rootNum = nif.getUInt();
roots.resize(rootNum);
//Determine which records are roots
for(size_t i = 0;i < rootNum;i++)
{
intptr_t idx = nif.getInt();
roots[i] = ((idx >= 0) ? records.at(idx) : NULL);
int idx = nif.getInt();
if (idx >= 0)
{
roots[i] = records.at(idx);
}
else
{
roots[i] = NULL;
warn("Null Root found");
}
}
// Once parsing is done, do post-processing.
@ -363,81 +186,4 @@ void NIFFile::parse()
records[i]->post(this);
}
/// \todo move to the write cpp file
void NiSkinInstance::post(NIFFile *nif)
{
data.post(nif);
root.post(nif);
bones.post(nif);
if(data.empty() || root.empty())
nif->fail("NiSkinInstance missing root or data");
size_t bnum = bones.length();
if(bnum != data->bones.size())
nif->fail("Mismatch in NiSkinData bone count");
root->makeRootBone(&data->trafo);
for(size_t i=0; i<bnum; i++)
{
if(bones[i].empty())
nif->fail("Oops: Missing bone! Don't know how to handle this.");
bones[i]->makeBone(i, data->bones[i]);
}
}
void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
const Nif::NiMaterialProperty *&matprop,
const Nif::NiAlphaProperty *&alphaprop,
const Nif::NiVertexColorProperty *&vertprop,
const Nif::NiZBufferProperty *&zprop,
const Nif::NiSpecularProperty *&specprop,
const Nif::NiWireframeProperty *&wireprop) const
{
if(parent)
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
for(size_t i = 0;i < props.length();i++)
{
// Entries may be empty
if(props[i].empty())
continue;
const Nif::Property *pr = props[i].getPtr();
if(pr->recType == Nif::RC_NiTexturingProperty)
texprop = static_cast<const Nif::NiTexturingProperty*>(pr);
else if(pr->recType == Nif::RC_NiMaterialProperty)
matprop = static_cast<const Nif::NiMaterialProperty*>(pr);
else if(pr->recType == Nif::RC_NiAlphaProperty)
alphaprop = static_cast<const Nif::NiAlphaProperty*>(pr);
else if(pr->recType == Nif::RC_NiVertexColorProperty)
vertprop = static_cast<const Nif::NiVertexColorProperty*>(pr);
else if(pr->recType == Nif::RC_NiZBufferProperty)
zprop = static_cast<const Nif::NiZBufferProperty*>(pr);
else if(pr->recType == Nif::RC_NiSpecularProperty)
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
else if(pr->recType == Nif::RC_NiWireframeProperty)
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
else
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
}
}
Ogre::Matrix4 Node::getLocalTransform() const
{
Ogre::Matrix4 mat4 = Ogre::Matrix4(Ogre::Matrix4::IDENTITY);
mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation));
return mat4;
}
Ogre::Matrix4 Node::getWorldTransform() const
{
if(parent != NULL)
return parent->getWorldTransform() * getLocalTransform();
return getLocalTransform();
}
}

View file

@ -1,53 +1,13 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (nif_file.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
///Main header for reading .nif files
#ifndef OPENMW_COMPONENTS_NIF_NIFFILE_HPP
#define OPENMW_COMPONENTS_NIF_NIFFILE_HPP
#include <OgreResourceGroupManager.h>
#include <OgreDataStream.h>
#include <OgreVector2.h>
#include <OgreVector3.h>
#include <OgreVector4.h>
#include <OgreMatrix3.h>
#include <OgreQuaternion.h>
#include <OgreStringConverter.h>
#include <stdexcept>
#include <vector>
#include <cassert>
#include <typeinfo>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/detail/endian.hpp>
#include <stdint.h>
#include <iostream>
#include "record.hpp"
#include "niftypes.hpp"
#include "nifstream.hpp"
namespace Nif
{
@ -61,62 +21,46 @@ class NIFFile
/// Nif file version
int ver;
/// File name, used for error messages
/// File name, used for error messages and opening the file
std::string filename;
/// Record list
std::vector<Record*> records;
/// Root list
/// Root list. This is a select portion of the pointers from records
std::vector<Record*> roots;
/// Parse the file
void parse();
class LoadedCache;
friend class LoadedCache;
// attempt to protect NIFFile from misuse...
struct psudo_private_modifier {}; // this dirty little trick should optimize out
///Private Copy Constructor
NIFFile (NIFFile const &);
///\overload
void operator = (NIFFile const &);
public:
/// Used for error handling
/// Used if file parsing fails
void fail(const std::string &msg)
{
std::string err = "NIFFile Error: " + msg;
err += "\nFile: " + filename;
throw std::runtime_error(err);
}
/// Used when something goes wrong, but not catastrophically so
void warn(const std::string &msg)
{
std::cerr << "NIFFile Warning: " << msg <<std::endl
<< "File: " << filename <<std::endl;
}
typedef boost::shared_ptr <NIFFile> ptr;
/// Open a NIF stream. The name is used for error messages.
NIFFile(const std::string &name, psudo_private_modifier);
/// Open a NIF stream. The name is used for error messages and opening the file.
NIFFile(const std::string &name);
~NIFFile();
static ptr create (const std::string &name);
static void lockCache ();
static void unlockCache ();
struct CacheLock
{
CacheLock () { lockCache (); }
~CacheLock () { unlockCache (); }
};
/// Get a given record
Record *getRecord(size_t index) const
{
Record *res = records.at(index);
assert(res != NULL);
return res;
}
/// Number of records
@ -126,7 +70,6 @@ public:
Record *getRoot(size_t index=0) const
{
Record *res = roots.at(index);
assert(res != NULL);
return res;
}
/// Number of roots
@ -134,131 +77,6 @@ public:
};
template<typename T>
struct KeyT {
float mTime;
T mValue;
T mForwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList
T mBackwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList
float mTension; // Only for TBC interpolation
float mBias; // Only for TBC interpolation
float mContinuity; // Only for TBC interpolation
};
typedef KeyT<float> FloatKey;
typedef KeyT<Ogre::Vector3> Vector3Key;
typedef KeyT<Ogre::Vector4> Vector4Key;
typedef KeyT<Ogre::Quaternion> QuaternionKey;
template<typename T, T (NIFStream::*getValue)()>
struct KeyListT {
typedef std::vector< KeyT<T> > VecType;
static const unsigned int sLinearInterpolation = 1;
static const unsigned int sQuadraticInterpolation = 2;
static const unsigned int sTBCInterpolation = 3;
static const unsigned int sXYZInterpolation = 4;
unsigned int mInterpolationType;
VecType mKeys;
KeyListT() : mInterpolationType(sLinearInterpolation) {}
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
void read(NIFStream *nif, bool force=false)
{
assert(nif);
mInterpolationType = 0;
size_t count = nif->getUInt();
if(count == 0 && !force)
return;
//If we aren't forcing things, make sure that read clears any previous keys
if(!force)
mKeys.clear();
mInterpolationType = nif->getUInt();
KeyT<T> key;
NIFStream &nifReference = *nif;
if(mInterpolationType == sLinearInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readTimeAndValue(nifReference, key);
mKeys.push_back(key);
}
}
else if(mInterpolationType == sQuadraticInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readQuadratic(nifReference, key);
mKeys.push_back(key);
}
}
else if(mInterpolationType == sTBCInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readTBC(nifReference, key);
mKeys.push_back(key);
}
}
//XYZ keys aren't actually read here.
//data.hpp sees that the last type read was sXYZInterpolation and:
// Eats a floating point number, then
// Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared.
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
else if(mInterpolationType == sXYZInterpolation)
{
//Don't try to read XYZ keys into the wrong part
if ( count != 1 )
nif->file->fail("XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: "+Ogre::StringConverter::toString(count));
}
else if (0 == mInterpolationType)
{
if (count != 0)
nif->file->fail("Interpolation type 0 doesn't work with keys");
}
else
nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType));
}
private:
static void readTimeAndValue(NIFStream &nif, KeyT<T> &key)
{
key.mTime = nif.getFloat();
key.mValue = (nif.*getValue)();
}
static void readQuadratic(NIFStream &nif, KeyT<Ogre::Quaternion> &key)
{
readTimeAndValue(nif, key);
}
template <typename U>
static void readQuadratic(NIFStream &nif, KeyT<U> &key)
{
readTimeAndValue(nif, key);
key.mForwardValue = (nif.*getValue)();
key.mBackwardValue = (nif.*getValue)();
}
static void readTBC(NIFStream &nif, KeyT<T> &key)
{
readTimeAndValue(nif, key);
key.mTension = nif.getFloat();
key.mBias = nif.getFloat();
key.mContinuity = nif.getFloat();
}
};
typedef KeyListT<float,&NIFStream::getFloat> FloatKeyList;
typedef KeyListT<Ogre::Vector3,&NIFStream::getVector3> Vector3KeyList;
typedef KeyListT<Ogre::Vector4,&NIFStream::getVector4> Vector4KeyList;
typedef KeyListT<Ogre::Quaternion,&NIFStream::getQuaternion> QuaternionKeyList;
} // Namespace
#endif

140
components/nif/nifkey.hpp Normal file
View file

@ -0,0 +1,140 @@
///File to handle keys used by nif file records
#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP
#define OPENMW_COMPONENTS_NIF_NIFKEY_HPP
#include <OgreStringConverter.h>
#include "nifstream.hpp"
namespace Nif
{
template<typename T>
struct KeyT {
float mTime;
T mValue;
T mForwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList
T mBackwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList
float mTension; // Only for TBC interpolation
float mBias; // Only for TBC interpolation
float mContinuity; // Only for TBC interpolation
};
typedef KeyT<float> FloatKey;
typedef KeyT<Ogre::Vector3> Vector3Key;
typedef KeyT<Ogre::Vector4> Vector4Key;
typedef KeyT<Ogre::Quaternion> QuaternionKey;
template<typename T, T (NIFStream::*getValue)()>
struct KeyListT {
typedef std::vector< KeyT<T> > VecType;
static const unsigned int sLinearInterpolation = 1;
static const unsigned int sQuadraticInterpolation = 2;
static const unsigned int sTBCInterpolation = 3;
static const unsigned int sXYZInterpolation = 4;
unsigned int mInterpolationType;
VecType mKeys;
KeyListT() : mInterpolationType(sLinearInterpolation) {}
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
void read(NIFStream *nif, bool force=false)
{
assert(nif);
mInterpolationType = 0;
size_t count = nif->getUInt();
if(count == 0 && !force)
return;
//If we aren't forcing things, make sure that read clears any previous keys
if(!force)
mKeys.clear();
mInterpolationType = nif->getUInt();
KeyT<T> key;
NIFStream &nifReference = *nif;
if(mInterpolationType == sLinearInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readTimeAndValue(nifReference, key);
mKeys.push_back(key);
}
}
else if(mInterpolationType == sQuadraticInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readQuadratic(nifReference, key);
mKeys.push_back(key);
}
}
else if(mInterpolationType == sTBCInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readTBC(nifReference, key);
mKeys.push_back(key);
}
}
//XYZ keys aren't actually read here.
//data.hpp sees that the last type read was sXYZInterpolation and:
// Eats a floating point number, then
// Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared.
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
else if(mInterpolationType == sXYZInterpolation)
{
//Don't try to read XYZ keys into the wrong part
if ( count != 1 )
nif->file->fail("XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: "+Ogre::StringConverter::toString(count));
}
else if (0 == mInterpolationType)
{
if (count != 0)
nif->file->fail("Interpolation type 0 doesn't work with keys");
}
else
nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType));
}
private:
static void readTimeAndValue(NIFStream &nif, KeyT<T> &key)
{
key.mTime = nif.getFloat();
key.mValue = (nif.*getValue)();
}
static void readQuadratic(NIFStream &nif, KeyT<Ogre::Quaternion> &key)
{
readTimeAndValue(nif, key);
}
template <typename U>
static void readQuadratic(NIFStream &nif, KeyT<U> &key)
{
readTimeAndValue(nif, key);
key.mForwardValue = (nif.*getValue)();
key.mBackwardValue = (nif.*getValue)();
}
static void readTBC(NIFStream &nif, KeyT<T> &key)
{
readTimeAndValue(nif, key);
key.mTension = nif.getFloat();
key.mBias = nif.getFloat();
key.mContinuity = nif.getFloat();
}
};
typedef KeyListT<float,&NIFStream::getFloat> FloatKeyList;
typedef KeyListT<Ogre::Vector3,&NIFStream::getVector3> Vector3KeyList;
typedef KeyListT<Ogre::Vector4,&NIFStream::getVector4> Vector4KeyList;
typedef KeyListT<Ogre::Quaternion,&NIFStream::getQuaternion> QuaternionKeyList;
} // Namespace
#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP

View file

@ -1,6 +1,20 @@
///Functions used to read raw binary data from .nif files
#ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
#define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
#include <stdint.h>
#include <stdexcept>
#include <OgreDataStream.h>
#include <OgreVector2.h>
#include <OgreVector3.h>
#include <OgreVector4.h>
#include <OgreMatrix3.h>
#include <OgreQuaternion.h>
#include "niftypes.hpp"
namespace Nif
{

57
components/nif/node.cpp Normal file
View file

@ -0,0 +1,57 @@
#include "node.hpp"
namespace Nif
{
void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
const Nif::NiMaterialProperty *&matprop,
const Nif::NiAlphaProperty *&alphaprop,
const Nif::NiVertexColorProperty *&vertprop,
const Nif::NiZBufferProperty *&zprop,
const Nif::NiSpecularProperty *&specprop,
const Nif::NiWireframeProperty *&wireprop) const
{
if(parent)
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
for(size_t i = 0;i < props.length();i++)
{
// Entries may be empty
if(props[i].empty())
continue;
const Nif::Property *pr = props[i].getPtr();
if(pr->recType == Nif::RC_NiTexturingProperty)
texprop = static_cast<const Nif::NiTexturingProperty*>(pr);
else if(pr->recType == Nif::RC_NiMaterialProperty)
matprop = static_cast<const Nif::NiMaterialProperty*>(pr);
else if(pr->recType == Nif::RC_NiAlphaProperty)
alphaprop = static_cast<const Nif::NiAlphaProperty*>(pr);
else if(pr->recType == Nif::RC_NiVertexColorProperty)
vertprop = static_cast<const Nif::NiVertexColorProperty*>(pr);
else if(pr->recType == Nif::RC_NiZBufferProperty)
zprop = static_cast<const Nif::NiZBufferProperty*>(pr);
else if(pr->recType == Nif::RC_NiSpecularProperty)
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
else if(pr->recType == Nif::RC_NiWireframeProperty)
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
else
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
}
}
Ogre::Matrix4 Node::getLocalTransform() const
{
Ogre::Matrix4 mat4 = Ogre::Matrix4(Ogre::Matrix4::IDENTITY);
mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation));
return mat4;
}
Ogre::Matrix4 Node::getWorldTransform() const
{
if(parent != NULL)
return parent->getWorldTransform() * getLocalTransform();
return getLocalTransform();
}
}

View file

@ -1,26 +1,3 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (node.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_NODE_HPP
#define OPENMW_COMPONENTS_NIF_NODE_HPP
@ -29,6 +6,7 @@
#include "controlled.hpp"
#include "data.hpp"
#include "property.hpp"
#include "niftypes.hpp"
namespace Nif
{
@ -149,6 +127,14 @@ struct NiNode : Node
Node::read(nif);
children.read(nif);
effects.read(nif);
// Discard tranformations for the root node, otherwise some meshes
// occasionally get wrong orientation. Only for NiNode-s for now, but
// can be expanded if needed.
if (0 == recIndex)
{
static_cast<Nif::Node*>(this)->trafo = Nif::Transformation::getIdentity();
}
}
void post(NIFFile *nif)

View file

@ -1,30 +1,8 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (record_ptr.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_RECORDPTR_HPP
#define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP
#include "niffile.hpp"
#include "nifstream.hpp"
#include <vector>
namespace Nif

View file

@ -1,5 +1 @@
niftool
*_test
*.nif
*.kf
output.txt
*.log

View file

@ -0,0 +1,19 @@
set(NIFTEST
niftest.cpp
)
source_group(components\\nif\\tests FILES ${NIFTEST})
# Main executable
add_executable(niftest
${NIFTEST}
)
target_link_libraries(niftest
${Boost_LIBRARIES}
components
)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(niftest gcov)
endif()

View file

@ -1,12 +0,0 @@
GCC=g++
all: niftool nif_bsa_test
niftool: niftool.cpp ../nif_file.hpp ../nif_file.cpp ../record.hpp
$(GCC) $< ../nif_file.cpp ../../tools/stringops.cpp -o $@
nif_bsa_test: nif_bsa_test.cpp ../nif_file.cpp ../../bsa/bsa_file.cpp ../../tools/stringops.cpp
$(GCC) $^ -o $@
clean:
rm niftool *_test

View file

@ -1,30 +0,0 @@
/*
Runs NIFFile through all the NIFs in Morrowind.bsa.
*/
#include "../nif_file.hpp"
#include "../../bsa/bsa_file.hpp"
#include "../../tools/stringops.hpp"
#include <iostream>
using namespace Mangle::Stream;
using namespace std;
using namespace Nif;
int main(int argc, char **args)
{
BSAFile bsa;
cout << "Reading Morrowind.bsa\n";
bsa.open("../../data/Morrowind.bsa");
const BSAFile::FileList &files = bsa.getList();
for(int i=0; i<files.size(); i++)
{
const char *n = files[i].name;
if(!ends(n, ".nif")) continue;
cout << "Decoding " << n << endl;
NIFFile nif(bsa.getFile(n), n);
}
}

View file

@ -0,0 +1,96 @@
///Program to test .nif files both on the FileSystem and in BSA archives.
#include "../niffile.hpp"
#include "../../bsa/bsa_file.hpp"
#include "../../bsa/bsa_archive.hpp"
#include <OgreRoot.h>
#include <OgreResourceGroupManager.h>
#include <iostream>
#include <algorithm>
#include <exception>
///See if the file has the named extension
bool hasExtension(std::string filename, std::string extensionToFind)
{
std::string extension = filename.substr(filename.find_last_of(".")+1);
//Convert strings to lower case for comparison
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
if(extension == extensionToFind)
return true;
else
return false;
}
///See if the file has the "nif" extension.
bool isNIF(std::string filename)
{
return hasExtension(filename,"nif");
}
///See if the file has the "bsa" extension.
bool isBSA(std::string filename)
{
return hasExtension(filename,"bsa");
}
///Check all the nif files in the given BSA archive
void readBSA(std::string filename)
{
Bsa::BSAFile bsa;
bsa.open(filename.c_str());
const Bsa::BSAFile::FileList &files = bsa.getList();
Bsa::addBSA(filename,"Bsa Files");
for(unsigned int i=0; i<files.size(); i++)
{
std::string name = files[i].name;
if(isNIF(name))
{
//std::cout << "Decoding " << name << std::endl;
Nif::NIFFile temp_nif(name);
}
}
}
int main(int argc, char **argv)
{
//Need this for Ogre's getSingleton
new Ogre::Root("", "", "niftest.log");
Ogre::ResourceGroupManager::getSingleton ().createResourceGroup ("Bsa Files");
//Needed to read files from file system
Ogre::ResourceGroupManager::getSingleton().addResourceLocation("/", "FileSystem");
// Initialize the resource groups:
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
std::cout << "Reading Files" << std::endl;
for(int i = 1; i<argc;i++)
{
std::string name = argv[i];
try{
if(isNIF(name))
{
//std::cout << "Decoding " << name << std::endl;
Nif::NIFFile temp_nif(name);
}
else if(isBSA(name))
{
std::cout << "Reading " << name << std::endl;
readBSA(name);
}
else
{
std::cerr << "ERROR: \"" << name << "\" is not a nif or bsa file!" << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occured" << e.what() << std::endl;
}
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,15 @@
#!/bin/bash
make || exit
#Script to test all nif files (both loose, and in BSA archives) in data files directory
mkdir -p output
DATAFILESDIR="$1"
PROGS=*_test
find "$DATAFILESDIR" -iname *bsa > nifs.txt
find "$DATAFILESDIR" -iname *nif >> nifs.txt
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done
sed -e 's/.*/\"&\"/' nifs.txt > quoted_nifs.txt
xargs --arg-file=quoted_nifs.txt ../../../niftest
rm nifs.txt
rm quoted_nifs.txt

View file

@ -28,6 +28,8 @@ http://www.gnu.org/licenses/ .
#include <components/misc/stringops.hpp>
#include <components/nifcache/nifcache.hpp>
#include "../nif/niffile.hpp"
#include "../nif/node.hpp"
#include "../nif/data.hpp"
@ -74,7 +76,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
// of the early stages of development. Right now we WANT to catch
// every error as early and intrusively as possible, as it's most
// likely a sign of incomplete code rather than faulty input.
Nif::NIFFile::ptr pnif (Nif::NIFFile::create (mResourceName.substr(0, mResourceName.length()-7)));
Nif::NIFFilePtr pnif (Nif::Cache::getInstance().load(mResourceName.substr(0, mResourceName.length()-7)));
Nif::NIFFile & nif = *pnif.get ();
if (nif.numRoots() < 1)
{
@ -388,7 +390,7 @@ bool findBoundingBox (const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::V
bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation)
{
Nif::NIFFile::ptr pnif (Nif::NIFFile::create (nifFile));
Nif::NIFFilePtr pnif (Nif::Cache::getInstance().load(nifFile));
Nif::NIFFile & nif = *pnif.get ();
if (nif.numRoots() < 1)

View file

@ -0,0 +1,40 @@
#include "nifcache.hpp"
namespace Nif
{
Cache* Cache::sThis = 0;
Cache& Cache::getInstance()
{
assert (sThis);
return *sThis;
}
Cache* Cache::getInstancePtr()
{
return sThis;
}
Cache::Cache()
{
assert (!sThis);
sThis = this;
}
NIFFilePtr Cache::load(const std::string &filename)
{
// TODO: normalize file path to make sure we're not loading the same file twice
LoadedMap::iterator it = mLoadedMap.find(filename);
if (it != mLoadedMap.end())
return it->second;
else
{
NIFFilePtr file(new Nif::NIFFile(filename));
mLoadedMap[filename] = file;
return file;
}
}
}

View file

@ -0,0 +1,50 @@
#ifndef OPENMW_COMPONENTS_NIFCACHE_H
#define OPENMW_COMPONENTS_NIFCACHE_H
#include <components/nif/niffile.hpp>
#include <boost/shared_ptr.hpp>
#include <map>
namespace Nif
{
typedef boost::shared_ptr<Nif::NIFFile> NIFFilePtr;
/// @brief A basic resource manager for NIF files
class Cache
{
public:
Cache();
/// Queue this file for background loading. A worker thread will start loading the file.
/// To get the loaded NIFFilePtr, use the load method, which will wait until the worker thread is finished
/// and then return the loaded file.
//void loadInBackground (const std::string& file);
/// Read and parse the given file. May retrieve from cache if this file has been used previously.
/// @note If the file is currently loading in the background, this function will block until
/// the background loading finishes, then return the background loaded file.
/// @note Returns a SharedPtr to the file and the file will stay loaded as long as the user holds on to this pointer.
/// When all external SharedPtrs to a file are released, the cache may decide to unload the file.
NIFFilePtr load (const std::string& filename);
/// Return instance of this class.
static Cache& getInstance();
static Cache* getInstancePtr();
private:
static Cache* sThis;
Cache(const Cache&);
Cache& operator =(const Cache&);
typedef std::map<std::string, NIFFilePtr> LoadedMap;
LoadedMap mLoadedMap;
};
}
#endif

View file

@ -15,6 +15,7 @@
#include <OgreKeyFrame.h>
#include <components/nif/node.hpp>
#include <components/nifcache/nifcache.hpp>
#include <components/misc/stringops.hpp>
#include "material.hpp"
@ -383,7 +384,7 @@ void NIFMeshLoader::loadResource(Ogre::Resource *resource)
Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!");
Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName);
Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(mName);
if(mShapeIndex >= nif->numRecords())
{
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();

View file

@ -45,6 +45,7 @@
#include <extern/shiny/Main/Factory.hpp>
#include <components/nif/node.hpp>
#include <components/nifcache/nifcache.hpp>
#include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
@ -52,6 +53,24 @@
#include "material.hpp"
#include "mesh.hpp"
#include "controller.hpp"
#include "particles.hpp"
namespace
{
void getAllNiNodes(const Nif::Node* node, std::vector<const Nif::NiNode*>& out)
{
const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node);
if (ninode)
{
out.push_back(ninode);
for (unsigned int i=0; i<ninode->children.length(); ++i)
if (!ninode->children[i].empty())
getAllNiNodes(ninode->children[i].getPtr(), out);
}
}
}
namespace NifOgre
{
@ -390,9 +409,10 @@ public:
class Value : public NodeTargetValue<Ogre::Real>, public ValueInterpolator
{
private:
Nif::QuaternionKeyList mRotations;
Nif::Vector3KeyList mTranslations;
Nif::FloatKeyList mScales;
const Nif::QuaternionKeyList* mRotations;
const Nif::Vector3KeyList* mTranslations;
const Nif::FloatKeyList* mScales;
Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid
using ValueInterpolator::interpKey;
@ -420,31 +440,33 @@ public:
}
public:
Value(Ogre::Node *target, const Nif::NiKeyframeData *data)
/// @note The NiKeyFrameData must be valid as long as this KeyframeController exists.
Value(Ogre::Node *target, const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data)
: NodeTargetValue<Ogre::Real>(target)
, mRotations(data->mRotations)
, mTranslations(data->mTranslations)
, mScales(data->mScales)
, mRotations(&data->mRotations)
, mTranslations(&data->mTranslations)
, mScales(&data->mScales)
, mNif(nif)
{ }
virtual Ogre::Quaternion getRotation(float time) const
{
if(mRotations.mKeys.size() > 0)
return interpKey(mRotations.mKeys, time);
if(mRotations->mKeys.size() > 0)
return interpKey(mRotations->mKeys, time);
return mNode->getOrientation();
}
virtual Ogre::Vector3 getTranslation(float time) const
{
if(mTranslations.mKeys.size() > 0)
return interpKey(mTranslations.mKeys, time);
if(mTranslations->mKeys.size() > 0)
return interpKey(mTranslations->mKeys, time);
return mNode->getPosition();
}
virtual Ogre::Vector3 getScale(float time) const
{
if(mScales.mKeys.size() > 0)
return Ogre::Vector3(interpKey(mScales.mKeys, time));
if(mScales->mKeys.size() > 0)
return Ogre::Vector3(interpKey(mScales->mKeys, time));
return mNode->getScale();
}
@ -456,12 +478,12 @@ public:
virtual void setValue(Ogre::Real time)
{
if(mRotations.mKeys.size() > 0)
mNode->setOrientation(interpKey(mRotations.mKeys, time));
if(mTranslations.mKeys.size() > 0)
mNode->setPosition(interpKey(mTranslations.mKeys, time));
if(mScales.mKeys.size() > 0)
mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time)));
if(mRotations->mKeys.size() > 0)
mNode->setOrientation(interpKey(mRotations->mKeys, time));
if(mTranslations->mKeys.size() > 0)
mNode->setPosition(interpKey(mTranslations->mKeys, time));
if(mScales->mKeys.size() > 0)
mNode->setScale(Ogre::Vector3(interpKey(mScales->mKeys, time)));
}
};
@ -898,9 +920,28 @@ 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
// Set the emitter bone(s) as user data on the particle system
// so the emitters/affectors can access it easily.
partsys->getUserObjectBindings().setUserAny(Ogre::Any(trgtbone));
std::vector<Ogre::Bone*> bones;
if (partctrl->recType == Nif::RC_NiBSPArrayController)
{
std::vector<const Nif::NiNode*> nodes;
getAllNiNodes(partctrl->emitter.getPtr(), nodes);
if (nodes.empty())
throw std::runtime_error("Emitter for NiBSPArrayController must be a NiNode");
for (unsigned int i=0; i<nodes.size(); ++i)
{
bones.push_back(scene->mSkelBase->getSkeleton()->getBone(
NIFSkeletonLoader::lookupOgreBoneHandle(name, nodes[i]->recIndex)));
}
}
else
{
bones.push_back(trgtbone);
}
NiNodeHolder holder;
holder.mBones = bones;
partsys->getUserObjectBindings().setUserAny(Ogre::Any(holder));
createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName());
}
@ -916,8 +957,10 @@ class NIFObjectLoader
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
if (partflags&Nif::NiNode::ParticleFlag_AutoPlay)
partsys->fastForward(1, 0.1);
// Emitting state will be overwritten on frame update by the ParticleSystemController,
// but set up an initial value anyway so the user can fast-forward particle systems
// immediately after creation if desired.
partsys->setEmitting(partflags&Nif::NiNode::ParticleFlag_AutoPlay);
}
ctrl = ctrl->next;
}
@ -929,7 +972,7 @@ class NIFObjectLoader
}
static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags)
static void createNodeControllers(const Nif::NIFFilePtr& nif, const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags)
{
do {
if (ctrl->flags & Nif::NiNode::ControllerFlag_Active)
@ -963,7 +1006,7 @@ class NIFObjectLoader
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, nif, key->data.getPtr()));
KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
@ -1016,7 +1059,7 @@ class NIFObjectLoader
}
static void createObjects(const std::string &name, const std::string &group,
static void createObjects(const Nif::NIFFilePtr& nif, const std::string &name, const std::string &group,
Ogre::SceneNode *sceneNode, const Nif::Node *node,
ObjectScenePtr scene, int flags, int animflags, int partflags)
{
@ -1073,7 +1116,7 @@ class NIFObjectLoader
}
if(!node->controller.empty())
createNodeControllers(name, node->controller, scene, animflags);
createNodeControllers(nif, name, node->controller, scene, animflags);
if(node->recType == Nif::RC_NiCamera)
{
@ -1098,7 +1141,7 @@ class NIFObjectLoader
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
createObjects(name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags);
createObjects(nif, name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags);
}
}
}
@ -1121,7 +1164,7 @@ class NIFObjectLoader
public:
static void load(Ogre::SceneNode *sceneNode, ObjectScenePtr scene, const std::string &name, const std::string &group, int flags=0)
{
Nif::NIFFile::ptr nif = Nif::NIFFile::create(name);
Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(name);
if(nif->numRoots() < 1)
{
nif->warn("Found no root nodes in "+name+".");
@ -1145,13 +1188,13 @@ public:
// Create a base skeleton entity if this NIF needs one
createSkelBase(name, group, sceneNode->getCreator(), node, scene);
}
createObjects(name, group, sceneNode, node, scene, flags, 0, 0);
createObjects(nif, name, group, sceneNode, node, scene, flags, 0, 0);
}
static void loadKf(Ogre::Skeleton *skel, const std::string &name,
TextKeyMap &textKeys, std::vector<Ogre::Controller<Ogre::Real> > &ctrls)
{
Nif::NIFFile::ptr nif = Nif::NIFFile::create(name);
Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(name);
if(nif->numRoots() < 1)
{
nif->warn("Found no root nodes in "+name+".");
@ -1202,7 +1245,7 @@ public:
Ogre::Bone *trgtbone = skel->getBone(strdata->string);
Ogre::ControllerValueRealPtr srcval;
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, nif, key->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
ctrls.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));

View file

@ -16,7 +16,7 @@
class NifEmitter : public Ogre::ParticleEmitter
{
public:
Ogre::Bone* mEmitterBone;
std::vector<Ogre::Bone*> mEmitterBones;
Ogre::Bone* mParticleBone;
Ogre::ParticleSystem* getPartSys() { return mParent; }
@ -131,7 +131,8 @@ public:
NifEmitter(Ogre::ParticleSystem *psys)
: Ogre::ParticleEmitter(psys)
{
mEmitterBone = Ogre::any_cast<Ogre::Bone*>(psys->getUserObjectBindings().getUserAny());
mEmitterBones = Ogre::any_cast<NiNodeHolder>(psys->getUserObjectBindings().getUserAny()).mBones;
assert (!mEmitterBones.empty());
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
initDefaults("Nif");
@ -170,8 +171,10 @@ public:
Ogre::Real& timeToLive = particle->timeToLive;
#endif
Ogre::Node* emitterBone = mEmitterBones.at((int)(::rand()/(RAND_MAX+1.0)*mEmitterBones.size()));
position = xOff + yOff + zOff +
mParticleBone->_getDerivedOrientation().Inverse() * (mEmitterBone->_getDerivedPosition()
mParticleBone->_getDerivedOrientation().Inverse() * (emitterBone->_getDerivedPosition()
- mParticleBone->_getDerivedPosition());
// Generate complex data by reference
@ -181,7 +184,7 @@ public:
Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom();
Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom();
direction = (mParticleBone->_getDerivedOrientation().Inverse()
* mEmitterBone->_getDerivedOrientation() *
* emitterBone->_getDerivedOrientation() *
Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) *
Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) *
Ogre::Vector3::UNIT_Z;
@ -635,7 +638,9 @@ public:
, mPosition(0.0f)
, mDirection(0.0f)
{
mEmitterBone = Ogre::any_cast<Ogre::Bone*>(psys->getUserObjectBindings().getUserAny());
std::vector<Ogre::Bone*> bones = Ogre::any_cast<NiNodeHolder>(psys->getUserObjectBindings().getUserAny()).mBones;
assert (!bones.empty());
mEmitterBone = bones[0];
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());

View file

@ -38,4 +38,13 @@ class GravityAffectorFactory : public Ogre::ParticleAffectorFactory
Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys);
};
struct NiNodeHolder
{
std::vector<Ogre::Bone*> mBones;
// Ogre::Any needs this for some reason
friend std::ostream& operator<<(std::ostream& o, const NiNodeHolder& r)
{ return o; }
};
#endif /* OENGINE_OGRE_PARTICLES_H */

View file

@ -6,6 +6,7 @@
#include <OgreBone.h>
#include <components/nif/node.hpp>
#include <components/nifcache/nifcache.hpp>
#include <components/misc/stringops.hpp>
namespace NifOgre
@ -83,7 +84,7 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource)
Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!");
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
Nif::NIFFilePtr nif(Nif::Cache::getInstance().load(skel->getName()));
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
try {

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@ namespace SFO
void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; }
void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; }
void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; }
void setJoyEventCallback(JoyListener* listen) { mJoyListener = listen; }
void setJoyEventCallback(JoyListener* listen) { mJoyListener = listen; }
void capture(bool windowEventsOnly);
bool isModifierHeld(SDL_Keymod mod);
@ -54,7 +54,7 @@ namespace SFO
SFO::MouseListener* mMouseListener;
SFO::KeyListener* mKeyboardListener;
SFO::WindowListener* mWindowListener;
SFO::JoyListener* mJoyListener;
SFO::JoyListener* mJoyListener;
typedef boost::unordered_map<SDL_Keycode, OIS::KeyCode> KeyMap;
KeyMap mKeyMap;

View file

@ -803,7 +803,7 @@ namespace sh
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
if (it->second.getMaterial()->isUnreferenced())
it->second.destroyAll();
it->second.getMaterial()->unreferenceTextures();
}
}

View file

@ -69,6 +69,7 @@ namespace sh
virtual void removeAll () = 0; ///< remove all configurations
virtual bool isUnreferenced() = 0;
virtual void unreferenceTextures() = 0;
virtual void ensureLoaded() = 0;
virtual void setLodLevels (const std::string& lodLevels) = 0;

View file

@ -35,6 +35,11 @@ namespace sh
return (!mMaterial.isNull() && mMaterial.useCount() <= Ogre::ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS+1);
}
void OgreMaterial::unreferenceTextures()
{
mMaterial->unload();
}
OgreMaterial::~OgreMaterial()
{
if (!mMaterial.isNull())

View file

@ -19,6 +19,7 @@ namespace sh
virtual bool createConfiguration (const std::string& name, unsigned short lodIndex);
virtual bool isUnreferenced();
virtual void unreferenceTextures();
virtual void ensureLoaded();
virtual void removeAll ();

View file

@ -6,7 +6,7 @@
<!-- Label -->
<Widget type="TextBox" skin="HeaderText" position="0 0 186 18" name="LabelT" align="Left Top">
<Property key="Caption" value="Choose a Specialization"/>
<Property key="TextAlign" value="Left Top"/>
<Property key="TextAlign" value="HCenter Top"/>
</Widget>
<!-- Attribute list -->

View file

@ -7,6 +7,9 @@
<Widget type="AutoSizedTextBox" skin="NormalText">
<Property key="Caption" value="#{sName}"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp8}"/>
</Widget>
<Widget type="EditBox" skin="MW_TextEdit" position="0 0 30 30" name="NameEdit">
@ -25,6 +28,9 @@
<Widget type="AutoSizedTextBox" skin="NormalText">
<Property key="Caption" value="#{sItem}"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp1}"/>
</Widget>
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="0 0 50 50" name="ItemBox">
</Widget>
@ -33,6 +39,9 @@
<Widget type="AutoSizedTextBox" skin="NormalText">
<Property key="Caption" value="#{sSoulGem}"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp2}"/>
</Widget>
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="0 0 50 50" name="SoulBox">
</Widget>
@ -70,6 +79,9 @@
<!-- Available effects -->
<Widget type="TextBox" skin="NormalText" position="12 108 300 24">
<Property key="Caption" value="#{sMagicEffects}"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp9}"/>
</Widget>
<Widget type="MWList" skin="MW_SimpleList" position="12 136 202 209" name="AvailableEffects">
</Widget>
@ -77,6 +89,9 @@
<!-- Used effects -->
<Widget type="TextBox" skin="NormalText" position="226 108 300 24">
<Property key="Caption" value="#{sEffects}"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp10}"/>
</Widget>
<Widget type="Widget" skin="MW_Box" position="226 136 316 209">
<Widget type="ScrollView" skin="MW_ScrollViewH" position="4 4 308 201" name="UsedEffects">
@ -89,6 +104,9 @@
<Widget type="AutoSizedButton" skin="MW_Button" name="TypeButton">
<Property key="Caption" value="Constant effect"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp7}"/>
</Widget>
<Widget type="Widget">
@ -100,6 +118,9 @@
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" name="PriceLabel">
<Property key="Caption" value="0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="#{sEnchantmentHelp6}"/>
</Widget>

View file

@ -113,7 +113,7 @@
<State name="disabled" colour="0.70 0.57 0.33" shift="0"/>
<State name="normal" colour="0.70 0.57 0.33" shift="0"/>
<State name="highlighted" colour="0.85 0.76 0.60" shift="0"/>
<State name="pushed" colour="0.33 0.38 0.67" shift="0"/>
<State name="pushed" colour="1 1 1" shift="0"/>
<State name="disabled_checked" colour="0.33 0.38 0.67" shift="0"/>
<State name="normal_checked" colour="0.33 0.38 0.67" shift="0"/>
<State name="highlighted_checked" colour="0.33 0.38 0.67" shift="0"/>

View file

@ -65,6 +65,24 @@
</Widget>
</Widget>
<!-- Hand-to-hand tooltip -->
<Widget type="HBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="HandToHandToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="VBox">
<UserString key="VStretch" value="true"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="HandToHandImage"/>
<Widget type="Widget">
<UserString key="VStretch" value="true"/>
</Widget>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="44 8 248 284" align="Left Top" name="HandToHandText">
<Property key="TextAlign" value="Center"/>
</Widget>
</Widget>
<!-- Health tooltip -->
<Widget type="HBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="HealthToolTip">
<Property key="AutoResize" value="true"/>

View file

@ -32,6 +32,7 @@ namespace Render
void SelectionBuffer::setupRenderTarget()
{
mRenderTarget = mTexture->getBuffer()->getRenderTarget();
mRenderTarget->removeAllViewports();
Ogre::Viewport* vp = mRenderTarget->addViewport(mCamera);
vp->setOverlaysEnabled(false);
vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
@ -65,6 +66,7 @@ namespace Render
{
Ogre::MaterialManager::getSingleton ().addListener (this);
mTexture->load();
if (mRenderTarget == NULL)
setupRenderTarget();