mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-28 23:06:41 +00:00
Merge branch 'master' of http://github.com/zinnschlag/openmw
This commit is contained in:
commit
304692dc8e
21 changed files with 1198 additions and 642 deletions
|
@ -41,6 +41,14 @@ set(GAMEGUI
|
||||||
)
|
)
|
||||||
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
||||||
|
|
||||||
|
set(GAMEDIALOGUE_HEADER
|
||||||
|
mwdialogue/dialoguemanager.hpp
|
||||||
|
)
|
||||||
|
set(GAMEDIALOGUE
|
||||||
|
mwdialogue/dialoguemanager.cpp
|
||||||
|
)
|
||||||
|
source_group(apps\\openmw\\mwdialogue FILES ${GAMEDIALOGUE_HEADER} ${GAMEDIALOGUE})
|
||||||
|
|
||||||
set(GAMESCRIPT
|
set(GAMESCRIPT
|
||||||
mwscript/scriptmanager.cpp
|
mwscript/scriptmanager.cpp
|
||||||
mwscript/compilercontext.cpp
|
mwscript/compilercontext.cpp
|
||||||
|
@ -97,6 +105,7 @@ set(GAMEWORLD_HEADER
|
||||||
mwworld/action.hpp
|
mwworld/action.hpp
|
||||||
mwworld/nullaction.hpp
|
mwworld/nullaction.hpp
|
||||||
mwworld/actionteleport.hpp
|
mwworld/actionteleport.hpp
|
||||||
|
mwworld/containerstore.hpp
|
||||||
mwworld/actiontalk.hpp
|
mwworld/actiontalk.hpp
|
||||||
mwworld/actiontake.hpp
|
mwworld/actiontake.hpp
|
||||||
mwworld/containerstore.hpp
|
mwworld/containerstore.hpp
|
||||||
|
@ -164,11 +173,11 @@ set(GAMEMECHANICS_HEADER
|
||||||
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
|
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
|
||||||
|
|
||||||
set(OPENMW_CPP ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEGUI} ${GAMEWORLD}
|
set(OPENMW_CPP ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEGUI} ${GAMEWORLD}
|
||||||
${GAMECLASS} ${GAMEMECHANICS}
|
${GAMECLASS} ${GAMEMECHANICS} ${GAMEDIALOGUE}
|
||||||
)
|
)
|
||||||
set(OPENMW_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}
|
set(OPENMW_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}
|
||||||
${GAMESOUND_HEADER} ${GAMEGUI_HEADER} ${GAMEWORLD_HEADER} ${GAMECLASS_HEADER}
|
${GAMESOUND_HEADER} ${GAMEGUI_HEADER} ${GAMEWORLD_HEADER} ${GAMECLASS_HEADER}
|
||||||
${GAMEMECHANICS_HEADER}
|
${GAMEMECHANICS_HEADER} ${GAMEDIALOG_HEADERUE}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include "mwclass/classes.hpp"
|
#include "mwclass/classes.hpp"
|
||||||
|
|
||||||
|
#include "mwdialogue/dialoguemanager.hpp"
|
||||||
|
|
||||||
#include "mwmechanics/mechanicsmanager.hpp"
|
#include "mwmechanics/mechanicsmanager.hpp"
|
||||||
|
|
||||||
#include <OgreRoot.h>
|
#include <OgreRoot.h>
|
||||||
|
@ -117,6 +119,7 @@ OMW::Engine::~Engine()
|
||||||
delete mEnvironment.mSoundManager;
|
delete mEnvironment.mSoundManager;
|
||||||
delete mEnvironment.mGlobalScripts;
|
delete mEnvironment.mGlobalScripts;
|
||||||
delete mEnvironment.mMechanicsManager;
|
delete mEnvironment.mMechanicsManager;
|
||||||
|
delete mEnvironment.mDialogueManager;
|
||||||
delete mScriptManager;
|
delete mScriptManager;
|
||||||
delete mScriptContext;
|
delete mScriptContext;
|
||||||
}
|
}
|
||||||
|
@ -253,6 +256,9 @@ void OMW::Engine::go()
|
||||||
mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (
|
mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (
|
||||||
mEnvironment.mWorld->getStore(), *mEnvironment.mWindowManager);
|
mEnvironment.mWorld->getStore(), *mEnvironment.mWindowManager);
|
||||||
|
|
||||||
|
// Create dialog system
|
||||||
|
mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment);
|
||||||
|
|
||||||
// load cell
|
// load cell
|
||||||
ESM::Position pos;
|
ESM::Position pos;
|
||||||
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
||||||
|
|
|
@ -15,6 +15,14 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
|
std::string Creature::getId (const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref =
|
||||||
|
ptr.get<ESM::Creature>();
|
||||||
|
|
||||||
|
return ref->base->mId;
|
||||||
|
}
|
||||||
|
|
||||||
void Creature::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Creature::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
virtual std::string getId (const MWWorld::Ptr& ptr) const;
|
||||||
|
///< Return ID of \a ptr
|
||||||
|
|
||||||
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const;
|
MWWorld::Environment& environment) const;
|
||||||
///< Add reference into a cell for rendering
|
///< Add reference into a cell for rendering
|
||||||
|
|
|
@ -16,6 +16,14 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
|
std::string Npc::getId (const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
|
||||||
|
ptr.get<ESM::NPC>();
|
||||||
|
|
||||||
|
return ref->base->mId;
|
||||||
|
}
|
||||||
|
|
||||||
void Npc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Npc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
virtual std::string getId (const MWWorld::Ptr& ptr) const;
|
||||||
|
///< Return ID of \a ptr
|
||||||
|
|
||||||
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const;
|
MWWorld::Environment& environment) const;
|
||||||
///< Add reference into a cell for rendering
|
///< Add reference into a cell for rendering
|
||||||
|
@ -26,14 +29,14 @@ namespace MWClass
|
||||||
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
|
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
|
||||||
///< Return creature stats
|
///< Return creature stats
|
||||||
|
|
||||||
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const;
|
|
||||||
///< Generate action for activation
|
|
||||||
|
|
||||||
virtual MWWorld::ContainerStore<MWWorld::RefData>& getContainerStore (
|
virtual MWWorld::ContainerStore<MWWorld::RefData>& getContainerStore (
|
||||||
const MWWorld::Ptr& ptr) const;
|
const MWWorld::Ptr& ptr) const;
|
||||||
///< Return container store
|
///< Return container store
|
||||||
|
|
||||||
|
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
||||||
|
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const;
|
||||||
|
///< Generate action for activation
|
||||||
|
|
||||||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||||
///< Return name of the script attached to ptr
|
///< Return name of the script attached to ptr
|
||||||
|
|
||||||
|
|
280
apps/openmw/mwdialogue/dialoguemanager.cpp
Normal file
280
apps/openmw/mwdialogue/dialoguemanager.cpp
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
|
||||||
|
#include "dialoguemanager.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <components/esm/loaddial.hpp>
|
||||||
|
|
||||||
|
#include <components/esm_store/store.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/environment.hpp"
|
||||||
|
#include "../mwworld/world.hpp"
|
||||||
|
#include "../mwworld/refdata.hpp"
|
||||||
|
|
||||||
|
#include "../mwgui/window_manager.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string toLower (const std::string& name)
|
||||||
|
{
|
||||||
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
return lowerCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
bool selectCompare (char comp, T1 value1, T2 value2)
|
||||||
|
{
|
||||||
|
switch (comp)
|
||||||
|
{
|
||||||
|
case '0': return value1==value2;
|
||||||
|
case '1': return value1!=value2;
|
||||||
|
case '2': return value1>value2;
|
||||||
|
case '3': return value1>=value2;
|
||||||
|
case '4': return value1<value2;
|
||||||
|
case '5': return value1<=value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown compare type in dialogue info select");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool checkLocal (char comp, const std::string& name, T value, const MWWorld::Ptr& actor,
|
||||||
|
const ESMS::ESMStore& store)
|
||||||
|
{
|
||||||
|
std::string scriptName = MWWorld::Class::get (actor).getScript (actor);
|
||||||
|
|
||||||
|
if (scriptName.empty())
|
||||||
|
return false; // no script
|
||||||
|
|
||||||
|
const ESM::Script *script = store.scripts.find (scriptName);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i<static_cast<int> (script->varNames.size()); ++i)
|
||||||
|
if (script->varNames[i]==name)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i>=static_cast<int> (script->varNames.size()))
|
||||||
|
return false; // script does not have a variable of this name
|
||||||
|
|
||||||
|
const MWScript::Locals& locals = actor.getRefData().getLocals();
|
||||||
|
|
||||||
|
if (i<script->data.numShorts)
|
||||||
|
return selectCompare (comp, locals.mShorts[i], value);
|
||||||
|
else
|
||||||
|
i -= script->data.numShorts;
|
||||||
|
|
||||||
|
if (i<script->data.numLongs)
|
||||||
|
return selectCompare (comp, locals.mLongs[i], value);
|
||||||
|
else
|
||||||
|
i -= script->data.numShorts;
|
||||||
|
|
||||||
|
return selectCompare (comp, locals.mFloats.at (i), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool checkGlobal (char comp, const std::string& name, T value, MWWorld::World& world)
|
||||||
|
{
|
||||||
|
switch (world.getGlobalVariableType (name))
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
|
||||||
|
return selectCompare (comp, value, world.getGlobalVariable (name).mShort);
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
|
||||||
|
return selectCompare (comp, value, world.getGlobalVariable (name).mLong);
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
|
||||||
|
return selectCompare (comp, value, world.getGlobalVariable (name).mFloat);
|
||||||
|
|
||||||
|
case ' ':
|
||||||
|
|
||||||
|
world.getGlobalVariable (name); // trigger exception
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
throw std::runtime_error ("unsupported gobal variable type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
bool DialogueManager::isMatching (const MWWorld::Ptr& actor,
|
||||||
|
const ESM::DialInfo::SelectStruct& select) const
|
||||||
|
{
|
||||||
|
char type = select.selectRule[1];
|
||||||
|
|
||||||
|
if (type!='0')
|
||||||
|
{
|
||||||
|
char comp = select.selectRule[4];
|
||||||
|
std::string name = select.selectRule.substr (5);
|
||||||
|
|
||||||
|
// TODO types 4, 5, 6, 7, 8, 9, A, B, C
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case '1': // function
|
||||||
|
|
||||||
|
return false; // TODO implement functions
|
||||||
|
|
||||||
|
case '2': // global
|
||||||
|
|
||||||
|
if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
|
||||||
|
select.type==ESM::VT_Long)
|
||||||
|
{
|
||||||
|
if (!checkGlobal (comp, toLower (name), select.i, *mEnvironment.mWorld))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (select.type==ESM::VT_Float)
|
||||||
|
{
|
||||||
|
if (!checkGlobal (comp, toLower (name), select.f, *mEnvironment.mWorld))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error (
|
||||||
|
"unsupported variable type in dialogue info select");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case '3': // local
|
||||||
|
|
||||||
|
if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
|
||||||
|
select.type==ESM::VT_Long)
|
||||||
|
{
|
||||||
|
if (!checkLocal (comp, toLower (name), select.i, actor,
|
||||||
|
mEnvironment.mWorld->getStore()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (select.type==ESM::VT_Float)
|
||||||
|
{
|
||||||
|
if (!checkLocal (comp, toLower (name), select.f, actor,
|
||||||
|
mEnvironment.mWorld->getStore()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error (
|
||||||
|
"unsupported variable type in dialogue info select");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
std::cout << "unchecked select: " << type << " " << comp << " " << name << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DialogueManager::isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const
|
||||||
|
{
|
||||||
|
// actor id
|
||||||
|
if (!info.actor.empty())
|
||||||
|
if (toLower (info.actor)!=MWWorld::Class::get (actor).getId (actor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!info.race.empty())
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (!cellRef)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toLower (info.race)!=toLower (cellRef->base->race))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.clas.empty())
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (!cellRef)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toLower (info.clas)!=toLower (cellRef->base->cls))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.npcFaction.empty())
|
||||||
|
{
|
||||||
|
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||||
|
|
||||||
|
if (!cellRef)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toLower (info.npcFaction)!=toLower (cellRef->base->faction))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check player faction
|
||||||
|
|
||||||
|
// check cell
|
||||||
|
if (!info.cell.empty())
|
||||||
|
if (mEnvironment.mWorld->getPlayerPos().getPlayer().getCell()->cell->name != info.cell)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// TODO check DATAstruct
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.selects.begin());
|
||||||
|
iter != info.selects.end(); ++iter)
|
||||||
|
if (!isMatching (actor, *iter))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "unchecked entries:" << std::endl
|
||||||
|
<< " player faction: " << info.pcFaction << std::endl
|
||||||
|
<< " DATAstruct" << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogueManager::DialogueManager (MWWorld::Environment& environment) : mEnvironment (environment) {}
|
||||||
|
|
||||||
|
void DialogueManager::startDialogue (const MWWorld::Ptr& actor)
|
||||||
|
{
|
||||||
|
std::cout << "talking with " << MWWorld::Class::get (actor).getName (actor) << std::endl;
|
||||||
|
|
||||||
|
const ESM::Dialogue *dialogue = mEnvironment.mWorld->getStore().dialogs.find ("hello");
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (isMatching (actor, *iter))
|
||||||
|
{
|
||||||
|
// start dialogue
|
||||||
|
std::cout << "found matching info record" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "response: " << iter->response << std::endl;
|
||||||
|
|
||||||
|
if (!iter->sound.empty())
|
||||||
|
{
|
||||||
|
// TODO play sound
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iter->resultScript.empty())
|
||||||
|
{
|
||||||
|
std::cout << "script: " << iter->resultScript << std::endl;
|
||||||
|
// TODO execute script
|
||||||
|
}
|
||||||
|
|
||||||
|
mEnvironment.mWindowManager->setMode (MWGui::GM_Dialogue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
apps/openmw/mwdialogue/dialoguemanager.hpp
Normal file
32
apps/openmw/mwdialogue/dialoguemanager.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef GAME_MMDIALOG_DIALOGUEMANAGER_H
|
||||||
|
#define GAME_MWDIALOG_DIALOGUEMANAGER_H
|
||||||
|
|
||||||
|
#include <components/esm/loadinfo.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class Environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
class DialogueManager
|
||||||
|
{
|
||||||
|
MWWorld::Environment& mEnvironment;
|
||||||
|
|
||||||
|
bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo::SelectStruct& select) const;
|
||||||
|
|
||||||
|
bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DialogueManager (MWWorld::Environment& environment);
|
||||||
|
|
||||||
|
void startDialogue (const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "environment.hpp"
|
#include "environment.hpp"
|
||||||
|
|
||||||
#include "../mwgui/window_manager.hpp"
|
#include "../mwdialogue/dialoguemanager.hpp"
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,6 @@ namespace MWWorld
|
||||||
|
|
||||||
void ActionTalk::execute (Environment& environment)
|
void ActionTalk::execute (Environment& environment)
|
||||||
{
|
{
|
||||||
environment.mWindowManager->setMode (MWGui::GM_Dialogue);
|
environment.mDialogueManager->startDialogue (mActor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace MWWorld
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActionTalk (const Ptr& actor);
|
ActionTalk (const Ptr& actor);
|
||||||
|
///< \param actor The actor the player is talking to
|
||||||
|
|
||||||
virtual void execute (Environment& environment);
|
virtual void execute (Environment& environment);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,11 @@ namespace MWWorld
|
||||||
|
|
||||||
Class::~Class() {}
|
Class::~Class() {}
|
||||||
|
|
||||||
|
std::string Class::getId (const Ptr& ptr) const
|
||||||
|
{
|
||||||
|
throw std::runtime_error ("class does not support ID retrieval");
|
||||||
|
}
|
||||||
|
|
||||||
void Class::insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Class::insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,10 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual ~Class();
|
virtual ~Class();
|
||||||
|
|
||||||
|
virtual std::string getId (const Ptr& ptr) const;
|
||||||
|
///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval
|
||||||
|
/// (default implementation: throw an exception)
|
||||||
|
|
||||||
virtual void insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
virtual void insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const;
|
MWWorld::Environment& environment) const;
|
||||||
///< Add reference into a cell for rendering (default implementation: don't render anything).
|
///< Add reference into a cell for rendering (default implementation: don't render anything).
|
||||||
|
|
|
@ -21,6 +21,11 @@ namespace MWMechanics
|
||||||
class MechanicsManager;
|
class MechanicsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
class DialogueManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class World;
|
class World;
|
||||||
|
@ -31,7 +36,7 @@ namespace MWWorld
|
||||||
public:
|
public:
|
||||||
Environment()
|
Environment()
|
||||||
: mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
|
: mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
|
||||||
mMechanicsManager (0), mFrameDuration (0)
|
mMechanicsManager (0), mDialogueManager (0), mFrameDuration (0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
World *mWorld;
|
World *mWorld;
|
||||||
|
@ -39,9 +44,9 @@ namespace MWWorld
|
||||||
MWScript::GlobalScripts *mGlobalScripts;
|
MWScript::GlobalScripts *mGlobalScripts;
|
||||||
MWGui::WindowManager *mWindowManager;
|
MWGui::WindowManager *mWindowManager;
|
||||||
MWMechanics::MechanicsManager *mMechanicsManager;
|
MWMechanics::MechanicsManager *mMechanicsManager;
|
||||||
|
MWDialogue::DialogueManager *mDialogueManager;
|
||||||
float mFrameDuration;
|
float mFrameDuration;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,11 @@ IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
||||||
SET(ICONV_FIND_QUIETLY TRUE)
|
SET(ICONV_FIND_QUIETLY TRUE)
|
||||||
ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
||||||
|
|
||||||
|
IF(WIN32)
|
||||||
|
SET(ICONV_INCLUDE_DIR $ENV{ICONV_INCLUDE_DIR})
|
||||||
|
SET(ICONV_LIBRARIES $ENV{ICONV_LIBRARIES})
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
|
FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
|
||||||
|
|
||||||
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv c)
|
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv c)
|
||||||
|
|
|
@ -9,7 +9,10 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifndef __WIN32__
|
||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <libs/mangle/stream/stream.hpp>
|
#include <libs/mangle/stream/stream.hpp>
|
||||||
#include <libs/mangle/stream/servers/file_stream.hpp>
|
#include <libs/mangle/stream/servers/file_stream.hpp>
|
||||||
|
@ -621,6 +624,9 @@ public:
|
||||||
// Convert a string from the encoding used by Morrowind to UTF-8
|
// Convert a string from the encoding used by Morrowind to UTF-8
|
||||||
std::string convertToUTF8 (std::string input)
|
std::string convertToUTF8 (std::string input)
|
||||||
{
|
{
|
||||||
|
#ifdef __WIN32__
|
||||||
|
return input;
|
||||||
|
#else
|
||||||
std::string output = "";
|
std::string output = "";
|
||||||
|
|
||||||
//create convert description
|
//create convert description
|
||||||
|
@ -701,6 +707,7 @@ public:
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void skip(int bytes) { esm->seek(esm->tell()+bytes); }
|
void skip(int bytes) { esm->seek(esm->tell()+bytes); }
|
||||||
uint64_t getOffset() { return esm->tell(); }
|
uint64_t getOffset() { return esm->tell(); }
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#ifndef _ESM_DIAL_H
|
#ifndef _ESM_DIAL_H
|
||||||
#define _ESM_DIAL_H
|
#define _ESM_DIAL_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
#include "loadinfo.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM {
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ struct Dialogue
|
||||||
};
|
};
|
||||||
|
|
||||||
char type;
|
char type;
|
||||||
|
std::vector<DialInfo> mInfo;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
#include "loadcont.hpp"
|
#include "loadcont.hpp"
|
||||||
#include "loadcrea.hpp"
|
#include "loadcrea.hpp"
|
||||||
#include "loadcrec.hpp"
|
#include "loadcrec.hpp"
|
||||||
|
#include "loadinfo.hpp"
|
||||||
#include "loaddial.hpp"
|
#include "loaddial.hpp"
|
||||||
#include "loaddoor.hpp"
|
#include "loaddoor.hpp"
|
||||||
#include "loadench.hpp"
|
#include "loadench.hpp"
|
||||||
#include "loadfact.hpp"
|
#include "loadfact.hpp"
|
||||||
#include "loadglob.hpp"
|
#include "loadglob.hpp"
|
||||||
#include "loadgmst.hpp"
|
#include "loadgmst.hpp"
|
||||||
#include "loadinfo.hpp"
|
|
||||||
#include "loadingr.hpp"
|
#include "loadingr.hpp"
|
||||||
#include "loadland.hpp"
|
#include "loadland.hpp"
|
||||||
#include "loadlevlist.hpp"
|
#include "loadlevlist.hpp"
|
||||||
|
|
|
@ -20,6 +20,8 @@ void ESMStore::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
set<string> missing;
|
set<string> missing;
|
||||||
|
|
||||||
|
ESM::Dialogue *dialogue = 0;
|
||||||
|
|
||||||
// Loop through all records
|
// Loop through all records
|
||||||
while(esm.hasMoreRecs())
|
while(esm.hasMoreRecs())
|
||||||
{
|
{
|
||||||
|
@ -30,21 +32,57 @@ void ESMStore::load(ESMReader &esm)
|
||||||
RecListList::iterator it = recLists.find(n.val);
|
RecListList::iterator it = recLists.find(n.val);
|
||||||
|
|
||||||
if(it == recLists.end())
|
if(it == recLists.end())
|
||||||
|
{
|
||||||
|
if (n.val==ESM::REC_INFO)
|
||||||
|
{
|
||||||
|
if (dialogue)
|
||||||
|
{
|
||||||
|
ESM::DialInfo info;
|
||||||
|
info.load (esm);
|
||||||
|
|
||||||
|
dialogue->mInfo.push_back (info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "error: info record without dialog" << std::endl;
|
||||||
|
esm.skipRecord();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Not found (this would be an error later)
|
// Not found (this would be an error later)
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
missing.insert(n.toString());
|
missing.insert(n.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Load it
|
// Load it
|
||||||
std::string id = esm.getHNOString("NAME");
|
std::string id = esm.getHNOString("NAME");
|
||||||
it->second->load(esm, id);
|
it->second->load(esm, id);
|
||||||
|
|
||||||
|
if (n.val==ESM::REC_DIAL)
|
||||||
|
{
|
||||||
|
RecListT<Dialogue>& recList = static_cast<RecListT<Dialogue>& > (*it->second);
|
||||||
|
|
||||||
|
id = recList.toLower (id);
|
||||||
|
|
||||||
|
RecListT<Dialogue>::MapType::iterator iter = recList.list.find (id);
|
||||||
|
|
||||||
|
assert (iter!=recList.list.end());
|
||||||
|
|
||||||
|
dialogue = &iter->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dialogue = 0;
|
||||||
|
|
||||||
// Insert the reference into the global lookup
|
// Insert the reference into the global lookup
|
||||||
if(!id.empty())
|
if(!id.empty())
|
||||||
all[id] = n.val;
|
all[id] = n.val;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This information isn't needed on screen. But keep the code around
|
/* This information isn't needed on screen. But keep the code around
|
||||||
for debugging purposes later.
|
for debugging purposes later.
|
||||||
|
|
|
@ -69,7 +69,6 @@ namespace ESMS
|
||||||
// Lists that need special rules
|
// Lists that need special rules
|
||||||
CellList cells;
|
CellList cells;
|
||||||
RecIDListT<GameSetting> gameSettings;
|
RecIDListT<GameSetting> gameSettings;
|
||||||
//RecListT<DialInfo> dialInfos;
|
|
||||||
//RecListT<Land> lands;
|
//RecListT<Land> lands;
|
||||||
//RecListT<LandTexture> landTexts;
|
//RecListT<LandTexture> landTexts;
|
||||||
//RecListT<MagicEffect> magicEffects;
|
//RecListT<MagicEffect> magicEffects;
|
||||||
|
@ -92,7 +91,6 @@ namespace ESMS
|
||||||
|
|
||||||
ESMStore()
|
ESMStore()
|
||||||
{
|
{
|
||||||
recLists[REC_ACTI] = &activators;
|
|
||||||
recLists[REC_ACTI] = &activators;
|
recLists[REC_ACTI] = &activators;
|
||||||
recLists[REC_ALCH] = &potions;
|
recLists[REC_ALCH] = &potions;
|
||||||
recLists[REC_APPA] = &appas;
|
recLists[REC_APPA] = &appas;
|
||||||
|
@ -113,7 +111,6 @@ namespace ESMS
|
||||||
recLists[REC_FACT] = &factions;
|
recLists[REC_FACT] = &factions;
|
||||||
recLists[REC_GLOB] = &globals;
|
recLists[REC_GLOB] = &globals;
|
||||||
recLists[REC_GMST] = &gameSettings;
|
recLists[REC_GMST] = &gameSettings;
|
||||||
//recLists[REC_INFO] = &dialInfos;
|
|
||||||
recLists[REC_INGR] = &ingreds;
|
recLists[REC_INGR] = &ingreds;
|
||||||
//recLists[REC_LAND] = &lands;
|
//recLists[REC_LAND] = &lands;
|
||||||
recLists[REC_LEVC] = &creatureLists;
|
recLists[REC_LEVC] = &creatureLists;
|
||||||
|
|
|
@ -26,12 +26,13 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <libs/mangle/vfs/servers/ogre_vfs.hpp>
|
#include <libs/mangle/vfs/servers/ogre_vfs.hpp>
|
||||||
#include "components/nif/nif_file.hpp"
|
#include "../nif/nif_file.hpp"
|
||||||
#include "components/nif/node.hpp"
|
#include "../nif/node.hpp"
|
||||||
#include "components/nif/data.hpp"
|
#include "../nif/data.hpp"
|
||||||
#include "components/nif/property.hpp"
|
#include "../nif/property.hpp"
|
||||||
#include "libs/platform/strings.h"
|
#include <libs/platform/strings.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
// For warning messages
|
// For warning messages
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -45,21 +46,43 @@ using namespace Ogre;
|
||||||
using namespace Nif;
|
using namespace Nif;
|
||||||
using namespace Mangle::VFS;
|
using namespace Mangle::VFS;
|
||||||
|
|
||||||
// This is the interface to the Ogre resource system. It allows us to
|
NIFLoader& NIFLoader::getSingleton()
|
||||||
// load NIFs from BSAs, in the file system and in any other place we
|
|
||||||
// tell Ogre to look (eg. in zip or rar files.) It's also used to
|
|
||||||
// check for the existence of texture files, so we can exchange the
|
|
||||||
// extension from .tga to .dds if the texture is missing.
|
|
||||||
static OgreVFS *vfs;
|
|
||||||
|
|
||||||
// Singleton instance used by load()
|
|
||||||
static NIFLoader g_sing;
|
|
||||||
|
|
||||||
// Makeshift error reporting system
|
|
||||||
static string errName;
|
|
||||||
static void warn(const string &msg)
|
|
||||||
{
|
{
|
||||||
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
|
static NIFLoader instance;
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
NIFLoader* NIFLoader::getSingletonPtr()
|
||||||
|
{
|
||||||
|
return &getSingleton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NIFLoader::warn(string msg)
|
||||||
|
{
|
||||||
|
std::cerr << "NIFLoader: Warn:" << msg << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void NIFLoader::fail(string msg)
|
||||||
|
{
|
||||||
|
std::cerr << "NIFLoader: Fail: "<< msg << std::endl;
|
||||||
|
assert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 NIFLoader::convertVector3(const Nif::Vector& vec)
|
||||||
|
{
|
||||||
|
return Ogre::Vector3(vec.array);
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot)
|
||||||
|
{
|
||||||
|
Real matrix[3][3];
|
||||||
|
|
||||||
|
for (int i=0; i<3; i++)
|
||||||
|
for (int j=0; j<3; j++)
|
||||||
|
matrix[i][j] = rot.v[i].array[j];
|
||||||
|
|
||||||
|
return Quaternion(Matrix3(matrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper class that computes the bounding box and of a mesh
|
// Helper class that computes the bounding box and of a mesh
|
||||||
|
@ -126,12 +149,24 @@ public:
|
||||||
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
|
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
|
||||||
}
|
}
|
||||||
|
|
||||||
float minX() { return X.min; }
|
float minX() {
|
||||||
float maxX() { return X.max; }
|
return X.min;
|
||||||
float minY() { return Y.min; }
|
}
|
||||||
float maxY() { return Y.max; }
|
float maxX() {
|
||||||
float minZ() { return Z.min; }
|
return X.max;
|
||||||
float maxZ() { return Z.max; }
|
}
|
||||||
|
float minY() {
|
||||||
|
return Y.min;
|
||||||
|
}
|
||||||
|
float maxY() {
|
||||||
|
return Y.max;
|
||||||
|
}
|
||||||
|
float minZ() {
|
||||||
|
return Z.min;
|
||||||
|
}
|
||||||
|
float maxZ() {
|
||||||
|
return Z.max;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Conversion of blend / test mode from NIF -> OGRE.
|
// Conversion of blend / test mode from NIF -> OGRE.
|
||||||
|
@ -177,7 +212,7 @@ static CompareFunction getTestMode(int mode)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void createMaterial(const String &name,
|
void NIFLoader::createMaterial(const String &name,
|
||||||
const Vector &ambient,
|
const Vector &ambient,
|
||||||
const Vector &diffuse,
|
const Vector &diffuse,
|
||||||
const Vector &specular,
|
const Vector &specular,
|
||||||
|
@ -186,7 +221,7 @@ static void createMaterial(const String &name,
|
||||||
float alphaFlags, float alphaTest,
|
float alphaFlags, float alphaTest,
|
||||||
const String &texName)
|
const String &texName)
|
||||||
{
|
{
|
||||||
MaterialPtr material = MaterialManager::getSingleton().create(name, "General");
|
MaterialPtr material = MaterialManager::getSingleton().create(name, resourceGroup);
|
||||||
|
|
||||||
// This assigns the texture to this material. If the texture name is
|
// This assigns the texture to this material. If the texture name is
|
||||||
// a file name, and this file exists (in a resource directory), it
|
// a file name, and this file exists (in a resource directory), it
|
||||||
|
@ -196,7 +231,8 @@ static void createMaterial(const String &name,
|
||||||
if (!texName.empty())
|
if (!texName.empty())
|
||||||
{
|
{
|
||||||
Pass *pass = material->getTechnique(0)->getPass(0);
|
Pass *pass = material->getTechnique(0)->getPass(0);
|
||||||
/*TextureUnitState *txt =*/ pass->createTextureUnitState(texName);
|
/*TextureUnitState *txt =*/
|
||||||
|
pass->createTextureUnitState(texName);
|
||||||
|
|
||||||
/* As of yet UNTESTED code from Chris:
|
/* As of yet UNTESTED code from Chris:
|
||||||
pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
|
pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
|
||||||
|
@ -254,7 +290,7 @@ static void createMaterial(const String &name,
|
||||||
|
|
||||||
// Takes a name and adds a unique part to it. This is just used to
|
// Takes a name and adds a unique part to it. This is just used to
|
||||||
// make sure that all materials are given unique names.
|
// make sure that all materials are given unique names.
|
||||||
static String getUniqueName(const String &input)
|
String NIFLoader::getUniqueName(const String &input)
|
||||||
{
|
{
|
||||||
static int addon = 0;
|
static int addon = 0;
|
||||||
static char buf[8];
|
static char buf[8];
|
||||||
|
@ -270,7 +306,7 @@ static String getUniqueName(const String &input)
|
||||||
// does not, change the string IN PLACE to say .dds instead and try
|
// does not, change the string IN PLACE to say .dds instead and try
|
||||||
// that. The texture may still not exist, but no information of value
|
// that. The texture may still not exist, but no information of value
|
||||||
// is lost in that case.
|
// is lost in that case.
|
||||||
static void findRealTexture(String &texName)
|
void NIFLoader::findRealTexture(String &texName)
|
||||||
{
|
{
|
||||||
assert(vfs);
|
assert(vfs);
|
||||||
if (vfs->isFile(texName)) return;
|
if (vfs->isFile(texName)) return;
|
||||||
|
@ -286,7 +322,7 @@ static void findRealTexture(String &texName)
|
||||||
|
|
||||||
// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given
|
// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given
|
||||||
// mesh.
|
// mesh.
|
||||||
static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material)
|
void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material)
|
||||||
{
|
{
|
||||||
NiTriShapeData *data = shape->data.getPtr();
|
NiTriShapeData *data = shape->data.getPtr();
|
||||||
SubMesh *sub = mesh->createSubMesh(shape->name.toString());
|
SubMesh *sub = mesh->createSubMesh(shape->name.toString());
|
||||||
|
@ -301,13 +337,16 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
||||||
sub->vertexData = new VertexData();
|
sub->vertexData = new VertexData();
|
||||||
sub->vertexData->vertexCount = numVerts;
|
sub->vertexData->vertexCount = numVerts;
|
||||||
sub->useSharedVertices = false;
|
sub->useSharedVertices = false;
|
||||||
|
|
||||||
VertexDeclaration *decl = sub->vertexData->vertexDeclaration;
|
VertexDeclaration *decl = sub->vertexData->vertexDeclaration;
|
||||||
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION);
|
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION);
|
||||||
|
|
||||||
HardwareVertexBufferSharedPtr vbuf =
|
HardwareVertexBufferSharedPtr vbuf =
|
||||||
HardwareBufferManager::getSingleton().createVertexBuffer(
|
HardwareBufferManager::getSingleton().createVertexBuffer(
|
||||||
VertexElement::getTypeSize(VET_FLOAT3),
|
VertexElement::getTypeSize(VET_FLOAT3),
|
||||||
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
|
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
|
||||||
vbuf->writeData(0, vbuf->getSizeInBytes(), data->vertices.ptr, true);
|
vbuf->writeData(0, vbuf->getSizeInBytes(), data->vertices.ptr, true);
|
||||||
|
|
||||||
VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding;
|
VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding;
|
||||||
bind->setBinding(nextBuf++, vbuf);
|
bind->setBinding(nextBuf++, vbuf);
|
||||||
|
|
||||||
|
@ -371,22 +410,6 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
||||||
|
|
||||||
// Set material if one was given
|
// Set material if one was given
|
||||||
if (!material.empty()) sub->setMaterialName(material);
|
if (!material.empty()) sub->setMaterialName(material);
|
||||||
|
|
||||||
/* Old commented D code. Might be useful when reimplementing
|
|
||||||
animation.
|
|
||||||
// Assign this submesh to the given bone
|
|
||||||
VertexBoneAssignment v;
|
|
||||||
v.boneIndex = ((Bone*)bone)->getHandle();
|
|
||||||
v.weight = 1.0;
|
|
||||||
|
|
||||||
std::cerr << "+ Assigning bone index " << v.boneIndex << "\n";
|
|
||||||
|
|
||||||
for(int i=0; i < numVerts; i++)
|
|
||||||
{
|
|
||||||
v.vertexIndex = i;
|
|
||||||
sub->addBoneAssignment(v);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper math functions. Reinventing linear algebra for the win!
|
// Helper math functions. Reinventing linear algebra for the win!
|
||||||
|
@ -432,7 +455,7 @@ static void vectorMul(const Matrix &A, float *C)
|
||||||
C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2];
|
C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds)
|
void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bounds)
|
||||||
{
|
{
|
||||||
assert(shape != NULL);
|
assert(shape != NULL);
|
||||||
|
|
||||||
|
@ -451,7 +474,10 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
||||||
// 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.
|
// anything.
|
||||||
if (flags & 0x800)
|
if (flags & 0x800)
|
||||||
{ collide = false; bbcollide = false; }
|
{
|
||||||
|
collide = false;
|
||||||
|
bbcollide = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!collide && !bbcollide && hidden)
|
if (!collide && !bbcollide && hidden)
|
||||||
// This mesh apparently isn't being used for anything, so don't
|
// This mesh apparently isn't being used for anything, so don't
|
||||||
|
@ -569,7 +595,80 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
||||||
float *ptr = (float*)data->vertices.ptr;
|
float *ptr = (float*)data->vertices.ptr;
|
||||||
float *optr = ptr;
|
float *optr = ptr;
|
||||||
|
|
||||||
// Rotate, scale and translate all the vertices
|
//use niskindata for the position of vertices.
|
||||||
|
if (!shape->skin.empty())
|
||||||
|
{
|
||||||
|
// vector that stores if the position if a vertex is absolute
|
||||||
|
std::vector<bool> vertexPosAbsolut(numVerts,false);
|
||||||
|
|
||||||
|
|
||||||
|
float *ptrNormals = (float*)data->normals.ptr;
|
||||||
|
//the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex]
|
||||||
|
//the first one contains a link to the bone, the second vertex transformation
|
||||||
|
//relative to the bone
|
||||||
|
int boneIndex = 0;
|
||||||
|
Bone *bonePtr;
|
||||||
|
Vector3 vecPos;
|
||||||
|
Quaternion vecRot;
|
||||||
|
|
||||||
|
std::vector<NiSkinData::BoneInfo> boneList = shape->skin->data->bones;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Iterate through the boneList which contains what vertices are linked to
|
||||||
|
the bone (it->weights array) and at what position (it->trafo)
|
||||||
|
That position is added to every vertex.
|
||||||
|
*/
|
||||||
|
for (std::vector<NiSkinData::BoneInfo>::iterator it = boneList.begin();
|
||||||
|
it != boneList.end(); it++)
|
||||||
|
{
|
||||||
|
//get the bone from bones array of skindata
|
||||||
|
bonePtr = skel->getBone(shape->skin->bones[boneIndex].name.toString());
|
||||||
|
|
||||||
|
// final_vector = old_vector + old_rotation*new_vector*old_scale
|
||||||
|
vecPos = bonePtr->_getDerivedPosition() +
|
||||||
|
bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans);
|
||||||
|
|
||||||
|
vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation);
|
||||||
|
|
||||||
|
for (unsigned int i=0; i<it->weights.length; i++)
|
||||||
|
{
|
||||||
|
unsigned int verIndex = (it->weights.ptr + i)->vertex;
|
||||||
|
|
||||||
|
//Check if the vertex is relativ, FIXME: Is there a better solution?
|
||||||
|
if (vertexPosAbsolut[verIndex] == false)
|
||||||
|
{
|
||||||
|
//apply transformation to the vertices
|
||||||
|
Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3);
|
||||||
|
|
||||||
|
//convert it back to float *
|
||||||
|
for (int j=0; j<3; j++)
|
||||||
|
(ptr + verIndex*3)[j] = absVertPos[j];
|
||||||
|
|
||||||
|
//apply rotation to the normals (not every vertex has a normal)
|
||||||
|
//FIXME: I guessed that vertex[i] = normal[i], is that true?
|
||||||
|
if (verIndex < data->normals.length)
|
||||||
|
{
|
||||||
|
Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3);
|
||||||
|
|
||||||
|
for (int j=0; j<3; j++)
|
||||||
|
(ptrNormals + verIndex*3)[j] = absNormalsPos[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: create vbas, and give them to createOgreSubMesh
|
||||||
|
|
||||||
|
vertexPosAbsolut[verIndex] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boneIndex++;
|
||||||
|
//it->trafo (pos, rot, scale) of the vertex
|
||||||
|
//it->weights array containt the vertices linked to the bone and the weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Rotate, scale and translate all the vertices,
|
||||||
const Matrix &rot = shape->trafo->rotation;
|
const Matrix &rot = shape->trafo->rotation;
|
||||||
const Vector &pos = shape->trafo->pos;
|
const Vector &pos = shape->trafo->pos;
|
||||||
float scale = shape->trafo->scale;
|
float scale = shape->trafo->scale;
|
||||||
|
@ -589,6 +688,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
||||||
ptr += 3;
|
ptr += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!hidden)
|
if (!hidden)
|
||||||
{
|
{
|
||||||
|
@ -596,12 +696,12 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
||||||
bounds.add(optr, numVerts);
|
bounds.add(optr, numVerts);
|
||||||
|
|
||||||
// Create the submesh
|
// Create the submesh
|
||||||
createOgreMesh(mesh, shape, material);
|
createOgreSubMesh(shape, material);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
void NIFLoader::handleNode(Nif::Node *node, int flags,
|
||||||
const Transformation *trafo, BoundsFinder &bounds)
|
const Transformation *trafo, BoundsFinder &bounds, Bone *parentBone)
|
||||||
{
|
{
|
||||||
// Accumulate the flags from all the child nodes. This works for all
|
// Accumulate the flags from all the child nodes. This works for all
|
||||||
// the flags we currently use, at least.
|
// the flags we currently use, at least.
|
||||||
|
@ -632,6 +732,29 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bone *bone = 0;
|
||||||
|
|
||||||
|
// create skeleton or add bones
|
||||||
|
if (node->recType == RC_NiNode)
|
||||||
|
{
|
||||||
|
if (node->name == "Bip01") //root node, create a skeleton
|
||||||
|
{
|
||||||
|
skel = SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skel.isNull()) //if there is a skeleton
|
||||||
|
{
|
||||||
|
bone = skel->createBone(node->name.toString());
|
||||||
|
|
||||||
|
if (parentBone)
|
||||||
|
parentBone->addChild(bone);
|
||||||
|
|
||||||
|
bone->setInheritOrientation(true);
|
||||||
|
bone->setPosition(convertVector3(node->trafo->pos));
|
||||||
|
bone->setOrientation(convertRotation(node->trafo->rotation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the parent transformation to this node. We overwrite the
|
// Apply the parent transformation to this node. We overwrite the
|
||||||
// existing data with the final transformation.
|
// existing data with the final transformation.
|
||||||
if (trafo)
|
if (trafo)
|
||||||
|
@ -661,27 +784,31 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
||||||
for (int i=0; i<n; i++)
|
for (int i=0; i<n; i++)
|
||||||
{
|
{
|
||||||
if (list.has(i))
|
if (list.has(i))
|
||||||
handleNode(mesh, &list[i], flags, node->trafo, bounds);
|
handleNode(&list[i], flags, node->trafo, bounds, bone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node->recType == RC_NiTriShape)
|
else if (node->recType == RC_NiTriShape)
|
||||||
// For shapes
|
// For shapes
|
||||||
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
|
handleNiTriShape(dynamic_cast<NiTriShape*>(node), flags, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NIFLoader::loadResource(Resource *resource)
|
void NIFLoader::loadResource(Resource *resource)
|
||||||
{
|
{
|
||||||
|
resourceName = "";
|
||||||
|
mesh = 0;
|
||||||
|
skel.setNull();
|
||||||
|
|
||||||
// Set up the VFS if it hasn't been done already
|
// Set up the VFS if it hasn't been done already
|
||||||
if(!vfs) vfs = new OgreVFS("General");
|
if (!vfs) vfs = new OgreVFS(resourceGroup);
|
||||||
|
|
||||||
// Get the mesh
|
// Get the mesh
|
||||||
Mesh *mesh = dynamic_cast<Mesh*>(resource);
|
mesh = dynamic_cast<Mesh*>(resource);
|
||||||
assert(mesh);
|
assert(mesh);
|
||||||
|
|
||||||
// Look it up
|
// Look it up
|
||||||
const String &name = mesh->getName();
|
resourceName = mesh->getName();
|
||||||
errName = name; // Set name for error messages
|
|
||||||
if(!vfs->isFile(name))
|
if (!vfs->isFile(resourceName))
|
||||||
{
|
{
|
||||||
warn("File not found.");
|
warn("File not found.");
|
||||||
return;
|
return;
|
||||||
|
@ -694,7 +821,7 @@ void NIFLoader::loadResource(Resource *resource)
|
||||||
// of the early stages of development. Right now we WANT to catch
|
// of the early stages of development. Right now we WANT to catch
|
||||||
// every error as early and intrusively as possible, as it's most
|
// every error as early and intrusively as possible, as it's most
|
||||||
// likely a sign of incomplete code rather than faulty input.
|
// likely a sign of incomplete code rather than faulty input.
|
||||||
NIFFile nif(vfs->open(name), name);
|
NIFFile nif(vfs->open(resourceName), resourceName);
|
||||||
|
|
||||||
if (nif.numRecords() < 1)
|
if (nif.numRecords() < 1)
|
||||||
{
|
{
|
||||||
|
@ -716,7 +843,11 @@ void NIFLoader::loadResource(Resource *resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the node
|
// Handle the node
|
||||||
handleNode(mesh, node, 0, NULL, bounds);
|
handleNode(node, 0, NULL, bounds, 0);
|
||||||
|
|
||||||
|
//set skeleton
|
||||||
|
// if (!skel.isNull())
|
||||||
|
// mesh->setSkeletonName(getSkeletonName());
|
||||||
|
|
||||||
// Finally, set the bounding value.
|
// Finally, set the bounding value.
|
||||||
if (bounds.isValid())
|
if (bounds.isValid())
|
||||||
|
@ -738,7 +869,7 @@ MeshPtr NIFLoader::load(const std::string &name,
|
||||||
return MeshPtr(ptr);
|
return MeshPtr(ptr);
|
||||||
|
|
||||||
// Nope, create a new one.
|
// Nope, create a new one.
|
||||||
return MeshManager::getSingleton().createManual(name, group, &g_sing);
|
return MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* More code currently not in use, from the old D source. This was
|
/* More code currently not in use, from the old D source. This was
|
||||||
|
@ -779,70 +910,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height,
|
||||||
pixelBuffer->unlock();
|
pixelBuffer->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need this later for animated meshes.
|
|
||||||
extern "C" void* ogre_setupSkeleton(char* name)
|
|
||||||
{
|
|
||||||
SkeletonPtr skel = SkeletonManager::getSingleton().create(
|
|
||||||
name, "Closet", true);
|
|
||||||
|
|
||||||
skel->load();
|
|
||||||
|
|
||||||
// Create all bones at the origin and unrotated. This is necessary
|
|
||||||
// since our submeshes each have their own model space. We must
|
|
||||||
// move the bones after creating an entity, then copy this entity.
|
|
||||||
return (void*)skel->createBone();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index)
|
|
||||||
{
|
|
||||||
return (void*) ( ((Bone*)rootBone)->createChild(index) );
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/* This was the D part:
|
|
||||||
|
|
||||||
// Create a skeleton and get the root bone (index 0)
|
|
||||||
BonePtr bone = ogre_setupSkeleton(name);
|
|
||||||
|
|
||||||
// Reset the bone index. The next bone to be created has index 1.
|
|
||||||
boneIndex = 1;
|
|
||||||
// Create a mesh and assign the skeleton to it
|
|
||||||
MeshPtr mesh = ogre_setupMesh(name);
|
|
||||||
|
|
||||||
// Loop through the nodes, creating submeshes, materials and
|
|
||||||
// skeleton bones in the process.
|
|
||||||
handleNode(node, bone, mesh);
|
|
||||||
|
|
||||||
// Create the "template" entity
|
|
||||||
EntityPtr entity = ogre_createEntity(name);
|
|
||||||
|
|
||||||
// Loop through once again, this time to set the right
|
|
||||||
// transformations on the entity's SkeletonInstance. The order of
|
|
||||||
// children will be the same, allowing us to reference bones using
|
|
||||||
// their boneIndex.
|
|
||||||
int lastBone = boneIndex;
|
|
||||||
boneIndex = 1;
|
|
||||||
transformBones(node, entity);
|
|
||||||
if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match");
|
|
||||||
|
|
||||||
if(!hasBBox)
|
|
||||||
ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
void handleNode(Node node, BonePtr root, MeshPtr mesh)
|
|
||||||
{
|
|
||||||
// Insert a new bone for this node
|
|
||||||
BonePtr bone = ogre_insertBone(node.name, root, boneIndex++);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void transformBones(Node node, EntityPtr entity)
|
|
||||||
{
|
|
||||||
ogre_transformBone(entity, &node.trafo, boneIndex++);
|
|
||||||
|
|
||||||
NiNode n = cast(NiNode)node;
|
|
||||||
if(n !is null)
|
|
||||||
foreach(Node nd; n.children)
|
|
||||||
transformBones(nd, entity);
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,6 +27,27 @@
|
||||||
#include <OgreResource.h>
|
#include <OgreResource.h>
|
||||||
#include <OgreMesh.h>
|
#include <OgreMesh.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
class BoundsFinder;
|
||||||
|
|
||||||
|
namespace Nif
|
||||||
|
{
|
||||||
|
class Node;
|
||||||
|
class Transformation;
|
||||||
|
class NiTriShape;
|
||||||
|
class Vector;
|
||||||
|
class Matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Mangle
|
||||||
|
{
|
||||||
|
namespace VFS
|
||||||
|
{
|
||||||
|
class OgreVFS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Manual resource loader for NIF meshes. This is the main class
|
/** Manual resource loader for NIF meshes. This is the main class
|
||||||
responsible for translating the internal NIF mesh structure into
|
responsible for translating the internal NIF mesh structure into
|
||||||
|
@ -43,12 +64,66 @@
|
||||||
very resource intensive, and can safely be done for a large number
|
very resource intensive, and can safely be done for a large number
|
||||||
of meshes at load time.
|
of meshes at load time.
|
||||||
*/
|
*/
|
||||||
struct NIFLoader : Ogre::ManualResourceLoader
|
class NIFLoader : Ogre::ManualResourceLoader
|
||||||
{
|
{
|
||||||
void loadResource(Ogre::Resource *resource);
|
public:
|
||||||
|
static NIFLoader& getSingleton();
|
||||||
|
static NIFLoader* getSingletonPtr();
|
||||||
|
|
||||||
|
virtual void loadResource(Ogre::Resource *resource);
|
||||||
|
|
||||||
static Ogre::MeshPtr load(const std::string &name,
|
static Ogre::MeshPtr load(const std::string &name,
|
||||||
const std::string &group="General");
|
const std::string &group="General");
|
||||||
|
|
||||||
|
Ogre::Vector3 convertVector3(const Nif::Vector& vec);
|
||||||
|
Ogre::Quaternion convertRotation(const Nif::Matrix& rot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NIFLoader() : resourceGroup("General") {}
|
||||||
|
NIFLoader(NIFLoader& n) {}
|
||||||
|
|
||||||
|
void warn(std::string msg);
|
||||||
|
void fail(std::string msg);
|
||||||
|
|
||||||
|
void handleNode( Nif::Node *node, int flags,
|
||||||
|
const Nif::Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone);
|
||||||
|
|
||||||
|
void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds);
|
||||||
|
|
||||||
|
void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material);
|
||||||
|
|
||||||
|
void createMaterial(const Ogre::String &name,
|
||||||
|
const Nif::Vector &ambient,
|
||||||
|
const Nif::Vector &diffuse,
|
||||||
|
const Nif::Vector &specular,
|
||||||
|
const Nif::Vector &emissive,
|
||||||
|
float glossiness, float alpha,
|
||||||
|
float alphaFlags, float alphaTest,
|
||||||
|
const Ogre::String &texName);
|
||||||
|
|
||||||
|
void findRealTexture(Ogre::String &texName);
|
||||||
|
|
||||||
|
Ogre::String getUniqueName(const Ogre::String &input);
|
||||||
|
|
||||||
|
//returns the skeleton name of this mesh
|
||||||
|
std::string getSkeletonName()
|
||||||
|
{
|
||||||
|
return resourceName + ".skel";
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the interface to the Ogre resource system. It allows us to
|
||||||
|
// load NIFs from BSAs, in the file system and in any other place we
|
||||||
|
// tell Ogre to look (eg. in zip or rar files.) It's also used to
|
||||||
|
// check for the existence of texture files, so we can exchange the
|
||||||
|
// extension from .tga to .dds if the texture is missing.
|
||||||
|
Mangle::VFS::OgreVFS *vfs;
|
||||||
|
|
||||||
|
std::string resourceName;
|
||||||
|
std::string resourceGroup;
|
||||||
|
|
||||||
|
// pointer to the ogre mesh which is currently build
|
||||||
|
Ogre::Mesh *mesh;
|
||||||
|
Ogre::SkeletonPtr skel;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue