1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-22 03:39:40 +00:00

Merge remote-tracking branch 'kcat/animations' into ref

This commit is contained in:
Marc Zinnschlag 2013-04-25 20:12:14 +02:00
commit 2c1796cc74
99 changed files with 10193 additions and 9948 deletions

View file

@ -15,7 +15,7 @@ include (OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 22) set (OPENMW_VERSION_MINOR 23)
set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")

View file

@ -439,7 +439,7 @@ void Record<ESM::Apparatus>::print()
template<> template<>
void Record<ESM::BodyPart>::print() void Record<ESM::BodyPart>::print()
{ {
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Race: " << mData.mRace << std::endl;
std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Type: " << meshTypeLabel(mData.mData.mType) std::cout << " Type: " << meshTypeLabel(mData.mData.mType)
<< " (" << (int)mData.mData.mType << ")" << std::endl; << " (" << (int)mData.mData.mType << ")" << std::endl;

View file

@ -102,3 +102,9 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(omwlauncher gcov) target_link_libraries(omwlauncher gcov)
endif() endif()
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
if (UNIX AND NOT APPLE)
target_link_libraries(omwlauncher dl Xt)
endif()

View file

@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream)
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine().simplified(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).simplified(); QString key = keyRe.cap(1);
QString value = keyRe.cap(2).simplified(); QString value = keyRe.cap(2);
// Don't remove existing data entries // Don't remove existing data entries
if (key != QLatin1String("data")) if (key != QLatin1String("data"))

View file

@ -52,7 +52,7 @@ public:
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine().simplified(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
@ -65,8 +65,8 @@ public:
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).simplified(); QString key = keyRe.cap(1);
QString value = keyRe.cap(2).simplified(); QString value = keyRe.cap(2);
if (!sectionPrefix.isEmpty()) if (!sectionPrefix.isEmpty())
key.prepend(sectionPrefix); key.prepend(sectionPrefix);

View file

@ -122,6 +122,12 @@ if (UNIX AND NOT APPLE)
target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
endif() endif()
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
if (UNIX AND NOT APPLE)
target_link_libraries(openmw dl Xt)
endif()
if(APPLE) if(APPLE)
find_library(CARBON_FRAMEWORK Carbon) find_library(CARBON_FRAMEWORK Carbon)
find_library(COCOA_FRAMEWORK Cocoa) find_library(COCOA_FRAMEWORK Cocoa)

View file

@ -99,6 +99,9 @@ namespace MWBase
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state.
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not ///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored. /// in the scene should be ignored.

View file

@ -49,6 +49,67 @@ namespace
{ {
return new CustomData (*this); return new CustomData (*this);
} }
void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
{
// race bonus
const ESM::Race *race =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc->mRace);
bool male = (npc->mFlags & ESM::NPC::Female) == 0;
int level = creatureStats.getLevel();
for (int i=0; i<ESM::Attribute::Length; ++i)
{
const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];
creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale);
}
// class bonus
const ESM::Class *class_ =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);
for (int i=0; i<2; ++i)
{
int attribute = class_->mData.mAttribute[i];
if (attribute>=0 && attribute<8)
{
creatureStats.getAttribute(attribute).setBase (
creatureStats.getAttribute(attribute).getBase() + 10);
}
}
// skill bonus
for (int attribute=0; attribute<ESM::Attribute::Length; ++attribute)
{
float modifierSum = 0;
for (int j=0; j<ESM::Skill::Length; ++j)
{
const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(j);
if (skill->mData.mAttribute != attribute)
continue;
// is this a minor or major skill?
float add=0.2;
for (int k=0; k<5; ++k)
{
if (class_->mData.mSkills[k][0] == j)
add=0.5;
}
for (int k=0; k<5; ++k)
{
if (class_->mData.mSkills[k][1] == j)
add=1.0;
}
modifierSum += add;
}
creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase()
+ static_cast<int>((level-1) * modifierSum+0.5), 100) );
}
}
} }
namespace MWClass namespace MWClass
@ -126,15 +187,14 @@ namespace MWClass
} }
else else
{ {
for (int i=0; i<8; ++i)
data->mCreatureStats.getAttribute (i).set (10);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
data->mCreatureStats.setDynamic (i, 10); data->mCreatureStats.setDynamic (i, 10);
data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
autoCalculateAttributes(ref->mBase, data->mCreatureStats);
} }
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);

View file

@ -426,6 +426,7 @@ namespace MWDialogue
void DialogueManager::questionAnswered (const std::string& answer) void DialogueManager::questionAnswered (const std::string& answer)
{ {
if (mChoiceMap.find(answer) != mChoiceMap.end()) if (mChoiceMap.find(answer) != mChoiceMap.end())
{ {
mChoice = mChoiceMap[answer]; mChoice = mChoiceMap[answer];
@ -442,6 +443,10 @@ namespace MWDialogue
std::string text = info->mResponse; std::string text = info->mResponse;
parseText (text); parseText (text);
mChoiceMap.clear();
mChoice = -1;
mIsInChoice = false;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
@ -449,9 +454,6 @@ namespace MWDialogue
mLastDialogue = *info; mLastDialogue = *info;
} }
} }
mChoiceMap.clear();
mChoice = -1;
mIsInChoice = false;
} }
updateTopics(); updateTopics();

View file

@ -73,6 +73,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
if (iter->second < info.mData.mRank) if (iter->second < info.mData.mRank)
return false; return false;
} }
else if (info.mData.mRank != -1)
{
// if there is a rank condition, but the NPC is not in a faction, always fail
return false;
}
// Gender // Gender
if (!isCreature) if (!isCreature)

View file

@ -27,7 +27,7 @@ namespace MWDialogue
static std::string idFromIndex (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index);
}; };
/// \biref A quest entry with a timestamp. /// \brief A quest entry with a timestamp.
struct StampedJournalEntry : public JournalEntry struct StampedJournalEntry : public JournalEntry
{ {
int mDay; int mDay;

View file

@ -8,8 +8,6 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/containerstore.hpp"
namespace namespace
{ {

View file

@ -10,7 +10,6 @@
namespace MWGui namespace MWGui
{ {
class AlchemyWindow : public WindowBase, public ContainerBase class AlchemyWindow : public WindowBase, public ContainerBase
{ {
public: public:

View file

@ -3,17 +3,12 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "widgets.hpp" #include "widgets.hpp"
using namespace MWGui;
using namespace Widgets;
namespace namespace
{ {
@ -24,6 +19,9 @@ bool sortBirthSigns(const std::pair<std::string, const ESM::BirthSign*>& left, c
} }
namespace MWGui
{
BirthDialog::BirthDialog() BirthDialog::BirthDialog()
: WindowModal("openmw_chargen_birth.layout") : WindowModal("openmw_chargen_birth.layout")
{ {
@ -168,7 +166,7 @@ void BirthDialog::updateSpells()
if (mCurrentBirthId.empty()) if (mCurrentBirthId.empty())
return; return;
MWSpellPtr spellWidget; Widgets::MWSpellPtr spellWidget;
const int lineHeight = 18; const int lineHeight = 18;
MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18);
@ -179,7 +177,7 @@ void BirthDialog::updateSpells()
store.get<ESM::BirthSign>().find(mCurrentBirthId); store.get<ESM::BirthSign>().find(mCurrentBirthId);
std::string texturePath = std::string("textures\\") + birth->mTexture; std::string texturePath = std::string("textures\\") + birth->mTexture;
fixTexturePath(texturePath); Widgets::fixTexturePath(texturePath);
mBirthImage->setImageTexture(texturePath); mBirthImage->setImageTexture(texturePath);
std::vector<std::string> abilities, powers, spells; std::vector<std::string> abilities, powers, spells;
@ -229,7 +227,7 @@ void BirthDialog::updateSpells()
for (std::vector<std::string>::const_iterator it = categories[category].spells.begin(); it != end; ++it) for (std::vector<std::string>::const_iterator it = categories[category].spells.begin(); it != end; ++it)
{ {
const std::string &spellId = *it; const std::string &spellId = *it;
spellWidget = mSpellArea->createWidget<MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast<std::string>(i)); spellWidget = mSpellArea->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast<std::string>(i));
spellWidget->setSpellId(spellId); spellWidget->setSpellId(spellId);
mSpellItems.push_back(spellWidget); mSpellItems.push_back(spellWidget);
@ -237,7 +235,7 @@ void BirthDialog::updateSpells()
MyGUI::IntCoord spellCoord = coord; MyGUI::IntCoord spellCoord = coord;
spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template?
spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0); spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0);
coord.top = spellCoord.top; coord.top = spellCoord.top;
++i; ++i;
@ -245,3 +243,5 @@ void BirthDialog::updateSpells()
} }
} }
} }
}

View file

@ -12,7 +12,8 @@
#include "formatting.hpp" #include "formatting.hpp"
using namespace MWGui; namespace MWGui
{
BookWindow::BookWindow () BookWindow::BookWindow ()
: WindowBase("openmw_book.layout") : WindowBase("openmw_book.layout")
@ -157,3 +158,5 @@ void BookWindow::updatePages()
++i; ++i;
} }
} }
}

View file

@ -5,8 +5,6 @@
#include "class.hpp" #include "class.hpp"
#include "birth.hpp" #include "birth.hpp"
#include "review.hpp" #include "review.hpp"
#include "dialogue.hpp"
#include "mode.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -45,7 +43,8 @@ namespace
}; };
} }
using namespace MWGui; namespace MWGui
{
CharacterCreation::CharacterCreation() CharacterCreation::CharacterCreation()
: mNameDialog(0) : mNameDialog(0)
@ -724,3 +723,5 @@ CharacterCreation::~CharacterCreation()
delete mBirthSignDialog; delete mBirthSignDialog;
delete mReviewDialog; delete mReviewDialog;
} }
}

View file

@ -1,11 +1,6 @@
#include "class.hpp" #include "class.hpp"
#include <iterator>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -16,7 +11,8 @@
#undef min #undef min
#undef max #undef max
using namespace MWGui; namespace MWGui
{
/* GenerateClassResultDialog */ /* GenerateClassResultDialog */
@ -883,3 +879,5 @@ void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender)
{ {
eventDone(this); eventDone(this);
} }
}

View file

@ -2,7 +2,6 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"

View file

@ -1,10 +1,5 @@
#include "confirmationdialog.hpp" #include "confirmationdialog.hpp"
#include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWGui namespace MWGui
{ {
ConfirmationDialog::ConfirmationDialog() : ConfirmationDialog::ConfirmationDialog() :

View file

@ -1,13 +1,7 @@
#include "console.hpp" #include "console.hpp"
#include <algorithm>
#include <fstream>
#include <components/compiler/exception.hpp> #include <components/compiler/exception.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwscript/extensions.hpp" #include "../mwscript/extensions.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -284,16 +278,15 @@ namespace MWGui
std::string Console::complete( std::string input, std::vector<std::string> &matches ) std::string Console::complete( std::string input, std::vector<std::string> &matches )
{ {
using namespace std; std::string output = input;
string output=input; std::string tmp = input;
string tmp=input;
bool has_front_quote = false; bool has_front_quote = false;
/* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Does the input string contain things that don't have to be completed? If yes erase them. */
/* Are there quotation marks? */ /* Are there quotation marks? */
if( tmp.find('"') != string::npos ) { if( tmp.find('"') != std::string::npos ) {
int numquotes=0; int numquotes=0;
for(string::iterator it=tmp.begin(); it < tmp.end(); ++it) { for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) {
if( *it == '"' ) if( *it == '"' )
numquotes++; numquotes++;
} }
@ -305,7 +298,7 @@ namespace MWGui
} }
else { else {
size_t pos; size_t pos;
if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) { if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('"') ) ) {
tmp.erase( 0, tmp.rfind(' ')+1); tmp.erase( 0, tmp.rfind(' ')+1);
} }
else { else {
@ -317,7 +310,7 @@ namespace MWGui
/* No quotation marks. Are there spaces?*/ /* No quotation marks. Are there spaces?*/
else { else {
size_t rpos; size_t rpos;
if( (rpos=tmp.rfind(' ')) != string::npos ) { if( (rpos=tmp.rfind(' ')) != std::string::npos ) {
if( rpos == 0 ) { if( rpos == 0 ) {
tmp.clear(); tmp.clear();
} }
@ -336,7 +329,7 @@ namespace MWGui
} }
/* Iterate through the vector. */ /* Iterate through the vector. */
for(vector<string>::iterator it=mNames.begin(); it < mNames.end();++it) { for(std::vector<std::string>::iterator it=mNames.begin(); it < mNames.end();++it) {
bool string_different=false; bool string_different=false;
/* Is the string shorter than the input string? If yes skip it. */ /* Is the string shorter than the input string? If yes skip it. */
@ -344,7 +337,7 @@ namespace MWGui
continue; continue;
/* Is the beginning of the string different from the input string? If yes skip it. */ /* Is the beginning of the string different from the input string? If yes skip it. */
for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) {
if( tolower(*iter) != tolower(*iter2) ) { if( tolower(*iter) != tolower(*iter2) ) {
string_different=true; string_different=true;
break; break;
@ -367,24 +360,24 @@ namespace MWGui
/* Only one match. We're done. */ /* Only one match. We're done. */
if( matches.size() == 1 ) { if( matches.size() == 1 ) {
/* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/
if( ( matches.front().find(' ') != string::npos ) ) { if( ( matches.front().find(' ') != std::string::npos ) ) {
if( !has_front_quote ) if( !has_front_quote )
output.append(string("\"")); output.append(std::string("\""));
return output.append(matches.front() + string("\" ")); return output.append(matches.front() + std::string("\" "));
} }
else if( has_front_quote ) { else if( has_front_quote ) {
return output.append(matches.front() + string("\" ")); return output.append(matches.front() + std::string("\" "));
} }
else { else {
return output.append(matches.front() + string(" ")); return output.append(matches.front() + std::string(" "));
} }
} }
/* Check if all matching strings match further than input. If yes complete to this match. */ /* Check if all matching strings match further than input. If yes complete to this match. */
int i = tmp.length(); int i = tmp.length();
for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) {
for(vector<string>::iterator it=matches.begin(); it < matches.end();++it) { for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end();++it) {
if( tolower((*it)[i]) != tolower(*iter) ) { if( tolower((*it)[i]) != tolower(*iter) ) {
/* Append the longest match to the end of the output string*/ /* Append the longest match to the end of the output string*/
output.append(matches.front().substr( 0, i)); output.append(matches.front().substr( 0, i));

View file

@ -1,11 +1,5 @@
#include "container.hpp" #include "container.hpp"
#include <cmath>
#include <algorithm>
#include <iterator>
#include <cassert>
#include <iostream>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -13,23 +7,13 @@
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwclass/container.hpp"
#include "widgets.hpp"
#include "countdialog.hpp" #include "countdialog.hpp"
#include "tradewindow.hpp" #include "tradewindow.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
using namespace MWGui;
using namespace Widgets;
namespace namespace
{ {
bool compareType(std::string type1, std::string type2) bool compareType(std::string type1, std::string type2)
@ -80,6 +64,8 @@ namespace
} }
} }
namespace MWGui
{
ContainerBase::ContainerBase(DragAndDrop* dragAndDrop) ContainerBase::ContainerBase(DragAndDrop* dragAndDrop)
: mDragAndDrop(dragAndDrop) : mDragAndDrop(dragAndDrop)
@ -763,3 +749,5 @@ void ContainerWindow::onReferenceUnavailable()
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
} }
}

View file

@ -2,9 +2,6 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWGui namespace MWGui
{ {
CountDialog::CountDialog() : CountDialog::CountDialog() :

View file

@ -2,13 +2,11 @@
#include <MyGUI_PointerManager.h> #include <MyGUI_PointerManager.h>
#include <MyGUI_InputManager.h> #include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_RotatingSkin.h> #include <MyGUI_RotatingSkin.h>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <OgreMath.h> #include <OgreMath.h>
namespace MWGui namespace MWGui
{ {

View file

@ -1,18 +1,11 @@
#include "dialogue.hpp" #include "dialogue.hpp"
#include <iostream>
#include <iterator>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
@ -26,14 +19,12 @@
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "travelwindow.hpp" #include "travelwindow.hpp"
using namespace MWGui;
using namespace Widgets;
/** /**
*Copied from the internet. *Copied from the internet.
*/ */
namespace { namespace
{
std::string lower_string(const std::string& str) std::string lower_string(const std::string& str)
{ {
@ -53,7 +44,8 @@ bool sortByLength (const std::string& left, const std::string& right)
} }
} }
namespace MWGui
{
PersuasionDialog::PersuasionDialog() PersuasionDialog::PersuasionDialog()
: WindowModal("openmw_persuasion_dialog.layout") : WindowModal("openmw_persuasion_dialog.layout")
@ -544,3 +536,4 @@ void DialogueWindow::onFrame()
mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(disp)+std::string("/100")+"#B29154"); mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(disp)+std::string("/100")+"#B29154");
} }
} }
}

View file

@ -12,8 +12,8 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
using namespace MWGui; namespace MWGui
using namespace Widgets; {
MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos) MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos)
{ {
@ -74,3 +74,5 @@ void DialogueHistory::addDialogText(const MyGUI::UString& parText)
addText(parText); addText(parText);
addText("\n"); addText("\n");
} }
}

View file

@ -3,15 +3,12 @@
#include <components/interpreter/defines.hpp> #include <components/interpreter/defines.hpp>
#include "../mwscript/interpretercontext.hpp" #include "../mwscript/interpretercontext.hpp"
#include "../mwworld/ptr.hpp"
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <OgreUTFString.h> #include <OgreUTFString.h>
using namespace MWGui;
namespace namespace
{ {
int convertFromHex(std::string hex) int convertFromHex(std::string hex)
@ -79,6 +76,9 @@ namespace
} }
} }
namespace MWGui
{
std::vector<std::string> BookTextParser::split(std::string utf8Text, const int width, const int height) std::vector<std::string> BookTextParser::split(std::string utf8Text, const int width, const int height)
{ {
using Ogre::UTFString; using Ogre::UTFString;
@ -363,3 +363,5 @@ void BookTextParser::parseSubText(std::string text)
parseSubText(text.substr(tagStart, text.size())); parseSubText(text.substr(tagStart, text.size()));
} }
} }
}

View file

@ -1,28 +1,19 @@
#include "hud.hpp" #include "hud.hpp"
#include <cmath>
#include <MyGUI_Widget.h>
#include <MyGUI_RenderManager.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwgui/widgets.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "container.hpp"
#include "console.hpp" #include "console.hpp"
#include "spellicons.hpp" #include "spellicons.hpp"
using namespace MWGui; namespace MWGui
{
HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop)
: Layout("openmw_hud.layout") : Layout("openmw_hud.layout")
@ -547,3 +538,5 @@ void HUD::update()
{ {
mSpellIcons->updateWidgets(mEffectBox, true); mSpellIcons->updateWidgets(mEffectBox, true);
} }
}

View file

@ -1,28 +1,15 @@
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include <cmath>
#include <algorithm>
#include <iterator>
#include <cassert>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <components/compiler/locals.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "widgets.hpp"
#include "bookwindow.hpp" #include "bookwindow.hpp"
#include "scrollwindow.hpp" #include "scrollwindow.hpp"
#include "spellwindow.hpp" #include "spellwindow.hpp"

View file

@ -4,9 +4,6 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwdialogue/journalentry.hpp"
namespace namespace
{ {

View file

@ -8,12 +8,10 @@
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/fallback.hpp" #include "../mwworld/fallback.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/stat.hpp"
namespace MWGui namespace MWGui
{ {

View file

@ -5,8 +5,11 @@
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <MyGUI_ScrollBar.h> #include <MyGUI_ScrollBar.h>
using namespace MWGui; namespace MWGui
using namespace MWGui::Widgets; {
namespace Widgets
{
MWList::MWList() : MWList::MWList() :
mClient(0) mClient(0)
@ -161,3 +164,6 @@ size_t MWScrollView::getScrollRange()
{ {
return getVScroll()->getScrollRange(); return getVScroll()->getScrollRange();
} }
}
}

View file

@ -1,25 +1,16 @@
#include "loadingscreen.hpp" #include "loadingscreen.hpp"
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>
#include <OgreRoot.h>
#include <OgreCompositorManager.h> #include <OgreCompositorManager.h>
#include <OgreCompositorChain.h> #include <OgreCompositorChain.h>
#include <OgreMaterial.h>
#include <boost/algorithm/string.hpp>
#include <openengine/ogre/fader.hpp> #include <openengine/ogre/fader.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include <components/esm/records.hpp>
namespace MWGui namespace MWGui
{ {

View file

@ -3,7 +3,6 @@
#include <OgreRoot.h> #include <OgreRoot.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"

View file

@ -2,12 +2,8 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <OgreVector2.h>
#include <OgreTextureManager.h>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <MyGUI_Gui.h>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -17,7 +13,8 @@
#include "widgets.hpp" #include "widgets.hpp"
using namespace MWGui; namespace MWGui
{
LocalMapBase::LocalMapBase() LocalMapBase::LocalMapBase()
: mCurX(0) : mCurX(0)
@ -444,3 +441,5 @@ void MapWindow::notifyMapChanged ()
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
"#{sWorld}"); "#{sWorld}");
} }
}

View file

@ -9,10 +9,7 @@
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "list.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "tradewindow.hpp" #include "tradewindow.hpp"

View file

@ -5,7 +5,8 @@
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
using namespace MWGui; namespace MWGui
{
MessageBoxManager::MessageBoxManager () MessageBoxManager::MessageBoxManager ()
{ {
@ -413,3 +414,5 @@ int InteractiveMessageBox::readPressedButton ()
mButtonPressed = -1; mButtonPressed = -1;
return pressed; return pressed;
} }
}

View file

@ -2,13 +2,9 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp" #include "../mwworld/actionequip.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/spellsuccess.hpp" #include "../mwmechanics/spellsuccess.hpp"
#include "../mwgui/inventorywindow.hpp" #include "../mwgui/inventorywindow.hpp"
#include "../mwgui/bookwindow.hpp" #include "../mwgui/bookwindow.hpp"

View file

@ -1,24 +1,15 @@
#include "race.hpp" #include "race.hpp"
#include <iostream>
#include <iterator>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "widgets.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"
using namespace MWGui;
using namespace Widgets;
namespace namespace
{ {
int wrap(int index, int max) int wrap(int index, int max)
@ -30,45 +21,16 @@ int wrap(int index, int max)
else else
return index; return index;
} }
}
int countParts(const std::string &part, const std::string &race, bool male) namespace MWGui
{ {
/// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs
const MWWorld::Store<ESM::BodyPart> &store =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
std::string prefix =
"b_n_" + race + ((male) ? "_m_" : "_f_") + part;
std::string suffix;
suffix.reserve(prefix.size() + 3);
int count = -1;
do {
++count;
suffix = "_" + (boost::format("%02d") % (count + 1)).str();
}
while (store.search(prefix + suffix) != 0);
if (count == 0 && part == "hair") {
count = -1;
do {
++count;
suffix = (boost::format("%02d") % (count + 1)).str();
}
while (store.search(prefix + suffix) != 0);
}
return count;
}
}
RaceDialog::RaceDialog() RaceDialog::RaceDialog()
: WindowModal("openmw_chargen_race.layout") : WindowModal("openmw_chargen_race.layout")
, mGenderIndex(0) , mGenderIndex(0)
, mFaceIndex(0) , mFaceIndex(0)
, mHairIndex(0) , mHairIndex(0)
, mFaceCount(10)
, mHairCount(14)
, mCurrentAngle(0) , mCurrentAngle(0)
{ {
// Centre dialog // Centre dialog
@ -233,67 +195,28 @@ void RaceDialog::onSelectNextGender(MyGUI::Widget*)
void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) void RaceDialog::onSelectPreviousFace(MyGUI::Widget*)
{ {
do mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size());
mFaceIndex = wrap(mFaceIndex - 1, mFaceCount);
while (!isFacePlayable());
updatePreview(); updatePreview();
} }
void RaceDialog::onSelectNextFace(MyGUI::Widget*) void RaceDialog::onSelectNextFace(MyGUI::Widget*)
{ {
do mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size());
mFaceIndex = wrap(mFaceIndex + 1, mFaceCount);
while (!isFacePlayable());
updatePreview(); updatePreview();
} }
void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) void RaceDialog::onSelectPreviousHair(MyGUI::Widget*)
{ {
do mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size());
mHairIndex = wrap(mHairIndex - 1, mHairCount);
while (!isHairPlayable());
updatePreview(); updatePreview();
} }
void RaceDialog::onSelectNextHair(MyGUI::Widget*) void RaceDialog::onSelectNextHair(MyGUI::Widget*)
{ {
do mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size());
mHairIndex = wrap(mHairIndex + 1, mHairCount);
while (!isHairPlayable());
updatePreview(); updatePreview();
} }
bool RaceDialog::isFacePlayable()
{
std::string prefix =
"b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_");
std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str();
const MWWorld::Store<ESM::BodyPart> &parts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
if (parts.search(prefix + "head_" + headIndex) == 0)
return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
else
return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
}
bool RaceDialog::isHairPlayable()
{
std::string prefix =
"b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_");
std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str();
const MWWorld::Store<ESM::BodyPart> &parts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
if (parts.search(prefix + "hair_" + hairIndex) == 0)
return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
else
return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
}
void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index)
{ {
if (_index == MyGUI::ITEM_NONE) if (_index == MyGUI::ITEM_NONE)
@ -314,18 +237,41 @@ void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index)
updateSpellPowers(); updateSpellPowers();
} }
void RaceDialog::getBodyParts (int part, std::vector<std::string>& out)
{
out.clear();
const MWWorld::Store<ESM::BodyPart> &store =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
for (MWWorld::Store<ESM::BodyPart>::iterator it = store.begin(); it != store.end(); ++it)
{
const ESM::BodyPart& bodypart = *it;
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
continue;
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
continue;
if (bodypart.mData.mPart != static_cast<ESM::BodyPart::MeshPart>(part))
continue;
if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
continue;
bool firstPerson = (bodypart.mId.size() >= 3)
&& bodypart.mId[bodypart.mId.size()-3] == '1'
&& bodypart.mId[bodypart.mId.size()-2] == 's'
&& bodypart.mId[bodypart.mId.size()-1] == 't';
if (firstPerson)
continue;
if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId))
out.push_back(bodypart.mId);
}
}
void RaceDialog::recountParts() void RaceDialog::recountParts()
{ {
mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs);
mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads);
mFaceIndex = 0; mFaceIndex = 0;
mHairIndex = 0; mHairIndex = 0;
while (!isHairPlayable())
mHairIndex = wrap(mHairIndex + 1, mHairCount);
while (!isFacePlayable())
mFaceIndex = wrap(mFaceIndex + 1, mFaceCount);
} }
// update widget content // update widget content
@ -336,21 +282,9 @@ void RaceDialog::updatePreview()
record.mRace = mCurrentRaceId; record.mRace = mCurrentRaceId;
record.setIsMale(mGenderIndex == 0); record.setIsMale(mGenderIndex == 0);
std::string prefix = record.mHead = mAvailableHeads[mFaceIndex];
"b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); record.mHair = mAvailableHairs[mHairIndex];
std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str();
std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str();
record.mHead = prefix + "head_" + headIndex;
record.mHair = prefix + "hair_" + hairIndex;
const MWWorld::Store<ESM::BodyPart> &parts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
if (parts.search(record.mHair) == 0) {
record.mHair = prefix + "hair" + hairIndex;
}
mPreview->setPrototype(record); mPreview->setPrototype(record);
} }
@ -388,7 +322,7 @@ void RaceDialog::updateSkills()
if (mCurrentRaceId.empty()) if (mCurrentRaceId.empty())
return; return;
MWSkillPtr skillWidget; Widgets::MWSkillPtr skillWidget;
const int lineHeight = 18; const int lineHeight = 18;
MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18);
@ -401,10 +335,10 @@ void RaceDialog::updateSkills()
if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes
continue; continue;
skillWidget = mSkillList->createWidget<MWSkill>("MW_StatNameValue", coord1, MyGUI::Align::Default, skillWidget = mSkillList->createWidget<Widgets::MWSkill>("MW_StatNameValue", coord1, MyGUI::Align::Default,
std::string("Skill") + boost::lexical_cast<std::string>(i)); std::string("Skill") + boost::lexical_cast<std::string>(i));
skillWidget->setSkillNumber(skillId); skillWidget->setSkillNumber(skillId);
skillWidget->setSkillValue(MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(race->mData.mBonus[i].mBonus));
ToolTips::createSkillToolTip(skillWidget, skillId); ToolTips::createSkillToolTip(skillWidget, skillId);
@ -425,7 +359,7 @@ void RaceDialog::updateSpellPowers()
if (mCurrentRaceId.empty()) if (mCurrentRaceId.empty())
return; return;
MWSpellPtr spellPowerWidget; Widgets::MWSpellPtr spellPowerWidget;
const int lineHeight = 18; const int lineHeight = 18;
MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18);
@ -437,7 +371,7 @@ void RaceDialog::updateSpellPowers()
for (int i = 0; it != end; ++it) for (int i = 0; it != end; ++it)
{ {
const std::string &spellpower = *it; const std::string &spellpower = *it;
spellPowerWidget = mSpellPowerList->createWidget<MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast<std::string>(i)); spellPowerWidget = mSpellPowerList->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast<std::string>(i));
spellPowerWidget->setSpellId(spellpower); spellPowerWidget->setSpellId(spellpower);
spellPowerWidget->setUserString("ToolTipType", "Spell"); spellPowerWidget->setUserString("ToolTipType", "Spell");
spellPowerWidget->setUserString("Spell", spellpower); spellPowerWidget->setUserString("Spell", spellpower);
@ -448,3 +382,4 @@ void RaceDialog::updateSpellPowers()
++i; ++i;
} }
} }
}

View file

@ -76,8 +76,10 @@ namespace MWGui
void updatePreview(); void updatePreview();
void recountParts(); void recountParts();
bool isHairPlayable(); void getBodyParts (int part, std::vector<std::string>& out);
bool isFacePlayable();
std::vector<std::string> mAvailableHeads;
std::vector<std::string> mAvailableHairs;
MyGUI::ImageBox* mPreviewImage; MyGUI::ImageBox* mPreviewImage;
MyGUI::ListBox* mRaceList; MyGUI::ListBox* mRaceList;
@ -90,7 +92,6 @@ namespace MWGui
std::vector<MyGUI::Widget*> mSpellPowerItems; std::vector<MyGUI::Widget*> mSpellPowerItems;
int mGenderIndex, mFaceIndex, mHairIndex; int mGenderIndex, mFaceIndex, mHairIndex;
int mFaceCount, mHairCount;
std::string mCurrentRaceId; std::string mCurrentRaceId;

View file

@ -18,18 +18,18 @@ namespace MWGui
void ReferenceInterface::checkReferenceAvailable() void ReferenceInterface::checkReferenceAvailable()
{ {
if (mPtr.isEmpty())
return;
MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell();
// check if player has changed cell, or count of the reference has become 0 // check if player has changed cell, or count of the reference has become 0
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)
|| mPtr.getRefData().getCount() == 0) || (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0))
{
if (!mPtr.isEmpty())
{ {
mPtr = MWWorld::Ptr(); mPtr = MWWorld::Ptr();
onReferenceUnavailable(); onReferenceUnavailable();
} }
}
mCurrentPlayerCell = playerCell; mCurrentPlayerCell = playerCell;
} }

View file

@ -1,24 +1,18 @@
#include "review.hpp" #include "review.hpp"
#include <cmath>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "widgets.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"
#undef min #undef min
#undef max #undef max
using namespace MWGui; namespace MWGui
using namespace Widgets; {
const int ReviewDialog::sLineHeight = 18; const int ReviewDialog::sLineHeight = 18;
@ -66,13 +60,13 @@ ReviewDialog::ReviewDialog()
// Setup attributes // Setup attributes
MWAttributePtr attribute; Widgets::MWAttributePtr attribute;
for (int idx = 0; idx < ESM::Attribute::Length; ++idx) for (int idx = 0; idx < ESM::Attribute::Length; ++idx)
{ {
getWidget(attribute, std::string("Attribute") + boost::lexical_cast<std::string>(idx)); getWidget(attribute, std::string("Attribute") + boost::lexical_cast<std::string>(idx));
mAttributeWidgets.insert(std::make_pair(static_cast<int>(ESM::Attribute::sAttributeIds[idx]), attribute)); mAttributeWidgets.insert(std::make_pair(static_cast<int>(ESM::Attribute::sAttributeIds[idx]), attribute));
attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]);
attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0)); attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0));
} }
// Setup skills // Setup skills
@ -161,7 +155,7 @@ void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value)
void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat<int>& value) void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat<int>& value)
{ {
std::map<int, MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId)); std::map<int, Widgets::MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId));
if (attr == mAttributeWidgets.end()) if (attr == mAttributeWidgets.end())
return; return;
@ -371,3 +365,5 @@ void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel)
else else
mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3));
} }
}

View file

@ -10,7 +10,8 @@
#include "formatting.hpp" #include "formatting.hpp"
using namespace MWGui; namespace MWGui
{
ScrollWindow::ScrollWindow () ScrollWindow::ScrollWindow ()
: WindowBase("openmw_scroll.layout") : WindowBase("openmw_scroll.layout")
@ -78,3 +79,4 @@ void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender)
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
} }
}

View file

@ -1,24 +1,18 @@
#include "settingswindow.hpp" #include "settingswindow.hpp"
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <OgrePlugin.h> #include <OgrePlugin.h>
#include <OgreString.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/math/common_factor_rt.hpp> #include <boost/math/common_factor_rt.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "confirmationdialog.hpp" #include "confirmationdialog.hpp"
namespace namespace

View file

@ -1,6 +1,5 @@
#include "soulgemdialog.hpp" #include "soulgemdialog.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "messagebox.hpp" #include "messagebox.hpp"

View file

@ -1,7 +1,5 @@
#include "spellbuyingwindow.hpp" #include "spellbuyingwindow.hpp"
#include <algorithm>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -11,9 +9,7 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"

View file

@ -4,22 +4,14 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/spellsuccess.hpp" #include "../mwmechanics/spellsuccess.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"
#include "widgets.hpp"
#include "class.hpp" #include "class.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "tradewindow.hpp" #include "tradewindow.hpp"

View file

@ -1,9 +1,5 @@
#include "spellicons.hpp" #include "spellicons.hpp"
#include <MyGUI_Widget.h>
#include <MyGUI_Gui.h>
#include <MyGUI_ImageBox.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -14,7 +10,6 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/activespells.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"

View file

@ -1,22 +1,14 @@
#include "spellwindow.hpp" #include "spellwindow.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp" #include "../mwworld/actionequip.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/spellsuccess.hpp" #include "../mwmechanics/spellsuccess.hpp"
#include "spellicons.hpp" #include "spellicons.hpp"

View file

@ -1,14 +1,9 @@
#include "statswindow.hpp" #include "statswindow.hpp"
#include <cmath>
#include <algorithm>
#include <iterator>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
@ -18,8 +13,9 @@
#include "tooltips.hpp" #include "tooltips.hpp"
namespace MWGui
{
using namespace MWGui;
const int StatsWindow::sLineHeight = 18; const int StatsWindow::sLineHeight = 18;
StatsWindow::StatsWindow () StatsWindow::StatsWindow ()
@ -578,3 +574,4 @@ void StatsWindow::onPinToggled()
{ {
MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned);
} }
}

View file

@ -3,7 +3,8 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
using namespace MWGui; namespace MWGui
{
TextInputDialog::TextInputDialog() TextInputDialog::TextInputDialog()
: WindowModal("openmw_text_input.layout") : WindowModal("openmw_text_input.layout")
@ -68,3 +69,5 @@ void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender)
else else
eventDone(this); eventDone(this);
} }
}

View file

@ -2,22 +2,15 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <OgreResourceGroupManager.h>
#include <components/settings/settings.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp"
#include "mapwindow.hpp" #include "mapwindow.hpp"
#include "widgets.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
using namespace MWGui; namespace MWGui
using namespace MyGUI; {
ToolTips::ToolTips() : ToolTips::ToolTips() :
Layout("openmw_tooltips.layout") Layout("openmw_tooltips.layout")
@ -69,7 +62,7 @@ void ToolTips::onFrame(float frameDuration)
mMainWidget->getChildAt(i)->setVisible(false); mMainWidget->getChildAt(i)->setVisible(false);
} }
const IntSize &viewSize = RenderManager::getInstance().getViewSize(); const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize();
if (!mEnabled) if (!mEnabled)
{ {
@ -78,7 +71,7 @@ void ToolTips::onFrame(float frameDuration)
if (!mGameMode) if (!mGameMode)
{ {
const MyGUI::IntPoint& mousePos = InputManager::getInstance().getMousePosition(); const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition();
if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)
|| (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container) || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container)
@ -91,7 +84,7 @@ void ToolTips::onFrame(float frameDuration)
const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject);
IntSize tooltipSize; MyGUI::IntSize tooltipSize;
if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console))
{ {
setCoord(0, 0, 300, 300); setCoord(0, 0, 300, 300);
@ -104,7 +97,7 @@ void ToolTips::onFrame(float frameDuration)
else else
tooltipSize = getToolTipViaPtr(true); tooltipSize = getToolTipViaPtr(true);
IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24);
// make the tooltip stay completely in the viewport // make the tooltip stay completely in the viewport
if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) if ((tooltipPosition.left + tooltipSize.width) > viewSize.width)
@ -121,7 +114,7 @@ void ToolTips::onFrame(float frameDuration)
else else
{ {
const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left);
if (mousePos == lastPressed) // mouseclick makes tooltip disappear if (mousePos == lastPressed) // mouseclick makes tooltip disappear
return; return;
@ -142,11 +135,11 @@ void ToolTips::onFrame(float frameDuration)
if (mRemainingDelay > 0) if (mRemainingDelay > 0)
return; return;
Widget* focus = InputManager::getInstance().getMouseFocusWidget(); MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();
if (focus == 0) if (focus == 0)
return; return;
IntSize tooltipSize; MyGUI::IntSize tooltipSize;
// try to go 1 level up until there is a widget that has tooltip // try to go 1 level up until there is a widget that has tooltip
// this is necessary because some skin elements are actually separate widgets // this is necessary because some skin elements are actually separate widgets
@ -259,7 +252,7 @@ void ToolTips::onFrame(float frameDuration)
else else
throw std::runtime_error ("unknown tooltip type"); throw std::runtime_error ("unknown tooltip type");
IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24);
// make the tooltip stay completely in the viewport // make the tooltip stay completely in the viewport
if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) if ((tooltipPosition.left + tooltipSize.width) > viewSize.width)
@ -278,7 +271,7 @@ void ToolTips::onFrame(float frameDuration)
{ {
if (!mFocusObject.isEmpty()) if (!mFocusObject.isEmpty())
{ {
IntSize tooltipSize = getToolTipViaPtr(); MyGUI::IntSize tooltipSize = getToolTipViaPtr();
setCoord(viewSize.width/2 - tooltipSize.width/2, setCoord(viewSize.width/2 - tooltipSize.width/2,
std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)),
@ -305,12 +298,12 @@ void ToolTips::setFocusObject(const MWWorld::Ptr& focus)
mFocusObject = focus; mFocusObject = focus;
} }
IntSize ToolTips::getToolTipViaPtr (bool image) MyGUI::IntSize ToolTips::getToolTipViaPtr (bool image)
{ {
// this the maximum width of the tooltip before it starts word-wrapping // this the maximum width of the tooltip before it starts word-wrapping
setCoord(0, 0, 300, 300); setCoord(0, 0, 300, 300);
IntSize tooltipSize; MyGUI::IntSize tooltipSize;
const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); const MWWorld::Class& object = MWWorld::Class::get (mFocusObject);
if (!object.hasToolTip(mFocusObject)) if (!object.hasToolTip(mFocusObject))
@ -344,7 +337,7 @@ void ToolTips::findImageExtension(std::string& image)
} }
} }
IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
{ {
mDynamicToolTipBox->setVisible(true); mDynamicToolTipBox->setVisible(true);
@ -378,7 +371,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
// this the maximum width of the tooltip before it starts word-wrapping // this the maximum width of the tooltip before it starts word-wrapping
setCoord(0, 0, 300, 300); setCoord(0, 0, 300, 300);
const IntPoint padding(8, 8); const MyGUI::IntPoint padding(8, 8);
const int maximumWidth = 500; const int maximumWidth = 500;
@ -388,32 +381,32 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
std::string realImage = "icons\\" + image; std::string realImage = "icons\\" + image;
findImageExtension(realImage); findImageExtension(realImage);
EditBox* captionWidget = mDynamicToolTipBox->createWidget<EditBox>("NormalText", IntCoord(0, 0, 300, 300), Align::Left | Align::Top, "ToolTipCaption"); MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
captionWidget->setProperty("Static", "true"); captionWidget->setProperty("Static", "true");
captionWidget->setCaptionWithReplacing(caption); captionWidget->setCaptionWithReplacing(caption);
IntSize captionSize = captionWidget->getTextSize(); MyGUI::IntSize captionSize = captionWidget->getTextSize();
int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize);
EditBox* textWidget = mDynamicToolTipBox->createWidget<EditBox>("SandText", IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), Align::Stretch, "ToolTipText"); MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText");
textWidget->setProperty("Static", "true"); textWidget->setProperty("Static", "true");
textWidget->setProperty("MultiLine", "true"); textWidget->setProperty("MultiLine", "true");
textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false");
textWidget->setCaptionWithReplacing(text); textWidget->setCaptionWithReplacing(text);
textWidget->setTextAlign(Align::HCenter | Align::Top); textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top);
IntSize textSize = textWidget->getTextSize(); MyGUI::IntSize textSize = textWidget->getTextSize();
captionSize += IntSize(imageSize, 0); // adjust for image captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image
IntSize totalSize = IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), MyGUI::IntSize totalSize = MyGUI::IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth),
((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight );
if (!info.effects.empty()) if (!info.effects.empty())
{ {
Widget* effectArea = mDynamicToolTipBox->createWidget<Widget>("", MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>("",
IntCoord(0, totalSize.height, 300, 300-totalSize.height), MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height),
Align::Stretch, "ToolTipEffectArea"); MyGUI::Align::Stretch, "ToolTipEffectArea");
IntCoord coord(0, 6, totalSize.width, 24); MyGUI::IntCoord coord(0, 6, totalSize.width, 24);
/** /**
* \todo * \todo
@ -422,7 +415,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
*/ */
Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget<Widgets::MWEffectList> Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget<Widgets::MWEffectList>
("MW_StatName", coord, Align::Default, "ToolTipEffectsWidget"); ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEffectsWidget");
effectsWidget->setEffectList(info.effects); effectsWidget->setEffectList(info.effects);
std::vector<MyGUI::Widget*> effectItems; std::vector<MyGUI::Widget*> effectItems;
@ -434,14 +427,14 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
if (info.enchant != "") if (info.enchant != "")
{ {
assert(enchant); assert(enchant);
Widget* enchantArea = mDynamicToolTipBox->createWidget<Widget>("", MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>("",
IntCoord(0, totalSize.height, 300, 300-totalSize.height), MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height),
Align::Stretch, "ToolTipEnchantArea"); MyGUI::Align::Stretch, "ToolTipEnchantArea");
IntCoord coord(0, 6, totalSize.width, 24); MyGUI::IntCoord coord(0, 6, totalSize.width, 24);
Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget<Widgets::MWEffectList> Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget<Widgets::MWEffectList>
("MW_StatName", coord, Align::Default, "ToolTipEnchantWidget"); ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEnchantWidget");
enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects));
std::vector<MyGUI::Widget*> enchantEffectItems; std::vector<MyGUI::Widget*> enchantEffectItems;
@ -458,7 +451,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
const int chargeWidth = 204; const int chargeWidth = 204;
TextBox* chargeText = enchantArea->createWidget<TextBox>("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); MyGUI::TextBox* chargeText = enchantArea->createWidget<MyGUI::TextBox>("SandText", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, "ToolTipEnchantChargeText");
chargeText->setCaptionWithReplacing("#{sCharges}"); chargeText->setCaptionWithReplacing("#{sCharges}");
const int chargeTextWidth = chargeText->getTextSize().width + 5; const int chargeTextWidth = chargeText->getTextSize().width + 5;
@ -469,18 +462,18 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18);
IntCoord chargeCoord; MyGUI::IntCoord chargeCoord;
if (totalSize.width < chargeWidth) if (totalSize.width < chargeWidth)
{ {
totalSize.width = chargeWidth; totalSize.width = chargeWidth;
chargeCoord = IntCoord(0, coord.top+6, chargeWidth, 18); chargeCoord = MyGUI::IntCoord(0, coord.top+6, chargeWidth, 18);
} }
else else
{ {
chargeCoord = IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); chargeCoord = MyGUI::IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18);
} }
Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget<Widgets::MWDynamicStat> Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget<Widgets::MWDynamicStat>
("MW_ChargeBar", chargeCoord, Align::Default, "ToolTipEnchantCharge"); ("MW_ChargeBar", chargeCoord, MyGUI::Align::Default, "ToolTipEnchantCharge");
chargeWidget->setValue(charge, charge); chargeWidget->setValue(charge, charge);
totalSize.height += 24; totalSize.height += 24;
} }
@ -504,23 +497,23 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
}else{ }else{
horizontal_scroll = 80 - mHorizontalScrollIndex; horizontal_scroll = 80 - mHorizontalScrollIndex;
} }
captionWidget->setPosition (IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); captionWidget->setPosition (MyGUI::IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top));
} else { } else {
captionWidget->setPosition (captionWidget->getPosition() + padding); captionWidget->setPosition (captionWidget->getPosition() + padding);
} }
textWidget->setPosition (textWidget->getPosition() + IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter
if (image != "") if (image != "")
{ {
ImageBox* imageWidget = mDynamicToolTipBox->createWidget<ImageBox>("ImageBox", MyGUI::ImageBox* imageWidget = mDynamicToolTipBox->createWidget<MyGUI::ImageBox>("ImageBox",
IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), MyGUI::IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize),
Align::Left | Align::Top, "ToolTipImage"); MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipImage");
imageWidget->setImageTexture(realImage); imageWidget->setImageTexture(realImage);
imageWidget->setPosition (imageWidget->getPosition() + padding); imageWidget->setPosition (imageWidget->getPosition() + padding);
} }
totalSize += IntSize(padding.left*2, padding.top*2); totalSize += MyGUI::IntSize(padding.left*2, padding.top*2);
return totalSize; return totalSize;
} }
@ -774,3 +767,5 @@ void ToolTips::setDelay(float delay)
mDelay = delay; mDelay = delay;
mRemainingDelay = mDelay; mRemainingDelay = mDelay;
} }
}

View file

@ -9,7 +9,6 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/manualref.hpp" #include "../mwworld/manualref.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"

View file

@ -1,22 +1,15 @@
#include "travelwindow.hpp" #include "travelwindow.hpp"
#include <algorithm>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <libs/openengine/ogre/fader.hpp> #include <libs/openengine/ogre/fader.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "tradewindow.hpp" #include "tradewindow.hpp"

View file

@ -1,7 +1,5 @@
#include "waitdialog.hpp" #include "waitdialog.hpp"
#include <cmath>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <libs/openengine/ogre/fader.hpp> #include <libs/openengine/ogre/fader.hpp>
@ -11,9 +9,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/timestamp.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"

View file

@ -9,13 +9,13 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/esmstore.hpp"
#undef min #undef min
#undef max #undef max
using namespace MWGui; namespace MWGui
using namespace MWGui::Widgets; {
namespace Widgets
{
/* Helper functions */ /* Helper functions */
@ -23,7 +23,7 @@ using namespace MWGui::Widgets;
* Fixes the filename of a texture path to use the correct .dds extension. * Fixes the filename of a texture path to use the correct .dds extension.
* This is needed on some ESM entries which point to a .tga file instead. * This is needed on some ESM entries which point to a .tga file instead.
*/ */
void MWGui::Widgets::fixTexturePath(std::string &path) void fixTexturePath(std::string &path)
{ {
int offset = path.rfind("."); int offset = path.rfind(".");
if (offset < 0) if (offset < 0)
@ -893,3 +893,5 @@ void VBox::onWidgetCreated(MyGUI::Widget* _widget)
{ {
align(); align();
} }
}
}

View file

@ -1,7 +1,5 @@
#include "windowbase.hpp" #include "windowbase.hpp"
#include <components/settings/settings.hpp>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
using namespace MWGui; using namespace MWGui;

View file

@ -1,39 +1,20 @@
#include "windowmanagerimp.hpp" #include "windowmanagerimp.hpp"
#include <cassert>
#include <iterator>
#include <MyGUI_UString.h>
#include <openengine/ogre/renderer.hpp> #include <openengine/ogre/renderer.hpp>
#include <openengine/gui/manager.hpp> #include <openengine/gui/manager.hpp>
#include <components/settings/settings.hpp>
#include <components/translation/translation.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/cellstore.hpp"
#include "console.hpp" #include "console.hpp"
#include "journalwindow.hpp" #include "journalwindow.hpp"
#include "charactercreation.hpp" #include "charactercreation.hpp"
#include "textinput.hpp"
#include "review.hpp"
#include "dialogue.hpp" #include "dialogue.hpp"
#include "dialoguehistory.hpp" #include "dialoguehistory.hpp"
#include "mapwindow.hpp"
#include "statswindow.hpp" #include "statswindow.hpp"
#include "messagebox.hpp" #include "messagebox.hpp"
#include "container.hpp"
#include "inventorywindow.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"
#include "scrollwindow.hpp" #include "scrollwindow.hpp"
#include "bookwindow.hpp" #include "bookwindow.hpp"
#include "list.hpp"
#include "hud.hpp" #include "hud.hpp"
#include "mainmenu.hpp" #include "mainmenu.hpp"
#include "countdialog.hpp" #include "countdialog.hpp"
@ -48,19 +29,18 @@
#include "loadingscreen.hpp" #include "loadingscreen.hpp"
#include "levelupdialog.hpp" #include "levelupdialog.hpp"
#include "waitdialog.hpp" #include "waitdialog.hpp"
#include "spellcreationdialog.hpp"
#include "enchantingdialog.hpp" #include "enchantingdialog.hpp"
#include "trainingwindow.hpp" #include "trainingwindow.hpp"
#include "imagebutton.hpp"
#include "exposedwindow.hpp" #include "exposedwindow.hpp"
#include "cursor.hpp" #include "cursor.hpp"
#include "spellicons.hpp"
#include "merchantrepair.hpp" #include "merchantrepair.hpp"
#include "repair.hpp" #include "repair.hpp"
#include "soulgemdialog.hpp" #include "soulgemdialog.hpp"
#include "companionwindow.hpp" #include "companionwindow.hpp"
#include "inventorywindow.hpp"
using namespace MWGui; namespace MWGui
{
WindowManager::WindowManager( WindowManager::WindowManager(
const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *ogre, const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *ogre,
@ -1200,3 +1180,5 @@ void WindowManager::frameStarted (float dt)
{ {
mInventoryWindow->doRenderUpdate (); mInventoryWindow->doRenderUpdate ();
} }
}

View file

@ -1,11 +1,9 @@
#include "windowpinnablebase.hpp" #include "windowpinnablebase.hpp"
#include "../mwbase/windowmanager.hpp"
#include "exposedwindow.hpp" #include "exposedwindow.hpp"
using namespace MWGui; namespace MWGui
{
WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) WindowPinnableBase::WindowPinnableBase(const std::string& parLayout)
: WindowBase(parLayout), mPinned(false), mVisible(false) : WindowBase(parLayout), mPinned(false), mVisible(false)
{ {
@ -26,3 +24,4 @@ void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender)
onPinToggled(); onPinToggled();
} }
}

View file

@ -290,6 +290,13 @@ namespace MWMechanics
return 0; return 0;
} }
void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second.forceStateUpdate();
}
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{ {
PtrControllerMap::iterator iter = mActors.find(ptr); PtrControllerMap::iterator iter = mActors.find(ptr);

View file

@ -78,6 +78,8 @@ namespace MWMechanics
int countDeaths (const std::string& id) const; int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID. ///< Return the number of deaths for actors with the given ID.
void forceStateUpdate(const MWWorld::Ptr &ptr);
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr); void skipAnimation(const MWWorld::Ptr& ptr);
}; };

View file

@ -103,14 +103,13 @@ static void getStateInfo(CharacterState state, std::string *group)
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop)
: mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) : mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false)
{ {
if(!mAnimation) if(!mAnimation)
return; return;
mAnimation->setController(this); std::string group;
getStateInfo(mCharState, &group);
getStateInfo(mState, &mCurrentGroup);
if(MWWorld::Class::get(mPtr).isActor()) if(MWWorld::Class::get(mPtr).isActor())
{ {
/* Accumulate along X/Y only for now, until we can figure out how we should /* Accumulate along X/Y only for now, until we can figure out how we should
@ -122,19 +121,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
/* Don't accumulate with non-actors. */ /* Don't accumulate with non-actors. */
mAnimation->setAccumulation(Ogre::Vector3(0.0f)); mAnimation->setAccumulation(Ogre::Vector3(0.0f));
} }
if(mAnimation->hasAnimation(mCurrentGroup)) if(mAnimation->hasAnimation(group))
mAnimation->play(mCurrentGroup, "stop", "stop", loop); mMovingAnim = mAnimation->play(group, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0);
}
CharacterController::CharacterController(const CharacterController &rhs)
: mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue)
, mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState)
, mSkipAnim(rhs.mSkipAnim)
{
if(!mAnimation)
return;
/* We've been copied. Update the animation with the new controller. */
mAnimation->setController(this);
} }
CharacterController::~CharacterController() CharacterController::~CharacterController()
@ -148,31 +136,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
} }
void CharacterController::markerEvent(float time, const std::string &evt)
{
if(evt == "stop")
{
if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1])
{
mAnimQueue.pop_front();
mAnimation->play(mCurrentGroup, "loop start", "stop", false);
}
else if(mAnimQueue.size() > 0)
{
mAnimQueue.pop_front();
if(mAnimQueue.size() > 0)
{
mCurrentGroup = mAnimQueue.front();
mAnimation->play(mCurrentGroup, "start", "stop", false);
}
}
return;
}
std::cerr<< "Unhandled animation event: "<<evt <<std::endl;
}
void CharacterController::update(float duration, Movement &movement) void CharacterController::update(float duration, Movement &movement)
{ {
float speed = 0.0f; float speed = 0.0f;
@ -214,11 +177,13 @@ void CharacterController::update(float duration, Movement &movement)
if(vec.x > 0.0f) if(vec.x > 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)
: (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true); : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true);
else if(vec.x < 0.0f) else if(vec.x < 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft)
: (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true); : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true);
// If this animation isn't moving us sideways, do it manually
if(!mMovingAnim)
movement.mPosition[0] += vec.x * (speed*duration);
// Apply any forward/backward movement manually // Apply any forward/backward movement manually
movement.mPosition[1] += vec.y * (speed*duration); movement.mPosition[1] += vec.y * (speed*duration);
} }
@ -227,12 +192,15 @@ void CharacterController::update(float duration, Movement &movement)
if(vec.y > 0.0f) if(vec.y > 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)
: (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true); : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true);
else if(vec.y < 0.0f) else if(vec.y < 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)
: (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true); : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true);
// Apply any sideways movement manually // Apply any sideways movement manually
movement.mPosition[0] += vec.x * (speed*duration); movement.mPosition[0] += vec.x * (speed*duration);
// If this animation isn't moving us forward/backward, do it manually
if(!mMovingAnim)
movement.mPosition[1] += vec.y * (speed*duration);
} }
else if(rot.z != 0.0f && !inwater && !sneak) else if(rot.z != 0.0f && !inwater && !sneak)
{ {
@ -241,8 +209,18 @@ void CharacterController::update(float duration, Movement &movement)
else if(rot.z < 0.0f) else if(rot.z < 0.0f)
setState(CharState_TurnLeft, true); setState(CharState_TurnLeft, true);
} }
else if(mAnimQueue.size() == 0) else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0))
{
if(mAnimQueue.size() == 0)
setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true);
else
{
mMovingAnim = mAnimation->play(mAnimQueue.front().first,
"start", "stop", 0.0f,
mAnimQueue.front().second);
mAnimQueue.pop_front();
}
}
movement.mRotation[0] += rot.x * duration; movement.mRotation[0] += rot.x * duration;
movement.mRotation[1] += rot.y * duration; movement.mRotation[1] += rot.y * duration;
@ -268,20 +246,17 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int
else else
{ {
count = std::max(count, 1); count = std::max(count, 1);
if(mode != 0 || mAnimQueue.size() == 0) if(mode != 0 || getState() != CharState_SpecialIdle)
{ {
mAnimQueue.clear(); mAnimQueue.clear();
while(count-- > 0) mCharState = CharState_SpecialIdle;
mAnimQueue.push_back(groupname); mLooping = false;
mCurrentGroup = groupname; mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1);
mState = CharState_SpecialIdle;
mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false);
} }
else if(mode == 0) else if(mode == 0)
{ {
mAnimQueue.resize(1); mAnimQueue.clear();
while(count-- > 0) mAnimQueue.push_back(std::make_pair(groupname, count-1));
mAnimQueue.push_back(groupname);
} }
} }
} }
@ -294,25 +269,24 @@ void CharacterController::skipAnim()
void CharacterController::setState(CharacterState state, bool loop) void CharacterController::setState(CharacterState state, bool loop)
{ {
if(mState == state) if(mCharState == state)
{
if(mAnimation)
mAnimation->setLooping(loop);
return; return;
} mCharState = state;
mState = state; mLooping = loop;
forceStateUpdate();
}
void CharacterController::forceStateUpdate()
{
if(!mAnimation) if(!mAnimation)
return; return;
mAnimQueue.clear(); mAnimQueue.clear();
std::string anim; std::string anim;
getStateInfo(mState, &anim); getStateInfo(mCharState, &anim);
if(mAnimation->hasAnimation(anim)) if((mMovingAnim=mAnimation->hasAnimation(anim)) != false)
{ mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, mLooping ? (~(size_t)0) : 0);
mCurrentGroup = anim;
mAnimation->play(mCurrentGroup, "start", "stop", loop);
}
} }
} }

View file

@ -72,22 +72,17 @@ class CharacterController
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
MWRender::Animation *mAnimation; MWRender::Animation *mAnimation;
typedef std::deque<std::string> AnimationQueue; typedef std::deque<std::pair<std::string,size_t> > AnimationQueue;
AnimationQueue mAnimQueue; AnimationQueue mAnimQueue;
std::string mCurrentGroup; CharacterState mCharState;
CharacterState mState; bool mLooping;
bool mSkipAnim; bool mSkipAnim;
protected: bool mMovingAnim;
/* Called by the animation whenever a new text key is reached. */
void markerEvent(float time, const std::string &evt);
friend class MWRender::Animation;
public: public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop);
CharacterController(const CharacterController &rhs);
virtual ~CharacterController(); virtual ~CharacterController();
void updatePtr(const MWWorld::Ptr &ptr); void updatePtr(const MWWorld::Ptr &ptr);
@ -99,7 +94,9 @@ public:
void setState(CharacterState state, bool loop); void setState(CharacterState state, bool loop);
CharacterState getState() const CharacterState getState() const
{ return mState; } { return mCharState; }
void forceStateUpdate();
}; };
} }

View file

@ -548,7 +548,7 @@ namespace MWMechanics
float bribeMod; float bribeMod;
if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat();
if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat();
else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); else bribeMod = gmst.find("fBribe1000Mod")->getFloat();
float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod;
@ -654,6 +654,12 @@ namespace MWMechanics
} }
} }
void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr)
{
if(MWWorld::Class::get(ptr).isActor())
mActors.forceStateUpdate(ptr);
}
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{ {
if(MWWorld::Class::get(ptr).isActor()) if(MWWorld::Class::get(ptr).isActor())

View file

@ -96,6 +96,8 @@ namespace MWMechanics
void toLower(std::string npcFaction); void toLower(std::string npcFaction);
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr); virtual void skipAnimation(const MWWorld::Ptr& ptr);
}; };

View file

@ -1,5 +1,5 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include <boost/graph/astar_search.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/adjacency_list.hpp> #include <boost/graph/adjacency_list.hpp>
#include "boost/tuple/tuple.hpp" #include "boost/tuple/tuple.hpp"
#include "OgreMath.h" #include "OgreMath.h"
@ -55,7 +55,7 @@ namespace
struct found_path {}; struct found_path {};
class goalVisited : public boost::default_astar_visitor /*class goalVisited : public boost::default_astar_visitor
{ {
public: public:
goalVisited(PointID goal) : mGoal(goal) {} goalVisited(PointID goal) : mGoal(goal) {}
@ -69,7 +69,7 @@ namespace
PointID mGoal; PointID mGoal;
}; };
class DistanceHeuristic : public boost::astar_heuristic <PathGridGraph, float> class DistanceHeuristic : public boost::atasr_heuristic <PathGridGraph, float>
{ {
public: public:
DistanceHeuristic(const PathGridGraph & l, PointID goal) DistanceHeuristic(const PathGridGraph & l, PointID goal)
@ -87,11 +87,23 @@ namespace
private: private:
const PathGridGraph & mGraph; const PathGridGraph & mGraph;
PointID mGoal; PointID mGoal;
}; };*/
}
namespace MWMechanics class goalVisited : public boost::default_dijkstra_visitor
{ {
public:
goalVisited(PointID goal) : mGoal(goal) {}
void examine_vertex(PointID u, const PathGridGraph g)
{
if(u == mGoal)
throw found_path();
}
private:
PointID mGoal;
};
PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0)
{ {
PathGridGraph graph; PathGridGraph graph;
@ -126,11 +138,10 @@ namespace MWMechanics
std::list<ESM::Pathgrid::Point> shortest_path; std::list<ESM::Pathgrid::Point> shortest_path;
try { try {
boost::astar_search boost::dijkstra_shortest_paths
( (
graph, graph,
start, start,
DistanceHeuristic(graph,end),
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph)) boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph))
); );
@ -146,6 +157,10 @@ namespace MWMechanics
//end of helpers functions //end of helpers functions
}
namespace MWMechanics
{
PathFinder::PathFinder() PathFinder::PathFinder()
{ {
mIsPathConstructed = false; mIsPathConstructed = false;

View file

@ -1,10 +1,5 @@
#include "activatoranimation.hpp" #include "activatoranimation.hpp"
#include <OgreEntity.h>
#include <OgreParticleSystem.h>
#include <OgreSceneManager.h>
#include <OgreSubEntity.h>
#include "renderconst.hpp" #include "renderconst.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -24,28 +19,10 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
assert (ref->mBase != NULL); assert (ref->mBase != NULL);
if(!ref->mBase->mModel.empty()) if(!ref->mBase->mModel.empty())
{ {
std::string mesh = "meshes\\" + ref->mBase->mModel; const std::string name = "meshes\\"+ref->mBase->mModel;
createObjectList(mPtr.getRefData().getBaseNode(), mesh); addObjectList(mPtr.getRefData().getBaseNode(), name, false);
for(size_t i = 0;i < mObjectList.mEntities.size();i++) setRenderProperties(mObjects.back().mObjectList, RV_Misc, RQG_Main, RQG_Alpha);
{
Ogre::Entity *ent = mObjectList.mEntities[i];
ent->setVisibilityFlags(RV_Misc);
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
{
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
part->setVisibilityFlags(RV_Misc);
part->setRenderQueueGroup(RQG_Alpha);
}
setAnimationSource(mesh);
} }
} }

View file

@ -94,6 +94,9 @@ void Actors::insertActivator (const MWWorld::Ptr& ptr)
bool Actors::deleteObject (const MWWorld::Ptr& ptr) bool Actors::deleteObject (const MWWorld::Ptr& ptr)
{ {
if (mAllActors.find(ptr) == mAllActors.end())
return false;
mRendering->removeWaterRippleEmitter (ptr); mRendering->removeWaterRippleEmitter (ptr);
delete mAllActors[ptr]; delete mAllActors[ptr];
@ -139,6 +142,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store)
Ogre::SceneNode *base = celliter->second; Ogre::SceneNode *base = celliter->second;
base->removeAndDestroyAllChildren(); base->removeAndDestroyAllChildren();
mRend.getScene()->destroySceneNode(base); mRend.getScene()->destroySceneNode(base);
mCellSceneNodes.erase(celliter); mCellSceneNodes.erase(celliter);
} }
} }

View file

@ -3,6 +3,8 @@
#include <OgreSkeletonManager.h> #include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h> #include <OgreSkeletonInstance.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreParticleSystem.h>
#include <OgreBone.h> #include <OgreBone.h>
#include <OgreSubMesh.h> #include <OgreSubMesh.h>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
@ -16,6 +18,32 @@
namespace MWRender namespace MWRender
{ {
Animation::AnimLayer::AnimLayer()
: mControllers(NULL)
, mTextKeys(NULL)
, mTime(0.0f)
, mPlaying(false)
, mLoopCount(0)
{
}
Ogre::Real Animation::AnimationValue::getValue() const
{
size_t idx = mIndex;
while(idx > 0 && mAnimation->mLayer[idx].mGroupName.empty())
idx--;
if(!mAnimation->mLayer[idx].mGroupName.empty())
return mAnimation->mLayer[idx].mTime;
return 0.0f;
}
void Animation::AnimationValue::setValue(Ogre::Real value)
{
mAnimation->mLayer[mIndex].mTime = value;
}
void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects)
{ {
for(size_t i = 0;i < objects.mParticles.size();i++) for(size_t i = 0;i < objects.mParticles.size();i++)
@ -31,22 +59,22 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL
Animation::Animation(const MWWorld::Ptr &ptr) Animation::Animation(const MWWorld::Ptr &ptr)
: mPtr(ptr) : mPtr(ptr)
, mController(NULL)
, mInsert(NULL) , mInsert(NULL)
, mSkelBase(NULL)
, mAccumRoot(NULL) , mAccumRoot(NULL)
, mNonAccumRoot(NULL) , mNonAccumRoot(NULL)
, mAccumulate(Ogre::Vector3::ZERO) , mNonAccumCtrl(NULL)
, mAccumulate(0.0f)
, mLastPosition(0.0f) , mLastPosition(0.0f)
, mCurrentControllers(NULL)
, mCurrentKeys(NULL)
, mCurrentAnim(NULL)
, mCurrentTime(0.0f)
, mStopTime(0.0f)
, mPlaying(false)
, mLooping(false)
, mAnimVelocity(0.0f) , mAnimVelocity(0.0f)
, mAnimSpeedMult(1.0f) , mAnimSpeedMult(1.0f)
{ {
for(size_t i = 0;i < sMaxLayers;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this, i));
/* As long as we remain under 128 active controllers, we can avoid
* reallocations. */
mActiveCtrls.reserve(128);
} }
Animation::~Animation() Animation::~Animation()
@ -54,105 +82,34 @@ Animation::~Animation()
if(mInsert) if(mInsert)
{ {
Ogre::SceneManager *sceneMgr = mInsert->getCreator(); Ogre::SceneManager *sceneMgr = mInsert->getCreator();
destroyObjectList(sceneMgr, mObjectList); for(size_t i = 0;i < mObjects.size();i++)
destroyObjectList(sceneMgr, mObjects[i].mObjectList);
for(size_t i = 0;i < mAnimationSources.size();i++) mObjects.clear();
destroyObjectList(sceneMgr, mAnimationSources[i]);
mAnimationSources.clear();
} }
} }
void Animation::setAnimationSources(const std::vector<std::string> &names) void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly)
{ {
if(!mObjectList.mSkelBase) if(!mInsert)
return;
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
mCurrentControllers = &mObjectList.mControllers;
mCurrentAnim = NULL;
mCurrentKeys = NULL;
mAnimVelocity = 0.0f;
mAccumRoot = NULL;
mNonAccumRoot = NULL;
mTextKeys.clear();
for(size_t i = 0;i < mAnimationSources.size();i++)
destroyObjectList(sceneMgr, mAnimationSources[i]);
mAnimationSources.clear();
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton();
std::vector<std::string>::const_iterator nameiter;
for(nameiter = names.begin();nameiter != names.end();nameiter++)
{
mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter));
if(!mAnimationSources.back().mSkelBase)
{
std::cerr<< "Failed to get skeleton source "<<*nameiter <<std::endl;
destroyObjectList(sceneMgr, mAnimationSources.back());
mAnimationSources.pop_back();
continue;
}
NifOgre::ObjectList &objects = mAnimationSources.back();
for(size_t i = 0;i < objects.mControllers.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objects.mControllers[i].getDestination().getPointer());
if(!dstval) continue;
const Ogre::String &trgtname = dstval->getNode()->getName();
if(!skelinst->hasBone(trgtname)) continue;
Ogre::Bone *bone = skelinst->getBone(trgtname);
dstval->setNode(bone);
}
for(size_t i = 0;i < objects.mControllers.size();i++)
{
if(objects.mControllers[i].getSource().isNull())
objects.mControllers[i].setSource(ctrlval);
}
Ogre::Entity *ent = objects.mSkelBase;
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName());
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator();
while(boneiter.hasMoreElements())
{
Ogre::Bone *bone = boneiter.getNext();
Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings();
const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID);
if(data.isEmpty() || !Ogre::any_cast<bool>(data))
continue;
if(!mNonAccumRoot)
{
mAccumRoot = mInsert;
mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName());
}
for(int i = 0;i < skel->getNumAnimations();i++)
{
Ogre::Animation *anim = skel->getAnimation(i);
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+
"@"+anim->getName());
if(!groupdata.isEmpty())
mTextKeys[anim->getName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata);
}
break;
}
}
}
void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model)
{ {
mInsert = node->createChildSceneNode(); mInsert = node->createChildSceneNode();
assert(mInsert); assert(mInsert);
}
mObjectList = NifOgre::Loader::createObjects(mInsert, model); mObjects.push_back(ObjectInfo());
if(mObjectList.mSkelBase) ObjectInfo &obj = mObjects.back();
obj.mActiveLayers = 0;
obj.mObjectList = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
NifOgre::Loader::createObjectBase(mInsert, model));
NifOgre::ObjectList &objlist = obj.mObjectList;
if(objlist.mSkelBase)
{ {
Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates(); if(mObjects.size() == 1)
mSkelBase = objlist.mSkelBase;
Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements()) while(asiter.hasMoreElements())
{ {
@ -164,27 +121,184 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model
// Set the bones as manually controlled since we're applying the // Set the bones as manually controlled since we're applying the
// transformations manually (needed if we want to apply an animation // transformations manually (needed if we want to apply an animation
// from one skeleton onto another). // from one skeleton onto another).
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton(); Ogre::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements()) while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true); boneiter.getNext()->setManuallyControlled(true);
} }
if(objlist.mSkelBase && mSkelBase)
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
for(size_t i = 0;i < mObjectList.mControllers.size();i++)
{ {
if(mObjectList.mControllers[i].getSource().isNull()) Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
mObjectList.mControllers[i].setSource(ctrlval); if(mSkelBase == objlist.mSkelBase)
{
if(objlist.mTextKeys.size() > 0)
{
mAccumRoot = mInsert;
mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first);
}
}
else
{
for(size_t i = 0;i < objlist.mControllers.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
if(!dstval) continue;
const Ogre::String &trgtname = dstval->getNode()->getName();
if(!baseinst->hasBone(trgtname)) continue;
Ogre::Bone *bone = baseinst->getBone(trgtname);
dstval->setNode(bone);
}
}
}
for(size_t i = 0;i < objlist.mControllers.size();i++)
{
if(objlist.mControllers[i].getSource().isNull())
objlist.mControllers[i].setSource(mAnimationValuePtr[0]);
}
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
}
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
{
for(size_t i = 0;i < objlist.mEntities.size();i++)
{
Ogre::Entity *ent = objlist.mEntities[i];
if(visflags != 0)
ent->setVisibilityFlags(visflags);
for(unsigned int j = 0;j < ent->getNumSubEntities();++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue);
}
}
for(size_t i = 0;i < objlist.mParticles.size();i++)
{
Ogre::ParticleSystem *part = objlist.mParticles[i];
if(visflags != 0)
part->setVisibilityFlags(visflags);
// TODO: Check particle material for actual transparency
part->setRenderQueueGroup(transqueue);
}
}
void Animation::clearExtraSources()
{
for(size_t layer = 0;layer < sMaxLayers;layer++)
{
mLayer[layer].mGroupName.clear();
mLayer[layer].mTextKeys = NULL;
mLayer[layer].mControllers = NULL;
mLayer[layer].mTime = 0.0f;
mLayer[layer].mLoopCount = 0;
mLayer[layer].mPlaying = false;
}
mNonAccumCtrl = NULL;
mAnimVelocity = 0.0f;
mLastPosition = Ogre::Vector3(0.0f);
if(mAccumRoot)
mAccumRoot->setPosition(mLastPosition);
if(mObjects.size() > 1)
{
mObjects.resize(1);
mObjects[0].mActiveLayers = 0;
NifOgre::ObjectList &objlist = mObjects[0].mObjectList;
mActiveCtrls.clear();
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
}
}
void Animation::updateActiveControllers()
{
mActiveCtrls.clear();
/* First, get all controllers that don't target a node, or that target
* nodes that don't belong to any particular layer.
*/
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
for(;obj != mObjects.end();obj++)
{
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(obj->mObjectList.mControllers.begin());
for(;ctrl != obj->mObjectList.mControllers.end();ctrl++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
if(dstval)
{
/*if(getLayerByName(dstval->getNode()->getName()) >= 0)*/
continue;
}
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
}
}
std::vector<Ogre::Controller<Ogre::Real> > *ctrls = NULL;
size_t layer = 0;
while(layer < sMaxLayers)
{
/* Now get controllers that target nodes that belong to this layer from
* whatever objectlist is active on this layer.
*/
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
for(;obj != mObjects.end();obj++)
{
if((obj->mActiveLayers&(1<<layer)))
{
ctrls = &obj->mObjectList.mControllers;
break;
}
}
if(ctrls == NULL)
{
layer++;
continue;
}
/* Check if any objectlists are active on subsequent layers. Include
* those layers if not.
*/
size_t nextlayer = layer+1;
for(;nextlayer < sMaxLayers;nextlayer++)
{
for(obj = mObjects.begin();obj != mObjects.end();obj++)
{
if((obj->mActiveLayers&(1<<nextlayer)))
break;
}
}
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(ctrls->begin());
for(;ctrl != ctrls->end();ctrl++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
if(dstval)
{
/*ssize_t idx = getLayerByName(dstval->getNode()->getName());
if(idx >= (ssize_t)layer && idx < (ssize_t)nextlayer)*/
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
}
}
layer = nextlayer;
} }
mCurrentControllers = &mObjectList.mControllers;
} }
Ogre::Node *Animation::getNode(const std::string &name) Ogre::Node *Animation::getNode(const std::string &name)
{ {
if(mObjectList.mSkelBase) if(mSkelBase)
{ {
Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton(); Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton();
if(skel->hasBone(name)) if(skel->hasBone(name))
return skel->getBone(name); return skel->getBone(name);
} }
@ -192,23 +306,37 @@ Ogre::Node *Animation::getNode(const std::string &name)
} }
NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname)
{
NifOgre::TextKeyMap::const_iterator iter(keys.begin());
for(;iter != keys.end();iter++)
{
if(iter->second.compare(0, groupname.size(), groupname) == 0 &&
iter->second.compare(groupname.size(), 2, ": ") == 0)
break;
}
return iter;
}
bool Animation::hasAnimation(const std::string &anim) bool Animation::hasAnimation(const std::string &anim)
{ {
for(std::vector<NifOgre::ObjectList>::const_iterator iter(mAnimationSources.begin());iter != mAnimationSources.end();iter++) if(!mSkelBase)
return false;
for(std::vector<ObjectInfo>::const_iterator iter(mObjects.begin());iter != mObjects.end();iter++)
{ {
if(iter->mSkelBase->hasAnimationState(anim)) if(iter->mObjectList.mTextKeys.size() == 0)
continue;
const NifOgre::TextKeyMap &keys = iter->mObjectList.mTextKeys.begin()->second;
if(findGroupStart(keys, anim) != keys.end())
return true; return true;
} }
return false; return false;
} }
void Animation::setController(MWMechanics::CharacterController *controller)
{
mController = controller;
}
void Animation::setAccumulation(const Ogre::Vector3 &accum) void Animation::setAccumulation(const Ogre::Vector3 &accum)
{ {
mAccumulate = accum; mAccumulate = accum;
@ -221,10 +349,6 @@ void Animation::setSpeed(float speed)
mAnimSpeedMult = speed / mAnimVelocity; mAnimSpeedMult = speed / mAnimVelocity;
} }
void Animation::setLooping(bool loop)
{
mLooping = loop;
}
void Animation::updatePtr(const MWWorld::Ptr &ptr) void Animation::updatePtr(const MWWorld::Ptr &ptr)
{ {
@ -232,47 +356,36 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr)
} }
void Animation::calcAnimVelocity() float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname)
{ {
const Ogre::NodeAnimationTrack *track = 0; const std::string start = groupname+": start";
const std::string loopstart = groupname+": loop start";
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); const std::string loopstop = groupname+": loop stop";
while(!track && trackiter.hasMoreElements()) const std::string stop = groupname+": stop";
float starttime = std::numeric_limits<float>::max();
float stoptime = 0.0f;
NifOgre::TextKeyMap::const_iterator keyiter(keys.begin());
while(keyiter != keys.end())
{ {
const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); if(keyiter->second == start || keyiter->second == loopstart)
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) starttime = keyiter->first;
track = cur; else if(keyiter->second == loopstop || keyiter->second == stop)
}
if(track && track->getNumKeyFrames() > 1)
{ {
float loopstarttime = 0.0f; stoptime = keyiter->first;
float loopstoptime = mCurrentAnim->getLength();
NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin();
while(keyiter != mCurrentKeys->end())
{
if(keyiter->second == "loop start")
loopstarttime = keyiter->first;
else if(keyiter->second == "loop stop")
{
loopstoptime = keyiter->first;
break; break;
} }
keyiter++; keyiter++;
} }
if(loopstoptime > loopstarttime) if(stoptime > starttime)
{ {
Ogre::TransformKeyFrame startkf(0, loopstarttime); Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum;
Ogre::TransformKeyFrame endkf(0, loopstoptime); Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum;
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf); return startpos.distance(endpos) / (stoptime-starttime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf); }
mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / return 0.0f;
(loopstoptime-loopstarttime);
}
}
} }
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
@ -313,94 +426,91 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og
} }
Ogre::Vector3 Animation::updatePosition() void Animation::updatePosition(Ogre::Vector3 &position)
{ {
Ogre::Vector3 posdiff; Ogre::Vector3 posdiff;
Ogre::TransformKeyFrame kf(0, mCurrentTime); /* Get the non-accumulation root's difference from the last update, and move the position
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); * accordingly.
while(trackiter.hasMoreElements()) */
{ posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate;
const Ogre::NodeAnimationTrack *track = trackiter.getNext(); position += posdiff;
if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName())
{
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
break;
}
}
/* Get the non-accumulation root's difference from the last update. */
posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate;
/* Translate the accumulation root back to compensate for the move. */ /* Translate the accumulation root back to compensate for the move. */
mLastPosition += posdiff; mLastPosition += posdiff;
mAccumRoot->setPosition(-mLastPosition); mAccumRoot->setPosition(-mLastPosition);
return posdiff;
} }
void Animation::reset(const std::string &start, const std::string &stop) bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
{ {
mNextKey = mCurrentKeys->begin(); std::string tag = groupname+": "+start;
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) while(startkey != keys.end() && startkey->second != tag)
mNextKey++; startkey++;
if(mNextKey != mCurrentKeys->end()) if(startkey == keys.end() && start == "loop start")
mCurrentTime = mNextKey->first;
else
{ {
mNextKey = mCurrentKeys->begin(); tag = groupname+": start";
while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start") startkey = keys.begin();
mNextKey++; while(startkey != keys.end() && startkey->second != tag)
if(mNextKey != mCurrentKeys->end()) startkey++;
mCurrentTime = mNextKey->first;
else
{
mNextKey = mCurrentKeys->begin();
mCurrentTime = 0.0f;
} }
if(startkey == keys.end())
return false;
tag = groupname+": "+stop;
NifOgre::TextKeyMap::const_iterator stopkey(startkey);
while(stopkey != keys.end() && stopkey->second != tag)
stopkey++;
if(stopkey == keys.end())
return false;
if(startkey == stopkey)
return false;
mLayer[layeridx].mStartKey = startkey;
mLayer[layeridx].mLoopStartKey = startkey;
mLayer[layeridx].mStopKey = stopkey;
mLayer[layeridx].mNextKey = startkey;
mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first + ((mLayer[layeridx].mStopKey->first-
mLayer[layeridx].mStartKey->first) * startpoint);
tag = groupname+": loop start";
while(mLayer[layeridx].mNextKey->first <= mLayer[layeridx].mTime && mLayer[layeridx].mNextKey != mLayer[layeridx].mStopKey)
{
if(mLayer[layeridx].mNextKey->second == tag)
mLayer[layeridx].mLoopStartKey = mLayer[layeridx].mNextKey;
mLayer[layeridx].mNextKey++;
} }
if(stop.length() > 0) if(layeridx == 0 && nonaccumctrl)
{ mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
NifOgre::TextKeyMap::const_iterator stopKey = mNextKey;
while(stopKey != mCurrentKeys->end() && stopKey->second != stop)
stopKey++;
if(stopKey != mCurrentKeys->end())
mStopTime = stopKey->first;
else
mStopTime = mCurrentAnim->getLength();
}
if(mNonAccumRoot)
{
const Ogre::NodeAnimationTrack *track = 0;
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
while(!track && trackiter.hasMoreElements())
{
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
track = cur;
}
if(track)
{
Ogre::TransformKeyFrame kf(0, mCurrentTime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
mLastPosition = kf.getTranslate() * mAccumulate;
}
}
}
bool Animation::handleEvent(float time, const std::string &evt)
{
if(evt == "start" || evt == "loop start")
{
/* Do nothing */
return true; return true;
} }
bool Animation::doLoop(size_t layeridx)
{
if(mLayer[layeridx].mLoopCount == 0)
return false;
mLayer[layeridx].mLoopCount--;
mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first;
mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey;
mLayer[layeridx].mNextKey++;
mLayer[layeridx].mPlaying = true;
if(layeridx == 0 && mNonAccumCtrl)
mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
return true;
}
bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key)
{
float time = key->first;
const std::string &evt = key->second;
if(evt.compare(0, 7, "sound: ") == 0) if(evt.compare(0, 7, "sound: ") == 0)
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
@ -414,105 +524,214 @@ bool Animation::handleEvent(float time, const std::string &evt)
return true; return true;
} }
if(evt == "loop stop") if(evt.compare(0, mLayer[layeridx].mGroupName.size(), mLayer[layeridx].mGroupName) != 0 ||
evt.compare(mLayer[layeridx].mGroupName.size(), 2, ": ") != 0)
{ {
if(mLooping) // Not ours, skip it
return true;
}
size_t off = mLayer[layeridx].mGroupName.size()+2;
size_t len = evt.size() - off;
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
{ {
reset("loop start", ""); mLayer[layeridx].mLoopStartKey = key;
if(mCurrentTime >= time) return true;
}
if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
{
if(doLoop(layeridx))
{
if(mLayer[layeridx].mTime >= time)
return false; return false;
} }
return true; return true;
} }
if(evt == "stop")
std::cerr<< "Unhandled animation textkey: "<<evt <<std::endl;
return true;
}
bool Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops)
{ {
if(mLooping) // TODO: parameterize this
{ size_t layeridx = 0;
reset("loop start", "");
if(mCurrentTime >= time) if(!mSkelBase)
return false; return false;
return true;
}
// fall-through
}
if(mController)
mController->markerEvent(time, evt);
return true;
}
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
iter->mActiveLayers &= ~(1<<layeridx);
mLayer[layeridx].mGroupName.clear();
mLayer[layeridx].mTextKeys = NULL;
mLayer[layeridx].mControllers = NULL;
mLayer[layeridx].mTime = 0.0f;
mLayer[layeridx].mLoopCount = 0;
mLayer[layeridx].mPlaying = false;
bool movinganim = false;
bool foundanim = false;
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop)
{
try {
bool found = false;
/* Look in reverse; last-inserted source has priority. */ /* Look in reverse; last-inserted source has priority. */
for(std::vector<NifOgre::ObjectList>::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++) for(std::vector<ObjectInfo>::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++)
{ {
if(iter->mSkelBase->hasAnimationState(groupname)) NifOgre::ObjectList &objlist = iter->mObjectList;
if(objlist.mTextKeys.size() == 0)
continue;
const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second;
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
if(layeridx == 0 && mNonAccumRoot)
{ {
mCurrentAnim = iter->mSkelBase->getSkeleton()->getAnimation(groupname); for(size_t i = 0;i < objlist.mControllers.size();i++)
mCurrentKeys = &mTextKeys[groupname]; {
mCurrentControllers = &iter->mControllers; NifOgre::NodeTargetValue<Ogre::Real> *dstval;
mAnimVelocity = 0.0f; dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
if(dstval && dstval->getNode() == mNonAccumRoot)
if(mNonAccumRoot) {
calcAnimVelocity(); nonaccumctrl = dstval;
found = true;
break; break;
} }
} }
if(!found)
throw std::runtime_error("Failed to find animation "+groupname);
reset(start, stop);
setLooping(loop);
mPlaying = true;
}
catch(std::exception &e) {
std::cerr<< e.what() <<std::endl;
}
} }
Ogre::Vector3 Animation::runAnimation(float timepassed) if(!foundanim)
{
if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop, startpoint))
continue;
mLayer[layeridx].mGroupName = groupname;
mLayer[layeridx].mTextKeys = &keys;
mLayer[layeridx].mControllers = &objlist.mControllers;
mLayer[layeridx].mLoopCount = loops;
mLayer[layeridx].mPlaying = true;
if(layeridx == 0)
{
mNonAccumCtrl = nonaccumctrl;
mAnimVelocity = 0.0f;
}
iter->mActiveLayers |= (1<<layeridx);
foundanim = true;
if(mAccumulate == Ogre::Vector3(0.0f))
break;
}
if(!nonaccumctrl)
break;
mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, mAccumulate, groupname);
if(mAnimVelocity > 1.0f)
{
movinganim = (nonaccumctrl==mNonAccumCtrl);
break;
}
}
if(!foundanim)
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
updateActiveControllers();
return movinganim;
}
void Animation::disable(size_t layeridx)
{
if(mLayer[layeridx].mGroupName.empty())
return;
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
iter->mActiveLayers &= ~(1<<layeridx);
mLayer[layeridx].mGroupName.clear();
mLayer[layeridx].mTextKeys = NULL;
mLayer[layeridx].mControllers = NULL;
mLayer[layeridx].mTime = 0.0f;
mLayer[layeridx].mLoopCount = 0;
mLayer[layeridx].mPlaying = false;
updateActiveControllers();
}
bool Animation::getInfo(size_t layeridx, float *complete, std::string *groupname, std::string *start, std::string *stop) const
{
if(mLayer[layeridx].mGroupName.empty())
{
if(complete) *complete = 0.0f;
if(groupname) *groupname = "";
if(start) *start = "";
if(stop) *stop = "";
return false;
}
if(complete) *complete = (mLayer[layeridx].mTime - mLayer[layeridx].mStartKey->first) /
(mLayer[layeridx].mStopKey->first - mLayer[layeridx].mStartKey->first);
if(groupname) *groupname = mLayer[layeridx].mGroupName;
if(start) *start = mLayer[layeridx].mStartKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
if(stop) *stop = mLayer[layeridx].mStopKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
return true;
}
Ogre::Vector3 Animation::runAnimation(float duration)
{ {
Ogre::Vector3 movement(0.0f); Ogre::Vector3 movement(0.0f);
timepassed *= mAnimSpeedMult; duration *= mAnimSpeedMult;
while(mCurrentAnim && mPlaying) for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++)
{ {
float targetTime = mCurrentTime + timepassed; if(mLayer[layeridx].mGroupName.empty())
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime) continue;
float timepassed = duration;
while(mLayer[layeridx].mPlaying)
{ {
mCurrentTime = std::min(mStopTime, targetTime); float targetTime = mLayer[layeridx].mTime + timepassed;
if(mNonAccumRoot) if(mLayer[layeridx].mNextKey->first > targetTime)
movement += updatePosition(); {
mPlaying = (mLooping || mStopTime > mCurrentTime); mLayer[layeridx].mTime = targetTime;
timepassed = targetTime - mCurrentTime; if(layeridx == 0 && mNonAccumCtrl)
updatePosition(movement);
break; break;
} }
float time = mNextKey->first; NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++);
const std::string &evt = mNextKey->second; mLayer[layeridx].mTime = key->first;
mNextKey++; if(layeridx == 0 && mNonAccumCtrl)
updatePosition(movement);
mCurrentTime = time; mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey);
if(mNonAccumRoot) timepassed = targetTime - mLayer[layeridx].mTime;
movement += updatePosition();
mPlaying = (mLooping || mStopTime > mCurrentTime);
timepassed = targetTime - mCurrentTime;
if(!handleEvent(time, evt)) if(!handleTextKey(layeridx, key))
break; break;
} }
for(size_t i = 0;i < mCurrentControllers->size();i++) }
(*mCurrentControllers)[i].update();
if(mObjectList.mSkelBase) for(size_t i = 0;i < mActiveCtrls.size();i++)
mActiveCtrls[i].update();
if(mSkelBase)
{ {
const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
{
Ogre::Entity *ent = iter->mObjectList.mSkelBase;
if(!ent) continue;
Ogre::SkeletonInstance *inst = ent->getSkeleton();
if(baseinst != inst)
updateSkeletonInstance(baseinst, inst);
// HACK: Dirty the animation state set so that Ogre will apply the // HACK: Dirty the animation state set so that Ogre will apply the
// transformations to entities this skeleton instance is shared with. // transformations to entities this skeleton instance is shared with.
mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty(); ent->getAllAnimationStates()->_notifyDirty();
}
} }
return movement; return movement;

View file

@ -8,10 +8,6 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace MWMechanics
{
class CharacterController;
}
namespace MWRender namespace MWRender
{ {
@ -23,48 +19,67 @@ protected:
{ {
private: private:
Animation *mAnimation; Animation *mAnimation;
size_t mIndex;
public: public:
AnimationValue(Animation *anim) : mAnimation(anim) AnimationValue(Animation *anim, size_t layeridx)
: mAnimation(anim), mIndex(layeridx)
{ } { }
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const;
{ virtual void setValue(Ogre::Real value);
return mAnimation->mCurrentTime; };
}
virtual void setValue(Ogre::Real value) struct ObjectInfo {
{ NifOgre::ObjectList mObjectList;
mAnimation->mCurrentTime = value; /* Bit-field specifying which animation layers this object list is
} * explicitly animating on (1 = layer 0, 2 = layer 1, 4 = layer 2.
* etc).
*/
int mActiveLayers;
};
struct AnimLayer {
std::string mGroupName;
std::vector<Ogre::Controller<Ogre::Real> > *mControllers;
const NifOgre::TextKeyMap *mTextKeys;
NifOgre::TextKeyMap::const_iterator mStartKey;
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
NifOgre::TextKeyMap::const_iterator mStopKey;
NifOgre::TextKeyMap::const_iterator mNextKey;
float mTime;
bool mPlaying;
size_t mLoopCount;
AnimLayer();
}; };
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
MWMechanics::CharacterController *mController;
Ogre::SceneNode *mInsert; Ogre::SceneNode *mInsert;
NifOgre::ObjectList mObjectList; Ogre::Entity *mSkelBase;
std::map<std::string,NifOgre::TextKeyMap> mTextKeys; std::vector<ObjectInfo> mObjects;
Ogre::Node *mAccumRoot; Ogre::Node *mAccumRoot;
Ogre::Bone *mNonAccumRoot; Ogre::Bone *mNonAccumRoot;
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
Ogre::Vector3 mAccumulate; Ogre::Vector3 mAccumulate;
Ogre::Vector3 mLastPosition; Ogre::Vector3 mLastPosition;
std::vector<NifOgre::ObjectList> mAnimationSources; std::vector<Ogre::Controller<Ogre::Real> > mActiveCtrls;
std::vector<Ogre::Controller<Ogre::Real> > *mCurrentControllers;
NifOgre::TextKeyMap *mCurrentKeys;
NifOgre::TextKeyMap::const_iterator mNextKey;
Ogre::Animation *mCurrentAnim;
float mCurrentTime;
float mStopTime;
bool mPlaying;
bool mLooping;
float mAnimVelocity; float mAnimVelocity;
float mAnimSpeedMult; float mAnimSpeedMult;
void calcAnimVelocity(); static const size_t sMaxLayers = 1;
AnimLayer mLayer[sMaxLayers];
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > mAnimationValuePtr[sMaxLayers];
static float calcAnimVelocity(const NifOgre::TextKeyMap &keys,
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
const Ogre::Vector3 &accum,
const std::string &groupname);
/* Updates a skeleton instance so that all bones matching the source skeleton (based on /* Updates a skeleton instance so that all bones matching the source skeleton (based on
* bone names) are positioned identically. */ * bone names) are positioned identically. */
@ -72,34 +87,39 @@ protected:
/* Updates the position of the accum root node for the current time, and /* Updates the position of the accum root node for the current time, and
* returns the wanted movement vector from the previous update. */ * returns the wanted movement vector from the previous update. */
Ogre::Vector3 updatePosition(); void updatePosition(Ogre::Vector3 &position);
static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname);
/* Resets the animation to the time of the specified start marker, without /* Resets the animation to the time of the specified start marker, without
* moving anything, and set the end time to the specified stop marker. If * moving anything, and set the end time to the specified stop marker. If
* the marker is not found, it resets to the beginning or end respectively. * the marker is not found, or if the markers are the same, it returns
* false.
*/ */
void reset(const std::string &start, const std::string &stop); bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys,
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
const std::string &groupname, const std::string &start, const std::string &stop,
float startpoint);
bool handleEvent(float time, const std::string &evt); bool doLoop(size_t layeridx);
/* Specifies a list of skeleton names to use as animation sources. */ bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
void setAnimationSources(const std::vector<std::string> &names);
/* Specifies a single skeleton name to use as an animation source. */ void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly);
void setAnimationSource(const std::string &name)
{
std::vector<std::string> names(1, name);
setAnimationSources(names);
}
void createObjectList(Ogre::SceneNode *node, const std::string &model);
static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
void updateActiveControllers();
public: public:
Animation(const MWWorld::Ptr &ptr); Animation(const MWWorld::Ptr &ptr);
virtual ~Animation(); virtual ~Animation();
void setController(MWMechanics::CharacterController *controller); /** Clears all ObjectLists except the first one. As a consequence, any
* playing animations are stopped.
*/
void clearExtraSources();
void updatePtr(const MWWorld::Ptr &ptr); void updatePtr(const MWWorld::Ptr &ptr);
@ -112,10 +132,38 @@ public:
void setSpeed(float speed); void setSpeed(float speed);
void setLooping(bool loop); /** Plays an animation.
* \param groupname Name of the animation group to play.
* \param start Key marker from which to start.
* \param stop Key marker to stop at.
* \param startpoint How far in between the two markers to start. 0 starts
* at the start marker, 1 starts at the stop marker.
* \param loops How many times to loop the animation. This will use the
* "loop start" and "loop stop" markers if they exist,
* otherwise it will use "start" and "stop".
* \return Boolean specifying whether the animation will return movement
* for the character at all.
*/
bool play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops);
void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); /** Stops and removes the animation from the given layer. */
virtual Ogre::Vector3 runAnimation(float timepassed); void disable(size_t layeridx);
/** Gets info about the given animation layer.
* \param layeridx Layer index to get info about.
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
* \param groupname Stores animation group being played.
* \param start Stores the start key
* \param stop Stores the stop key
* \return True if an animation is active on the layer, false otherwise.
*/
bool getInfo(size_t layeridx, float *complete=NULL, std::string *groupname=NULL, std::string *start=NULL, std::string *stop=NULL) const;
virtual Ogre::Vector3 runAnimation(float duration);
/* Returns if there's an animation playing on the given layer. */
bool isPlaying(size_t layeridx) const
{ return mLayer[layeridx].mPlaying; }
Ogre::Node *getNode(const std::string &name); Ogre::Node *getNode(const std::string &name);
}; };

View file

@ -21,13 +21,15 @@ namespace MWRender
CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name,
Ogre::Vector3 position, Ogre::Vector3 lookAt) Ogre::Vector3 position, Ogre::Vector3 lookAt)
: mSizeX(sizeX)
, mSizeY(sizeY) : mSceneMgr (0)
, mName(name)
, mPosition(position) , mPosition(position)
, mLookAt(lookAt) , mLookAt(lookAt)
, mCharacter(character) , mCharacter(character)
, mAnimation(NULL) , mAnimation(NULL)
, mName(name)
, mSizeX(sizeX)
, mSizeY(sizeY)
{ {
} }
@ -87,17 +89,21 @@ namespace MWRender
} }
CharacterPreview::~CharacterPreview () CharacterPreview::~CharacterPreview ()
{
if (mSceneMgr)
{ {
//Ogre::TextureManager::getSingleton().remove(mName); //Ogre::TextureManager::getSingleton().remove(mName);
mSceneMgr->destroyCamera (mName); mSceneMgr->destroyAllCameras();
delete mAnimation; delete mAnimation;
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
} }
}
void CharacterPreview::rebuild() void CharacterPreview::rebuild()
{ {
assert(mAnimation); assert(mAnimation);
delete mAnimation; delete mAnimation;
mAnimation = 0;
mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter),
0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
@ -149,7 +155,7 @@ namespace MWRender
if (!mSelectionBuffer) if (!mSelectionBuffer)
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0);
mAnimation->play("inventoryhandtohand", "start", "stop", false); mAnimation->play("inventoryhandtohand", "start", "stop", 0.0f, 0);
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
@ -183,7 +189,7 @@ namespace MWRender
void RaceSelectionPreview::onSetup () void RaceSelectionPreview::onSetup ()
{ {
mAnimation->play("idle", "start", "stop", false); mAnimation->play("idle", "start", "stop", 0.0f, 0);
updateCamera(); updateCamera();
} }

View file

@ -1,10 +1,5 @@
#include "creatureanimation.hpp" #include "creatureanimation.hpp"
#include <OgreEntity.h>
#include <OgreParticleSystem.h>
#include <OgreSceneManager.h>
#include <OgreSubEntity.h>
#include "renderconst.hpp" #include "renderconst.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -26,31 +21,11 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
{ {
std::string model = "meshes\\"+ref->mBase->mModel; std::string model = "meshes\\"+ref->mBase->mModel;
createObjectList(mPtr.getRefData().getBaseNode(), model);
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
{
Ogre::Entity *ent = mObjectList.mEntities[i];
ent->setVisibilityFlags(RV_Actors);
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
{
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
part->setVisibilityFlags(RV_Actors);
part->setRenderQueueGroup(RQG_Alpha);
}
std::vector<std::string> names;
if((ref->mBase->mFlags&ESM::Creature::Biped)) if((ref->mBase->mFlags&ESM::Creature::Biped))
names.push_back("meshes\\base_anim.nif"); addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true);
names.push_back(model);
setAnimationSources(names); addObjectList(mPtr.getRefData().getBaseNode(), model, false);
setRenderProperties(mObjects.back().mObjectList, RV_Actors, RQG_Main, RQG_Alpha);
} }
} }

View file

@ -11,6 +11,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
@ -96,39 +97,24 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
createObjectList(node, smodel); addObjectList(node, smodel, true);
for(size_t i = 0;i < mObjectList.mEntities.size();i++) if(mBodyPrefix.find("argonian") != std::string::npos)
{ addObjectList(node, "meshes\\argonian_swimkna.nif", true);
Ogre::Entity *base = mObjectList.mEntities[i]; else if(!mNpc->isMale() && !isBeast)
addObjectList(node, "meshes\\base_anim_female.nif", true);
base->getUserObjectBindings().setUserAny(Ogre::Any(-1));
if (mVisibilityFlags != 0)
base->setVisibilityFlags(mVisibilityFlags);
for(unsigned int j=0; j < base->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = base->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
{
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
part->getUserObjectBindings().setUserAny(Ogre::Any(-1));
if(mVisibilityFlags != 0)
part->setVisibilityFlags(mVisibilityFlags);
part->setRenderQueueGroup(RQG_Alpha);
}
std::vector<std::string> skelnames(1, smodel);
if(!mNpc->isMale() && !isBeast)
skelnames.push_back("meshes\\base_anim_female.nif");
else if(mBodyPrefix.find("argonian") != std::string::npos)
skelnames.push_back("meshes\\argonian_swimkna.nif");
if(mNpc->mModel.length() > 0) if(mNpc->mModel.length() > 0)
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); addObjectList(node, "meshes\\"+mNpc->mModel, true);
setAnimationSources(skelnames); if(mViewMode == VM_FirstPerson)
{
/* A bit counter-intuitive, but unlike third-person anims, it seems
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
*/
addObjectList(node, "meshes\\base_anim.1st.nif", true);
if(isBeast)
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
if(!mNpc->isMale() && !isBeast)
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
}
forceUpdate(); forceUpdate();
} }
@ -138,28 +124,31 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
assert(viewMode != VM_HeadOnly); assert(viewMode != VM_HeadOnly);
mViewMode = viewMode; mViewMode = viewMode;
/* FIXME: Enable this once first-person animations work. */ Ogre::SceneNode *node = mInsert->getParentSceneNode();
#if 0
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace); const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
std::vector<std::string> skelnames(1, smodel); clearExtraSources();
if(!mNpc->isMale() && !isBeast) if(mBodyPrefix.find("argonian") != std::string::npos)
skelnames.push_back("meshes\\base_anim_female.nif"); addObjectList(node, "meshes\\argonian_swimkna.nif", true);
else if(mBodyPrefix.find("argonian") != std::string::npos) else if(!mNpc->isMale() && !isBeast)
skelnames.push_back("meshes\\argonian_swimkna.nif"); addObjectList(node, "meshes\\base_anim_female.nif", true);
if(mNpc->mModel.length() > 0) if(mNpc->mModel.length() > 0)
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); addObjectList(node, "meshes\\"+mNpc->mModel, true);
if(mViewMode == VM_FirstPerson) if(mViewMode == VM_FirstPerson)
{ {
smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif"); /* A bit counter-intuitive, but unlike third-person anims, it seems
skelnames.push_back(smodel); * beast races get both base_anim.1st.nif and base_animkna.1st.nif.
*/
addObjectList(node, "meshes\\base_anim.1st.nif", true);
if(isBeast)
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
if(!mNpc->isMale() && !isBeast)
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
} }
setAnimationSources(skelnames); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
#endif
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < sPartListSize;i++)
removeIndividualPart(i); removeIndividualPart(i);
@ -305,81 +294,95 @@ void NpcAnimation::updateParts(bool forceupdate)
if(mViewMode == VM_HeadOnly) if(mViewMode == VM_HeadOnly)
return; return;
static const struct { std::map<int, int> bodypartMap;
ESM::PartReferenceType type; bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck;
const char name[2][12]; bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest;
} PartTypeList[] = { bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin;
{ ESM::PRT_Neck, { "neck", "" } }, bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand;
{ ESM::PRT_Cuirass, { "chest", "" } }, bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand;
{ ESM::PRT_Groin, { "groin", "" } }, bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist;
{ ESM::PRT_RHand, { "hand", "hands" } }, bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist;
{ ESM::PRT_LHand, { "hand", "hands" } }, bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm;
{ ESM::PRT_RWrist, { "wrist", "" } }, bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm;
{ ESM::PRT_LWrist, { "wrist", "" } }, bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm;
{ ESM::PRT_RForearm, { "forearm", "" } }, bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm;
{ ESM::PRT_LForearm, { "forearm", "" } }, bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot;
{ ESM::PRT_RUpperarm, { "upper arm", "" } }, bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot;
{ ESM::PRT_LUpperarm, { "upper arm", "" } }, bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle;
{ ESM::PRT_RFoot, { "foot", "feet" } }, bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle;
{ ESM::PRT_LFoot, { "foot", "feet" } }, bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee;
{ ESM::PRT_RAnkle, { "ankle", "" } }, bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee;
{ ESM::PRT_LAnkle, { "ankle", "" } }, bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg;
{ ESM::PRT_RKnee, { "knee", "" } }, bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg;
{ ESM::PRT_LKnee, { "knee", "" } }, bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail;
{ ESM::PRT_RLeg, { "upper leg", "" } },
{ ESM::PRT_LLeg, { "upper leg", "" } },
{ ESM::PRT_Tail, { "tail", "" } }
};
const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++)
const int Flag_Female = 0x01;
const int Flag_FirstPerson = 0x02;
int flags = 0;
if (!mNpc->isMale())
flags |= Flag_Female;
if (mViewMode == VM_FirstPerson)
flags |= Flag_FirstPerson;
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
static std::map< std::pair<std::string, int> , std::vector<const ESM::BodyPart*> > sRaceMapping;
std::string race = Misc::StringUtils::lowerCase(mNpc->mRace);
std::pair<std::string, int> thisCombination = std::make_pair(race, flags);
if (sRaceMapping.find(thisCombination) == sRaceMapping.end())
{ {
if(mPartPriorities[PartTypeList[i].type] < 1) sRaceMapping[thisCombination].resize(ESM::PRT_Count);
{ for (int i=0; i<ESM::PRT_Count; ++i)
const ESM::BodyPart *part = NULL; sRaceMapping[thisCombination][i] = NULL;
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>(); const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
if(!mNpc->isMale()) for (MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
{ {
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext); const ESM::BodyPart& bodypart = *it;
if(part == 0) if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext); continue;
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
{
continue;
} }
if(part == 0) if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext); continue;
if(part == 0) if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace))
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext); continue;
if(part) bool firstPerson = (bodypart.mId.size() >= 3)
addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); && bodypart.mId[bodypart.mId.size()-3] == '1'
&& bodypart.mId[bodypart.mId.size()-2] == 's'
&& bodypart.mId[bodypart.mId.size()-1] == 't';
if (firstPerson != (mViewMode == VM_FirstPerson))
continue;
for (std::map<int, int>::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt )
if (bIt->second == bodypart.mData.mPart)
sRaceMapping[thisCombination][bIt->first] = &*it;
} }
} }
for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part)
{
const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part];
if (mPartPriorities[part] < 1 && bodypart)
addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel);
}
} }
NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename)
{ {
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename, NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
mInsert, model); setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha);
for(size_t i = 0;i < objects.mEntities.size();i++)
{
objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
if(mVisibilityFlags != 0)
objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags);
for(unsigned int j=0; j < objects.mEntities[i]->getNumSubEntities(); ++j) for(size_t i = 0;i < objects.mEntities.size();i++)
{ objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
Ogre::SubEntity* subEnt = objects.mEntities[i]->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < objects.mParticles.size();i++) for(size_t i = 0;i < objects.mParticles.size();i++)
{
objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
if(mVisibilityFlags != 0)
objects.mParticles[i]->setVisibilityFlags(mVisibilityFlags);
objects.mParticles[i]->setRenderQueueGroup(RQG_Alpha);
}
if(objects.mSkelBase) if(objects.mSkelBase)
{ {
Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates();
@ -395,6 +398,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in
while(boneiter.hasMoreElements()) while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true); boneiter.getNext()->setManuallyControlled(true);
} }
return objects; return objects;
} }
@ -408,14 +412,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
mTimeToChange -= timepassed; mTimeToChange -= timepassed;
Ogre::Vector3 ret = Animation::runAnimation(timepassed); Ogre::Vector3 ret = Animation::runAnimation(timepassed);
const Ogre::SkeletonInstance *skelsrc = mObjectList.mSkelBase->getSkeleton();
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < sPartListSize;i++)
{ {
Ogre::Entity *ent = mObjectParts[i].mSkelBase; Ogre::Entity *ent = mObjectParts[i].mSkelBase;
if(!ent) continue; if(!ent) continue;
updateSkeletonInstance(skelsrc, ent->getSkeleton()); updateSkeletonInstance(baseinst, ent->getSkeleton());
ent->getAllAnimationStates()->_notifyDirty(); ent->getAllAnimationStates()->_notifyDirty();
} }
return ret; return ret;
} }

View file

@ -30,6 +30,8 @@
#include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/inputmanager.hpp" // FIXME
#include "../mwbase/windowmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
@ -315,13 +317,15 @@ void RenderingManager::update (float duration, bool paused)
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
mRendering.getFader()->setFactor(1.f-(blind / 100.f));
setAmbientMode();
// player position // player position
MWWorld::RefData &data = MWWorld::RefData &data = player.getRefData();
MWBase::Environment::get()
.getWorld()
->getPlayer()
.getPlayer()
.getRefData();
float *_playerPos = data.getPosition().pos; float *_playerPos = data.getPosition().pos;
Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]);
@ -597,8 +601,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
{ {
mRendering.getScene()->setAmbientLight(colour); mAmbientColor = colour;
mTerrainManager->setAmbient(colour);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude;
Ogre::ColourValue final = colour;
final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f));
mRendering.getScene()->setAmbientLight(final);
mTerrainManager->setAmbient(final);
} }
void RenderingManager::sunEnable(bool real) void RenderingManager::sunEnable(bool real)

View file

@ -788,8 +788,8 @@ void VideoState::decode_thread_loop(VideoState *self)
// main decode loop // main decode loop
while(!self->quit) while(!self->quit)
{ {
if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) ||
(self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE))
{ {
boost::this_thread::sleep(boost::posix_time::milliseconds(10)); boost::this_thread::sleep(boost::posix_time::milliseconds(10));
continue; continue;

View file

@ -13,8 +13,8 @@ namespace ESM
namespace MWWorld namespace MWWorld
{ {
/// List all (Ogre-)handles. /// List all (Ogre-)handles, then reset RefData::mBaseNode to 0.
struct ListHandles struct ListAndResetHandles
{ {
std::vector<Ogre::SceneNode*> mHandles; std::vector<Ogre::SceneNode*> mHandles;
@ -23,6 +23,8 @@ namespace MWWorld
Ogre::SceneNode* handle = data.getBaseNode(); Ogre::SceneNode* handle = data.getBaseNode();
if (handle) if (handle)
mHandles.push_back (handle); mHandles.push_back (handle);
data.setBaseNode(0);
return true; return true;
} }
}; };

View file

@ -193,6 +193,8 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
{ {
count = std::abs(count); /// \todo implement item restocking (indicated by negative count) count = std::abs(count); /// \todo implement item restocking (indicated by negative count)
try
{
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
@ -248,6 +250,13 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
addImp (ref.getPtr()); addImp (ref.getPtr());
} }
} }
catch (std::logic_error& e)
{
// Vanilla doesn't fail on nonexistent items in levelled lists
std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl;
return;
}
}
void MWWorld::ContainerStore::clear() void MWWorld::ContainerStore::clear()
{ {

View file

@ -76,16 +76,18 @@ namespace MWWorld
void Scene::unloadCell (CellStoreCollection::iterator iter) void Scene::unloadCell (CellStoreCollection::iterator iter)
{ {
std::cout << "Unloading cell\n"; std::cout << "Unloading cell\n";
ListHandles functor; ListAndResetHandles functor;
(*iter)->forEach<ListHandles>(functor); (*iter)->forEach<ListAndResetHandles>(functor);
{ {
// silence annoying g++ warning // silence annoying g++ warning
for (std::vector<Ogre::SceneNode*>::const_iterator iter2 (functor.mHandles.begin()); for (std::vector<Ogre::SceneNode*>::const_iterator iter2 (functor.mHandles.begin());
iter2!=functor.mHandles.end(); ++iter2){ iter2!=functor.mHandles.end(); ++iter2)
{
Ogre::SceneNode* node = *iter2; Ogre::SceneNode* node = *iter2;
mPhysics->removeObject (node->getName()); mPhysics->removeObject (node->getName());
} }
}
if ((*iter)->mCell->isExterior()) if ((*iter)->mCell->isExterior())
{ {
@ -97,7 +99,6 @@ namespace MWWorld
if (land) if (land)
mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() );
} }
}
mRendering.removeCell(*iter); mRendering.removeCell(*iter);

View file

@ -415,8 +415,18 @@ namespace MWWorld
} }
}; };
struct DynamicExtCmp
{
bool operator()(const std::pair<int, int> &left, const std::pair<int, int> &right) const {
if (left.first == right.first) {
return left.second < right.second;
}
return left.first < right.first;
}
};
typedef std::map<std::string, ESM::Cell> DynamicInt; typedef std::map<std::string, ESM::Cell> DynamicInt;
typedef std::map<std::pair<int, int>, ESM::Cell> DynamicExt; typedef std::map<std::pair<int, int>, ESM::Cell, DynamicExtCmp> DynamicExt;
DynamicInt mInt; DynamicInt mInt;
DynamicExt mExt; DynamicExt mExt;
@ -465,7 +475,7 @@ namespace MWWorld
cell.mData.mX = x, cell.mData.mY = y; cell.mData.mX = x, cell.mData.mY = y;
std::pair<int, int> key(x, y); std::pair<int, int> key(x, y);
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key); DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) { if (it != mExt.end()) {
return &(it->second); return &(it->second);
} }
@ -483,7 +493,7 @@ namespace MWWorld
cell.mData.mX = x, cell.mData.mY = y; cell.mData.mX = x, cell.mData.mY = y;
std::pair<int, int> key(x, y); std::pair<int, int> key(x, y);
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key); DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) { if (it != mExt.end()) {
return &(it->second); return &(it->second);
} }
@ -524,7 +534,7 @@ namespace MWWorld
void setUp() { void setUp() {
//typedef std::vector<ESM::Cell>::iterator Iterator; //typedef std::vector<ESM::Cell>::iterator Iterator;
typedef std::map<std::pair<int, int>, ESM::Cell>::iterator ExtIterator; typedef DynamicExt::iterator ExtIterator;
typedef std::map<std::string, ESM::Cell>::iterator IntIterator; typedef std::map<std::string, ESM::Cell>::iterator IntIterator;
//std::sort(mInt.begin(), mInt.end(), RecordCmp()); //std::sort(mInt.begin(), mInt.end(), RecordCmp());
@ -862,7 +872,28 @@ namespace MWWorld
} }
void setUp() { void setUp() {
std::sort(mStatic.begin(), mStatic.end(), Compare()); /// \note This method sorts indexed values for further
/// searches. Every loaded item is present in storage, but
/// latest loaded shadows any previous while searching.
/// If memory cost will be too high, it is possible to remove
/// unused values.
Compare cmp;
std::stable_sort(mStatic.begin(), mStatic.end(), cmp);
typename std::vector<T>::iterator first, next;
next = first = mStatic.begin();
while (first != mStatic.end() && ++next != mStatic.end()) {
while (next != mStatic.end() && !cmp(*first, *next)) {
++next;
}
if (first != --next) {
std::swap(*first, *next);
}
first = ++next;
}
} }
const T *search(int index) const { const T *search(int index) const {

View file

@ -38,7 +38,9 @@ enum PartReferenceType
PRT_RPauldron = 23, PRT_RPauldron = 23,
PRT_LPauldron = 24, PRT_LPauldron = 24,
PRT_Weapon = 25, PRT_Weapon = 25,
PRT_Tail = 26 PRT_Tail = 26,
PRT_Count = 27
}; };
// Reference to body parts // Reference to body parts

View file

@ -9,13 +9,13 @@ namespace ESM
void BodyPart::load(ESMReader &esm) void BodyPart::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mModel = esm.getHNString("MODL");
mName = esm.getHNString("FNAM"); mRace = esm.getHNString("FNAM");
esm.getHNT(mData, "BYDT", 4); esm.getHNT(mData, "BYDT", 4);
} }
void BodyPart::save(ESMWriter &esm) void BodyPart::save(ESMWriter &esm)
{ {
esm.writeHNCString("MODL", mModel); esm.writeHNCString("MODL", mModel);
esm.writeHNCString("FNAM", mName); esm.writeHNCString("FNAM", mRace);
esm.writeHNT("BYDT", mData, 4); esm.writeHNT("BYDT", mData, 4);
} }

View file

@ -27,7 +27,9 @@ struct BodyPart
MP_Knee = 11, MP_Knee = 11,
MP_Upperleg = 12, MP_Upperleg = 12,
MP_Clavicle = 13, MP_Clavicle = 13,
MP_Tail = 14 MP_Tail = 14,
MP_Count = 15
}; };
enum Flags enum Flags
@ -52,7 +54,7 @@ struct BodyPart
}; };
BYDTstruct mData; BYDTstruct mData;
std::string mId, mModel, mName; std::string mId, mModel, mRace;
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm); void save(ESMWriter &esm);

View file

@ -303,5 +303,28 @@ public:
} }
}; };
class NiFlipController : public Controller
{
public:
int mTexSlot;
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
NiSourceTextureList mSources;
void read(NIFStream *nif)
{
Controller::read(nif);
mTexSlot = nif->getUInt();
/*unknown=*/nif->getUInt();/*0?*/
mDelta = nif->getFloat();
mSources.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
mSources.post(nif);
}
};
} // Namespace } // Namespace
#endif #endif

View file

@ -208,7 +208,7 @@ static const RecordFactoryEntry recordFactories [] = {
{ "NiNode", &construct <NiNode >, RC_NiNode }, { "NiNode", &construct <NiNode >, RC_NiNode },
{ "AvoidNode", &construct <NiNode >, RC_NiNode }, { "AvoidNode", &construct <NiNode >, RC_NiNode },
{ "NiBSParticleNode", &construct <NiNode >, RC_NiNode }, { "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode }, { "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode }, { "NiBillboardNode", &construct <NiNode >, RC_NiNode },
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape }, { "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = {
{ "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController }, { "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController },
{ "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController }, { "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController },
{ "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController }, { "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController },
{ "NiFlipController", &construct <NiFlipController >, RC_NiFlipController },
{ "NiAmbientLight", &construct <NiLight >, RC_NiLight }, { "NiAmbientLight", &construct <NiLight >, RC_NiLight },
{ "NiDirectionalLight", &construct <NiLight >, RC_NiLight }, { "NiDirectionalLight", &construct <NiLight >, RC_NiLight },
{ "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect }, { "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect },

View file

@ -128,13 +128,17 @@ struct NiNode : Node
NodeList children; NodeList children;
NodeList effects; NodeList effects;
/* Known NiNode flags: enum Flags {
0x01 hidden Flag_Hidden = 0x0001,
0x02 use mesh for collision Flag_MeshCollision = 0x0002,
0x04 use bounding box for collision (?) Flag_BBoxCollision = 0x0004
0x08 unknown, but common };
0x20, 0x40, 0x80 unknown enum BSAnimFlags {
*/ AnimFlag_AutoPlay = 0x0020
};
enum BSParticleFlags {
ParticleFlag_AutoPlay = 0x0020
};
void read(NIFStream *nif) void read(NIFStream *nif)
{ {

View file

@ -39,6 +39,7 @@ enum RecordType
RC_NiTriShape, RC_NiTriShape,
RC_NiRotatingParticles, RC_NiRotatingParticles,
RC_NiAutoNormalParticles, RC_NiAutoNormalParticles,
RC_NiBSParticleNode,
RC_NiCamera, RC_NiCamera,
RC_NiTexturingProperty, RC_NiTexturingProperty,
RC_NiMaterialProperty, RC_NiMaterialProperty,
@ -59,6 +60,7 @@ enum RecordType
RC_NiMaterialColorController, RC_NiMaterialColorController,
RC_NiBSPArrayController, RC_NiBSPArrayController,
RC_NiParticleSystemController, RC_NiParticleSystemController,
RC_NiFlipController,
RC_NiBSAnimationNode, RC_NiBSAnimationNode,
RC_NiLight, RC_NiLight,
RC_NiTextureEffect, RC_NiTextureEffect,

View file

@ -163,6 +163,7 @@ typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordListT<Node> NodeList; typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList; typedef RecordListT<Property> PropertyList;
typedef RecordListT<NiSourceTexture> NiSourceTextureList;
} // Namespace } // Namespace
#endif #endif

View file

@ -255,9 +255,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::
assert(shape != NULL); assert(shape != NULL);
// Interpret flags // Interpret flags
bool hidden = (flags & 0x01) != 0; // Not displayed bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0;
bool collide = (flags & 0x02) != 0; // Use mesh for collision bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0;
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0;
// If the object was marked "NCO" earlier, it shouldn't collide with // If the object was marked "NCO" earlier, it shouldn't collide with
// anything. So don't do anything. // anything. So don't do anything.

View file

@ -109,13 +109,26 @@ NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape) void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape)
{ {
Ogre::SkeletonPtr skel;
const Nif::NiTriShapeData *data = shape->data.getPtr(); const Nif::NiTriShapeData *data = shape->data.getPtr();
const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr());
std::vector<Ogre::Vector3> srcVerts = data->vertices; std::vector<Ogre::Vector3> srcVerts = data->vertices;
std::vector<Ogre::Vector3> srcNorms = data->normals; std::vector<Ogre::Vector3> srcNorms = data->normals;
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
bool vertShadowBuffer = false; bool vertShadowBuffer = false;
if(!shape->controller.empty())
{
Nif::ControllerPtr ctrl = shape->controller;
do {
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
vertShadowBuffer = true;
break;
}
} while(!(ctrl=ctrl->next).empty());
}
if(skin != NULL) if(skin != NULL)
{ {
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
@ -125,10 +138,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
// explicitly attached later. // explicitly attached later.
mesh->setSkeletonName(mName); mesh->setSkeletonName(mName);
// Get the skeleton resource, so vertices can be transformed into the bones' initial state.
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
skel = skelMgr->getByName(mName);
// Convert vertices and normals to bone space from bind position. It would be // Convert vertices and normals to bone space from bind position. It would be
// better to transform the bones into bind position, but there doesn't seem to // better to transform the bones into bind position, but there doesn't seem to
// be a reliable way to do that. // be a reliable way to do that.
@ -139,11 +148,10 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
const Nif::NodeList &bones = skin->bones; const Nif::NodeList &bones = skin->bones;
for(size_t b = 0;b < bones.length();b++) for(size_t b = 0;b < bones.length();b++)
{ {
Ogre::Bone *bone = skel->getBone(bones[b]->name);
Ogre::Matrix4 mat; Ogre::Matrix4 mat;
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
Ogre::Quaternion(data->bones[b].trafo.rotation)); Ogre::Quaternion(data->bones[b].trafo.rotation));
mat = bone->_getFullTransform() * mat; mat = bones[b]->getWorldTransform() * mat;
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights; const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
for(size_t i = 0;i < weights.size();i++) for(size_t i = 0;i < weights.size();i++)
@ -260,13 +268,24 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
// Texture UV coordinates // Texture UV coordinates
size_t numUVs = data->uvlist.size(); size_t numUVs = data->uvlist.size();
for(size_t i = 0;i < numUVs;i++) if (numUVs)
{ {
vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC);
vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[i][0], true); for(size_t i = 0; i < numUVs; i++)
decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i);
vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(),
Ogre::HardwareBuffer::HBU_STATIC);
std::vector<Ogre::Vector2> allUVs;
allUVs.reserve(srcVerts.size()*numUVs);
for (size_t vert = 0; vert<srcVerts.size(); ++vert)
for(size_t i = 0; i < numUVs; i++)
allUVs.push_back(data->uvlist[i][vert]);
vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true);
decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i);
bind->setBinding(nextBuf++, vbuf); bind->setBinding(nextBuf++, vbuf);
} }
@ -285,6 +304,8 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
// Assign bone weights for this TriShape // Assign bone weights for this TriShape
if(skin != NULL) if(skin != NULL)
{ {
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName);
const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NiSkinData *data = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones; const Nif::NodeList &bones = skin->bones;
for(size_t i = 0;i < bones.length();i++) for(size_t i = 0;i < bones.length();i++)

View file

@ -44,15 +44,6 @@
#include "material.hpp" #include "material.hpp"
#include "mesh.hpp" #include "mesh.hpp"
namespace std
{
// TODO: Do something useful
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
{ return o; }
}
namespace NifOgre namespace NifOgre
{ {
@ -74,11 +65,7 @@ public:
, mStopTime(ctrl->timeStop) , mStopTime(ctrl->timeStop)
{ {
if(mDeltaInput) if(mDeltaInput)
{
mDeltaCount = mPhase; mDeltaCount = mPhase;
while(mDeltaCount < mStartTime)
mDeltaCount += (mStopTime-mStartTime);
}
} }
virtual Ogre::Real calculate(Ogre::Real value) virtual Ogre::Real calculate(Ogre::Real value)
@ -86,6 +73,9 @@ public:
if(mDeltaInput) if(mDeltaInput)
{ {
mDeltaCount += value*mFrequency; mDeltaCount += value*mFrequency;
if(mDeltaCount < mStartTime)
mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount,
mStopTime - mStartTime);
mDeltaCount = std::fmod(mDeltaCount - mStartTime, mDeltaCount = std::fmod(mDeltaCount - mStartTime,
mStopTime - mStartTime) + mStartTime; mStopTime - mStartTime) + mStartTime;
return mDeltaCount; return mDeltaCount;
@ -104,7 +94,7 @@ public:
private: private:
std::vector<Nif::NiVisData::VisData> mData; std::vector<Nif::NiVisData::VisData> mData;
virtual bool calculate(Ogre::Real time) bool calculate(Ogre::Real time) const
{ {
if(mData.size() == 0) if(mData.size() == 0)
return true; return true;
@ -144,10 +134,19 @@ public:
, mData(data->mVis) , mData(data->mVis)
{ } { }
virtual Ogre::Quaternion getRotation(float time) const
{ return Ogre::Quaternion(); }
virtual Ogre::Vector3 getTranslation(float time) const
{ return Ogre::Vector3(0.0f); }
virtual Ogre::Vector3 getScale(float time) const
{ return Ogre::Vector3(1.0f); }
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const
{ {
// Should not be called // Should not be called
return 1.0f; return 0.0f;
} }
virtual void setValue(Ogre::Real time) virtual void setValue(Ogre::Real time)
@ -170,6 +169,60 @@ public:
Nif::Vector3KeyList mTranslations; Nif::Vector3KeyList mTranslations;
Nif::FloatKeyList mScales; Nif::FloatKeyList mScales;
static float interpKey(const Nif::FloatKeyList::VecType &keys, float time)
{
if(time <= keys.front().mTime)
return keys.front().mValue;
Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1);
for(;iter != keys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
return last->mValue + ((iter->mValue - last->mValue)*a);
}
return keys.back().mValue;
}
static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time)
{
if(time <= keys.front().mTime)
return keys.front().mValue;
Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1);
for(;iter != keys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
return last->mValue + ((iter->mValue - last->mValue)*a);
}
return keys.back().mValue;
}
static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time)
{
if(time <= keys.front().mTime)
return keys.front().mValue;
Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1);
for(;iter != keys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue);
}
return keys.back().mValue;
}
public: public:
Value(Ogre::Node *target, const Nif::NiKeyframeData *data) Value(Ogre::Node *target, const Nif::NiKeyframeData *data)
: NodeTargetValue<Ogre::Real>(target) : NodeTargetValue<Ogre::Real>(target)
@ -178,6 +231,27 @@ public:
, mScales(data->mScales) , mScales(data->mScales)
{ } { }
virtual Ogre::Quaternion getRotation(float time) const
{
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);
return mNode->getPosition();
}
virtual Ogre::Vector3 getScale(float time) const
{
if(mScales.mKeys.size() > 0)
return Ogre::Vector3(interpKey(mScales.mKeys, time));
return mNode->getScale();
}
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const
{ {
// Should not be called // Should not be called
@ -187,68 +261,11 @@ public:
virtual void setValue(Ogre::Real time) virtual void setValue(Ogre::Real time)
{ {
if(mRotations.mKeys.size() > 0) if(mRotations.mKeys.size() > 0)
{ mNode->setOrientation(interpKey(mRotations.mKeys, time));
if(time <= mRotations.mKeys.front().mTime)
mNode->setOrientation(mRotations.mKeys.front().mValue);
else if(time >= mRotations.mKeys.back().mTime)
mNode->setOrientation(mRotations.mKeys.back().mValue);
else
{
Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1);
for(;iter != mRotations.mKeys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue));
break;
}
}
}
if(mTranslations.mKeys.size() > 0) if(mTranslations.mKeys.size() > 0)
{ mNode->setPosition(interpKey(mTranslations.mKeys, time));
if(time <= mTranslations.mKeys.front().mTime)
mNode->setPosition(mTranslations.mKeys.front().mValue);
else if(time >= mTranslations.mKeys.back().mTime)
mNode->setPosition(mTranslations.mKeys.back().mValue);
else
{
Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1);
for(;iter != mTranslations.mKeys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a));
break;
}
}
}
if(mScales.mKeys.size() > 0) if(mScales.mKeys.size() > 0)
{ mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time)));
if(time <= mScales.mKeys.front().mTime)
mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue));
else if(time >= mScales.mKeys.back().mTime)
mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue));
else
{
Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1);
for(;iter != mScales.mKeys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a)));
break;
}
}
}
} }
}; };
@ -289,7 +306,7 @@ public:
} }
public: public:
Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data) Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data)
: mMaterial(material) : mMaterial(material)
, mUTrans(data->mKeyList[0]) , mUTrans(data->mKeyList[0])
, mVTrans(data->mKeyList[1]) , mVTrans(data->mKeyList[1])
@ -329,11 +346,70 @@ public:
typedef DefaultFunction Function; typedef DefaultFunction Function;
}; };
class ParticleSystemController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>
{
private:
Ogre::ParticleSystem *mParticleSys;
float mEmitStart;
float mEmitStop;
public:
Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl)
: mParticleSys(psys)
, mEmitStart(pctrl->startTime)
, mEmitStop(pctrl->stopTime)
{
}
Ogre::Real getValue() const
{ return 0.0f; }
void setValue(Ogre::Real value)
{
mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop);
}
};
typedef DefaultFunction Function;
};
class GeomMorpherController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>
{
private:
Ogre::SubEntity *mSubEntity;
std::vector<Nif::NiMorphData::MorphData> mMorphs;
public:
Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data)
: mSubEntity(subent)
, mMorphs(data->mMorphs)
{ }
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real value)
{
// TODO: Implement
}
};
typedef DefaultFunction Function;
};
/** Manual resource loader for NIF objects (meshes, particle systems, etc). /** Object creator for NIFs. This is the main class responsible for creating
* This is the main class responsible for translating the internal NIF * "live" Ogre objects (entities, particle systems, controllers, etc) from
* structures into something Ogre can use. * their NIF equivalents.
*/ */
class NIFObjectLoader class NIFObjectLoader
{ {
@ -349,14 +425,79 @@ class NIFObjectLoader
} }
static void createEntity(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
const Nif::Node *node, int flags, int animflags)
{
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(shape->name.length() > 0)
fullname += "@shape="+shape->name;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
if(meshMgr.getByName(fullname).isNull())
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
objectlist.mEntities.push_back(entity);
if(objectlist.mSkelBase)
{
if(entity->hasSkeleton())
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
else
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
}
}
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiUVController)
{
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
const Nif::NiGeomMorpherController *geom = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
Ogre::SubEntity *subent = entity->getSubEntity(0);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
ctrl = ctrl->next;
}
}
static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl)
{ {
Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif");
emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom, emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f,
partctrl->velocity+partctrl->velocityRandom); partctrl->velocity + partctrl->velocityRandom*0.5f);
emitter->setEmissionRate(partctrl->emitRate); emitter->setEmissionRate(partctrl->emitRate);
emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom, emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f,
partctrl->lifetime+partctrl->lifetimeRandom); partctrl->lifetime + partctrl->lifetimeRandom*0.5f);
emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x));
emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y));
emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z));
@ -416,9 +557,9 @@ class NIFObjectLoader
} }
} }
static Ogre::ParticleSystem *createParticleSystem(const std::string &name, const std::string &group, static void createParticleSystem(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase, Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
const Nif::Node *partnode) const Nif::Node *partnode, int flags, int partflags)
{ {
const Nif::NiAutoNormalParticlesData *particledata = NULL; const Nif::NiAutoNormalParticlesData *particledata = NULL;
if(partnode->recType == Nif::RC_NiAutoNormalParticles) if(partnode->recType == Nif::RC_NiAutoNormalParticles)
@ -426,13 +567,13 @@ class NIFObjectLoader
else if(partnode->recType == Nif::RC_NiRotatingParticles) else if(partnode->recType == Nif::RC_NiRotatingParticles)
particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr(); particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr();
Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem();
try {
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex);
if(partnode->name.length() > 0) if(partnode->name.length() > 0)
fullname += "@type="+partnode->name; fullname += "@type="+partnode->name;
Misc::StringUtils::toLower(fullname); Misc::StringUtils::toLower(fullname);
Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem();
const Nif::NiTexturingProperty *texprop = NULL; const Nif::NiTexturingProperty *texprop = NULL;
const Nif::NiMaterialProperty *matprop = NULL; const Nif::NiMaterialProperty *matprop = NULL;
const Nif::NiAlphaProperty *alphaprop = NULL; const Nif::NiAlphaProperty *alphaprop = NULL;
@ -467,9 +608,17 @@ class NIFObjectLoader
if(!partctrl->emitter.empty() && !partsys->isAttached()) if(!partctrl->emitter.empty() && !partsys->isAttached())
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
entitybase->attachObjectToBone(trgtbone->getName(), partsys); objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
} }
Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
} }
ctrl = ctrl->next; ctrl = ctrl->next;
} }
@ -477,22 +626,80 @@ class NIFObjectLoader
if(!partsys->isAttached()) if(!partsys->isAttached())
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
entitybase->attachObjectToBone(trgtbone->getName(), partsys); objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
}
partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
objectlist.mParticles.push_back(partsys);
}
static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags)
{
do {
if(ctrl->recType == Nif::RC_NiVisController)
{
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiKeyframeController)
{
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(!key->data.empty())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
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::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
ctrl = ctrl->next;
} while(!ctrl.empty());
}
static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys)
{
for(size_t i = 0;i < tk->list.size();i++)
{
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
if(::isspace(str[pos]))
{
pos++;
continue;
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
} }
} }
catch(std::exception &e) {
std::cerr<< "Particles exception: "<<e.what() <<std::endl;
sceneMgr->destroyParticleSystem(partsys);
partsys = NULL;
};
return partsys;
} }
static void createObjects(const std::string &name, const std::string &group, static void createObjects(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, const Nif::Node *node, Ogre::SceneManager *sceneMgr, const Nif::Node *node,
ObjectList &objectlist, int flags) ObjectList &objectlist, int flags, int animflags, int partflags)
{ {
// Do not create objects for the collision shape (includes all children) // Do not create objects for the collision shape (includes all children)
if(node->recType == Nif::RC_RootCollisionNode) if(node->recType == Nif::RC_RootCollisionNode)
@ -503,12 +710,24 @@ class NIFObjectLoader
if (node->name.find("marker") != std::string::npos) if (node->name.find("marker") != std::string::npos)
return; return;
if(node->recType == Nif::RC_NiBSAnimationNode)
animflags |= node->flags;
else if(node->recType == Nif::RC_NiBSParticleNode)
partflags |= node->flags;
else
flags |= node->flags; flags |= node->flags;
Nif::ExtraPtr e = node->extra; Nif::ExtraPtr e = node->extra;
while(!e.empty()) while(!e.empty())
{ {
if(e->recType == Nif::RC_NiStringExtraData) if(e->recType == Nif::RC_NiTextKeyExtraData)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
extractTextKeys(tk, objectlist.mTextKeys[trgtid]);
}
else if(e->recType == Nif::RC_NiStringExtraData)
{ {
const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr()); const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
// String markers may contain important information // String markers may contain important information
@ -520,9 +739,13 @@ class NIFObjectLoader
flags |= 0x80000000; flags |= 0x80000000;
} }
} }
e = e->extra; e = e->extra;
} }
if(!node->controller.empty())
createNodeControllers(name, node->controller, objectlist, animflags);
if(node->recType == Nif::RC_NiCamera) if(node->recType == Nif::RC_NiCamera)
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
@ -530,94 +753,15 @@ class NIFObjectLoader
objectlist.mCameras.push_back(trgtbone); objectlist.mCameras.push_back(trgtbone);
} }
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiVisController)
{
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, false));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiKeyframeController)
{
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(!key->data.empty())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
ctrl = ctrl->next;
}
if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000))
{ {
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node); createEntity(name, group, sceneMgr, objectlist, node, flags, animflags);
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(shape->name.length() > 0)
fullname += "@shape="+shape->name;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
if(meshMgr.getByName(fullname).isNull())
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
entity->setVisible(!(flags&0x01));
objectlist.mEntities.push_back(entity);
if(objectlist.mSkelBase)
{
if(entity->hasSkeleton())
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
else
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
}
}
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiUVController)
{
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
Ogre::ControllerValueRealPtr srcval(Ogre::ControllerManager::getSingleton().getFrameTimeSource());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, true));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
ctrl = ctrl->next;
}
} }
if((node->recType == Nif::RC_NiAutoNormalParticles || if((node->recType == Nif::RC_NiAutoNormalParticles ||
node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000))
{ {
Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node); createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags);
if(partsys != NULL)
{
partsys->setVisible(!(flags&0x01));
objectlist.mParticles.push_back(partsys);
}
} }
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
@ -627,7 +771,7 @@ class NIFObjectLoader
for(size_t i = 0;i < children.length();i++) for(size_t i = 0;i < children.length();i++)
{ {
if(!children[i].empty()) if(!children[i].empty())
createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags); createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags);
} }
} }
} }
@ -674,7 +818,7 @@ public:
// Create a base skeleton entity if this NIF needs one // Create a base skeleton entity if this NIF needs one
createSkelBase(name, group, sceneMgr, node, objectlist); createSkelBase(name, group, sceneMgr, node, objectlist);
} }
createObjects(name, group, sceneMgr, node, objectlist, flags); createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
} }
}; };
@ -757,12 +901,15 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena
} }
ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string name, const std::string &group) ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
{ {
ObjectList objectlist; ObjectList objectlist;
Misc::StringUtils::toLower(name); Misc::StringUtils::toLower(name);
NIFObjectLoader::load(sceneMgr, objectlist, name, group, 0xC0000000); NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000);
if(objectlist.mSkelBase)
parentNode->attachObject(objectlist.mSkelBase);
return objectlist; return objectlist;
} }

View file

@ -50,6 +50,8 @@ struct ObjectList {
// bones in the mSkelBase which are NiCamera nodes. // bones in the mSkelBase which are NiCamera nodes.
std::vector<Ogre::Bone*> mCameras; std::vector<Ogre::Bone*> mCameras;
std::map<int,TextKeyMap> mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers; std::vector<Ogre::Controller<Ogre::Real> > mControllers;
ObjectList() : mSkelBase(0) ObjectList() : mSkelBase(0)
@ -69,7 +71,7 @@ public:
std::string name, std::string name,
const std::string &group="General"); const std::string &group="General");
static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr, static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
std::string name, std::string name,
const std::string &group="General"); const std::string &group="General");
}; };
@ -85,6 +87,10 @@ public:
NodeTargetValue(Ogre::Node *target) : mNode(target) NodeTargetValue(Ogre::Node *target) : mNode(target)
{ } { }
virtual Ogre::Quaternion getRotation(T value) const = 0;
virtual Ogre::Vector3 getTranslation(T value) const = 0;
virtual Ogre::Vector3 getScale(T value) const = 0;
void setNode(Ogre::Node *target) void setNode(Ogre::Node *target)
{ mNode = target; } { mNode = target; }
Ogre::Node *getNode() const Ogre::Node *getNode() const
@ -94,14 +100,4 @@ typedef Ogre::SharedPtr<NodeTargetValue<Ogre::Real> > NodeTargetValueRealPtr;
} }
namespace std
{
// These operators allow extra data types to be stored in an Ogre::Any
// object, which can then be stored in user object bindings on the nodes
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&);
}
#endif #endif

View file

@ -11,142 +11,7 @@
namespace NifOgre namespace NifOgre
{ {
void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime) void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent)
{
Ogre::Animation *anim = skel->createAnimation(name, stopTime);
for(size_t i = 0;i < ctrls.size();i++)
{
const Nif::NiKeyframeController *kfc = ctrls[i];
if(kfc->data.empty())
continue;
const Nif::NiKeyframeData *kf = kfc->data.getPtr();
/* Get the keyframes and make sure they're sorted first to last */
const Nif::QuaternionKeyList &quatkeys = kf->mRotations;
const Nif::Vector3KeyList &trankeys = kf->mTranslations;
const Nif::FloatKeyList &scalekeys = kf->mScales;
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
Ogre::Bone *bone = skel->getBone(targets[i]);
// NOTE: For some reason, Ogre doesn't like the node track ID being different from
// the bone ID
Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ?
anim->getNodeTrack(bone->getHandle()) :
anim->createNodeTrack(bone->getHandle(), bone);
Ogre::Quaternion lastquat, curquat;
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
if(quatiter != quatkeys.mKeys.end())
lastquat = curquat = quatiter->mValue;
if(traniter != trankeys.mKeys.end())
lasttrans = curtrans = traniter->mValue;
if(scaleiter != scalekeys.mKeys.end())
lastscale = curscale = Ogre::Vector3(scaleiter->mValue);
bool didlast = false;
while(!didlast)
{
float curtime = std::numeric_limits<float>::max();
//Get latest time
if(quatiter != quatkeys.mKeys.end())
curtime = std::min(curtime, quatiter->mTime);
if(traniter != trankeys.mKeys.end())
curtime = std::min(curtime, traniter->mTime);
if(scaleiter != scalekeys.mKeys.end())
curtime = std::min(curtime, scaleiter->mTime);
curtime = std::max(curtime, startTime);
if(curtime >= stopTime)
{
didlast = true;
curtime = stopTime;
}
// Get the latest quaternions, translations, and scales for the
// current time
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
{
lastquat = curquat;
if(++quatiter != quatkeys.mKeys.end())
curquat = quatiter->mValue;
}
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
{
lasttrans = curtrans;
if(++traniter != trankeys.mKeys.end())
curtrans = traniter->mValue;
}
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
{
lastscale = curscale;
if(++scaleiter != scalekeys.mKeys.end())
curscale = Ogre::Vector3(scaleiter->mValue);
}
Ogre::TransformKeyFrame *kframe;
kframe = nodetrack->createNodeKeyFrame(curtime);
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
kframe->setRotation(curquat);
else
{
Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
}
if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
kframe->setTranslate(curtrans);
else
{
Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
}
if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
kframe->setScale(curscale);
else
{
Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
kframe->setScale(lastscale + ((curscale-lastscale)*diff));
}
}
}
anim->optimise();
}
TextKeyMap NIFSkeletonLoader::extractTextKeys(const Nif::NiTextKeyExtraData *tk)
{
TextKeyMap textkeys;
for(size_t i = 0;i < tk->list.size();i++)
{
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
if(::isspace(str[pos]))
{
pos++;
continue;
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
}
}
return textkeys;
}
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent)
{ {
Ogre::Bone *bone; Ogre::Bone *bone;
if(!skel->hasBone(node->name)) if(!skel->hasBone(node->name))
@ -164,6 +29,8 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */
node->recType == Nif::RC_NiBSParticleNode ||
node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiCamera ||
node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiAutoNormalParticles ||
node->recType == Nif::RC_NiRotatingParticles node->recType == Nif::RC_NiRotatingParticles
@ -173,28 +40,16 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
Nif::ControllerPtr ctrl = node->controller; Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty()) while(!ctrl.empty())
{ {
if(ctrl->recType == Nif::RC_NiKeyframeController) if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr()));
else if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
ctrl->recType == Nif::RC_NiVisController || ctrl->recType == Nif::RC_NiVisController ||
ctrl->recType == Nif::RC_NiUVController ctrl->recType == Nif::RC_NiUVController ||
ctrl->recType == Nif::RC_NiKeyframeController ||
ctrl->recType == Nif::RC_NiGeomMorpherController
)) ))
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
ctrl = ctrl->next; ctrl = ctrl->next;
} }
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
textkeys = extractTextKeys(tk);
animroot = bone;
}
e = e->extra;
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode) if(ninode)
{ {
@ -202,7 +57,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
for(size_t i = 0;i < children.length();i++) for(size_t i = 0;i < children.length();i++)
{ {
if(!children[i].empty()) if(!children[i].empty())
buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); buildBones(skel, children[i].getPtr(), bone);
} }
} }
} }
@ -215,84 +70,14 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource)
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0)); const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
std::vector<const Nif::NiKeyframeController*> ctrls;
Ogre::Bone *animroot = NULL;
TextKeyMap textkeys;
try { try {
buildBones(skel, node, animroot, textkeys, ctrls); buildBones(skel, node);
} }
catch(std::exception &e) { catch(std::exception &e) {
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl; std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
std::cerr<< e.what() <<std::endl; std::cerr<< e.what() <<std::endl;
return; return;
} }
/* Animations without textkeys don't get Ogre::Animation objects. */
if(!animroot)
return;
std::vector<std::string> targets;
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
if(ctrls.size() == 0) // No animations? Then we're done.
return;
float maxtime = 0.0f;
for(size_t i = 0;i < ctrls.size();i++)
{
const Nif::NiKeyframeController *ctrl = ctrls[i];
maxtime = std::max(maxtime, ctrl->timeStop);
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
if(target != NULL)
targets.push_back(target->name);
}
if(targets.size() != ctrls.size())
{
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
return;
}
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
std::string currentgroup;
TextKeyMap::const_iterator keyiter = textkeys.begin();
for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
{
std::string::size_type sep = keyiter->second.find(':');
if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
(sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
continue;
currentgroup = keyiter->second.substr(0, sep);
if(skel->hasAnimation(currentgroup))
continue;
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
while((--lastkeyiter)->first > keyiter->first)
{
if(lastkeyiter->second.find(':') == currentgroup.length() &&
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
break;
}
buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first);
TextKeyMap::const_iterator insiter(keyiter);
TextKeyMap groupkeys;
do {
sep = insiter->second.find(':');
if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0)
groupkeys.insert(std::make_pair(insiter->first, insiter->second.substr(sep+2)));
else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0))
groupkeys.insert(std::make_pair(insiter->first, insiter->second));
} while(insiter++ != lastkeyiter);
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
}
} }
@ -300,16 +85,16 @@ Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, con
{ {
/* We need to be a little aggressive here, since some NIFs have a crap-ton /* We need to be a little aggressive here, since some NIFs have a crap-ton
* of nodes and Ogre only supports 256 bones. We will skip a skeleton if: * of nodes and Ogre only supports 256 bones. We will skip a skeleton if:
* There are no bones used for skinning, there are no controllers on non- * There are no bones used for skinning, there are no controllers, there
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree * are no nodes named "AttachLight", and the tree consists of NiNode,
* consists of NiNode, NiTriShape, and RootCollisionNode types only. * NiTriShape, and RootCollisionNode types only.
*/ */
if(!node->boneTrafo) if(!node->boneTrafo)
{ {
if(node->recType == Nif::RC_NiTriShape)
return Ogre::SkeletonPtr();
if(node->controller.empty() && node->name != "AttachLight") if(node->controller.empty() && node->name != "AttachLight")
{ {
if(node->recType == Nif::RC_NiTriShape)
return Ogre::SkeletonPtr();
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
{ {
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);

View file

@ -36,10 +36,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader
abort(); abort();
} }
static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime); void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL);
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk);
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL);
// Lookup to retrieve an Ogre bone handle for a given Nif record index // Lookup to retrieve an Ogre bone handle for a given Nif record index
std::map<int,int> mNifToOgreHandleMap; std::map<int,int> mNifToOgreHandleMap;

View file

@ -9,6 +9,7 @@
<Property key="OffsetHeight" value="0"/> <Property key="OffsetHeight" value="0"/>
<Codes> <Codes>
<Code range="33 126"/> <Code range="33 126"/>
<Code range="160"/> <!-- Non-breaking space -->
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs --> <Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/> <Code range="1025 1105"/>
<Code range="2026"/> <!-- Ellipsis --> <Code range="2026"/> <!-- Ellipsis -->

View file

@ -511,8 +511,8 @@ namespace Physic
void PhysicEngine::stepSimulation(double deltaT) void PhysicEngine::stepSimulation(double deltaT)
{ {
// This isn't needed as there are no dynamic objects at this point // This seems to be needed for character controller objects
//dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); dynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
if(isDebugCreated) if(isDebugCreated)
{ {
mDebugDrawer->step(); mDebugDrawer->step();

View file

@ -559,6 +559,8 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
assert(mgr); assert(mgr);
mSceneMgr = mgr; mSceneMgr = mgr;
mShaderRenderManager = NULL;
mRenderManager = NULL;
using namespace MyGUI; using namespace MyGUI;
@ -574,7 +576,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
// Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later.
mLogManager = new LogManager(); mLogManager = new LogManager();
mRenderManager = new MyGUI::ShaderBasedRenderManager(); if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION))
mShaderRenderManager = new MyGUI::ShaderBasedRenderManager();
else
mRenderManager = new MyGUI::OgreRenderManager();
mDataManager = new MyGUI::FixedOgreDataManager(); mDataManager = new MyGUI::FixedOgreDataManager();
LogManager::getInstance().setSTDOutputEnabled(logging); LogManager::getInstance().setSTDOutputEnabled(logging);
@ -582,6 +587,9 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
if (!theLogFile.empty()) if (!theLogFile.empty())
LogManager::getInstance().createDefaultSource(theLogFile); LogManager::getInstance().createDefaultSource(theLogFile);
if (mShaderRenderManager)
mShaderRenderManager->initialise(wnd, mgr);
else
mRenderManager->initialise(wnd, mgr); mRenderManager->initialise(wnd, mgr);
mDataManager->initialise("General"); mDataManager->initialise("General");
@ -591,10 +599,18 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
} }
void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd)
{
if (mShaderRenderManager)
{
mShaderRenderManager->setRenderWindow (wnd);
mShaderRenderManager->setActiveViewport(0);
}
else
{ {
mRenderManager->setRenderWindow (wnd); mRenderManager->setRenderWindow (wnd);
mRenderManager->setActiveViewport(0); mRenderManager->setActiveViewport(0);
} }
}
void MyGUIManager::shutdown() void MyGUIManager::shutdown()
{ {
@ -606,6 +622,12 @@ void MyGUIManager::shutdown()
delete mRenderManager; delete mRenderManager;
mRenderManager = NULL; mRenderManager = NULL;
} }
if(mShaderRenderManager)
{
mShaderRenderManager->shutdown();
delete mShaderRenderManager;
mShaderRenderManager = NULL;
}
if(mDataManager) if(mDataManager)
{ {
mDataManager->shutdown(); mDataManager->shutdown();

View file

@ -8,6 +8,7 @@ namespace MyGUI
class Gui; class Gui;
class LogManager; class LogManager;
class OgreDataManager; class OgreDataManager;
class OgreRenderManager;
class ShaderBasedRenderManager; class ShaderBasedRenderManager;
} }
@ -25,12 +26,12 @@ namespace GUI
MyGUI::Gui *mGui; MyGUI::Gui *mGui;
MyGUI::LogManager* mLogManager; MyGUI::LogManager* mLogManager;
MyGUI::OgreDataManager* mDataManager; MyGUI::OgreDataManager* mDataManager;
MyGUI::ShaderBasedRenderManager* mRenderManager; MyGUI::OgreRenderManager* mRenderManager;
MyGUI::ShaderBasedRenderManager* mShaderRenderManager;
Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mSceneMgr;
public: public:
MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {}
MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""))
{ {
setup(wnd,mgr,logging, logDir); setup(wnd,mgr,logging, logDir);

View file

@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr)
, mTargetAlpha(0.f) , mTargetAlpha(0.f)
, mCurrentAlpha(0.f) , mCurrentAlpha(0.f)
, mStartAlpha(0.f) , mStartAlpha(0.f)
, mFactor(1.f)
{ {
// Create the fading material // Create the fading material
MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
@ -63,18 +64,19 @@ void Fader::update(float dt)
if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
} }
applyAlpha();
mRemainingTime -= dt; mRemainingTime -= dt;
} }
if (mCurrentAlpha == 0.f) mRectangle->setVisible(false); if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)
mRectangle->setVisible(false);
else
applyAlpha();
} }
void Fader::applyAlpha() void Fader::applyAlpha()
{ {
mRectangle->setVisible(true); mRectangle->setVisible(true);
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor));
} }
void Fader::fadeIn(float time) void Fader::fadeIn(float time)

View file

@ -29,6 +29,8 @@ namespace Render
void fadeOut(const float time); void fadeOut(const float time);
void fadeTo(const int percent, const float time); void fadeTo(const int percent, const float time);
void setFactor (float factor) { mFactor = factor; }
private: private:
enum FadingMode enum FadingMode
{ {
@ -49,6 +51,8 @@ namespace Render
float mCurrentAlpha; float mCurrentAlpha;
float mStartAlpha; float mStartAlpha;
float mFactor;
Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mSceneMgr;
}; };
}} }}

View file

@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind
OpenMW is an attempt at recreating the engine for the popular role-playing game OpenMW is an attempt at recreating the engine for the popular role-playing game
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
Version: 0.22.0 Version: 0.23.0
License: GPL (see GPL3.txt for more information) License: GPL (see GPL3.txt for more information)
Website: http://www.openmw.org Website: http://www.openmw.org
@ -94,6 +94,68 @@ Allowed options:
CHANGELOG CHANGELOG
0.23.0
Bug #522: Player collides with placeable items
Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open
Bug #561: Tooltip word wrapping delay
Bug #578: Bribing works incorrectly
Bug #601: PositionCell fails on negative coordinates
Bug #606: Some NPCs hairs not rendered with Better Heads addon
Bug #609: Bad rendering of bone boots
Bug #613: Messagebox causing assert to fail
Bug #631: Segfault on shutdown
Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard
Bug #635: Scale NPCs depending on race
Bug #643: Dialogue Race select function is inverted
Bug #646: Twohanded weapons don't work properly
Bug #654: Crash when dropping objects without a collision shape
Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell
Bug #660: "g" in "change" cut off in Race Menu
Bug #661: Arrille sells me the key to his upstairs room
Bug #662: Day counter starts at 2 instead of 1
Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur
Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window.
Bug #666: Looking up/down problem
Bug #667: Active effects border visible during loading
Bug #669: incorrect player position at new game start
Bug #670: race selection menu: sex, face and hair left button not totally clickable
Bug #671: new game: player is naked
Bug #674: buying or selling items doesn't change amount of gold
Bug #675: fatigue is not set to its maximum when starting a new game
Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly
Bug #680: different gold coins in Tel Mara
Bug #682: Race menu ignores playable flag for some hairs and faces
Bug #685: Script compiler does not accept ":" after a function name
Bug #688: dispose corpse makes cross-hair to disappear
Bug #691: Auto equipping ignores equipment conditions
Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder
Bug #696: Draugr incorrect head offset
Bug #697: Sail transparency issue
Bug #700: "On the rocks" mod does not load its UV coordinates correctly.
Bug #702: Some race mods don't work
Bug #711: Crash during character creation
Bug #715: Growing Tauryon
Bug #725: Auto calculate stats
Bug #728: Failure to open container and talk dialogue
Bug #731: Crash with Mush-Mere's "background" topic
Feature #55/657: Item Repairing
Feature #62/87: Enchanting
Feature #99: Pathfinding
Feature #104: AI Package: Travel
Feature #129: Levelled items
Feature #204: Texture animations
Feature #239: Fallback-Settings
Feature #535: Console object selection improvements
Feature #629: Add levelup description in levelup layout dialog
Feature #630: Optional format subrecord in (tes3) header
Feature #641: Armor rating
Feature #645: OnDeath script function
Feature #683: Companion item UI
Feature #698: Basic Particles
Task #648: Split up components/esm/loadlocks
Task #695: mwgui cleanup
0.22.0 0.22.0
Bug #311: Potential infinite recursion in script compiler Bug #311: Potential infinite recursion in script compiler