1 #include "creatureanimation.hpp"
2 
3 #include <osg/MatrixTransform>
4 
5 #include <components/esm/loadcrea.hpp>
6 #include <components/debug/debuglog.hpp>
7 #include <components/resource/resourcesystem.hpp>
8 #include <components/resource/scenemanager.hpp>
9 #include <components/sceneutil/attach.hpp>
10 #include <components/sceneutil/visitor.hpp>
11 #include <components/sceneutil/positionattitudetransform.hpp>
12 #include <components/sceneutil/skeleton.hpp>
13 #include <components/settings/settings.hpp>
14 #include <components/misc/stringops.hpp>
15 
16 #include "../mwbase/environment.hpp"
17 #include "../mwbase/world.hpp"
18 
19 #include "../mwmechanics/weapontype.hpp"
20 
21 #include "../mwworld/class.hpp"
22 #include "../mwworld/esmstore.hpp"
23 
24 namespace MWRender
25 {
26 
CreatureAnimation(const MWWorld::Ptr & ptr,const std::string & model,Resource::ResourceSystem * resourceSystem)27 CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr,
28                                      const std::string& model, Resource::ResourceSystem* resourceSystem)
29   : ActorAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)
30 {
31     MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
32 
33     if(!model.empty())
34     {
35         setObjectRoot(model, false, false, true);
36 
37         if((ref->mBase->mFlags&ESM::Creature::Bipedal))
38             addAnimSource(Settings::Manager::getString("xbaseanim", "Models"), model);
39         addAnimSource(model, model);
40     }
41 }
42 
43 
CreatureWeaponAnimation(const MWWorld::Ptr & ptr,const std::string & model,Resource::ResourceSystem * resourceSystem)44 CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem)
45     : ActorAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)
46     , mShowWeapons(false)
47     , mShowCarriedLeft(false)
48 {
49     MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
50 
51     if(!model.empty())
52     {
53         setObjectRoot(model, true, false, true);
54 
55         if((ref->mBase->mFlags&ESM::Creature::Bipedal))
56         {
57             addAnimSource(Settings::Manager::getString("xbaseanim", "Models"), model);
58         }
59         addAnimSource(model, model);
60 
61         mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);
62 
63         updateParts();
64     }
65 
66     mWeaponAnimationTime = std::shared_ptr<WeaponAnimationTime>(new WeaponAnimationTime(this));
67 }
68 
showWeapons(bool showWeapon)69 void CreatureWeaponAnimation::showWeapons(bool showWeapon)
70 {
71     if (showWeapon != mShowWeapons)
72     {
73         mShowWeapons = showWeapon;
74         updateParts();
75     }
76 }
77 
showCarriedLeft(bool show)78 void CreatureWeaponAnimation::showCarriedLeft(bool show)
79 {
80     if (show != mShowCarriedLeft)
81     {
82         mShowCarriedLeft = show;
83         updateParts();
84     }
85 }
86 
updateParts()87 void CreatureWeaponAnimation::updateParts()
88 {
89     mAmmunition.reset();
90     mWeapon.reset();
91     mShield.reset();
92 
93     updateHolsteredWeapon(!mShowWeapons);
94     updateQuiver();
95     updateHolsteredShield(mShowCarriedLeft);
96 
97     if (mShowWeapons)
98         updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
99     if (mShowCarriedLeft)
100         updatePart(mShield, MWWorld::InventoryStore::Slot_CarriedLeft);
101 }
102 
updatePart(PartHolderPtr & scene,int slot)103 void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
104 {
105     if (!mObjectRoot)
106         return;
107 
108     const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
109     MWWorld::ConstContainerStoreIterator it = inv.getSlot(slot);
110 
111     if (it == inv.end())
112     {
113         scene.reset();
114         return;
115     }
116     MWWorld::ConstPtr item = *it;
117 
118     std::string bonename;
119     std::string itemModel = item.getClass().getModel(item);
120     if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
121     {
122         if(item.getTypeName() == typeid(ESM::Weapon).name())
123         {
124             int type = item.get<ESM::Weapon>()->mBase->mData.mType;
125             bonename = MWMechanics::getWeaponType(type)->mAttachBone;
126             if (bonename != "Weapon Bone")
127             {
128                 const NodeMap& nodeMap = getNodeMap();
129                 NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));
130                 if (found == nodeMap.end())
131                     bonename = "Weapon Bone";
132             }
133         }
134         else
135             bonename = "Weapon Bone";
136     }
137     else
138     {
139         bonename = "Shield Bone";
140         if (item.getTypeName() == typeid(ESM::Armor).name())
141         {
142             // Shield body part model should be used if possible.
143             const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
144             for (const auto& part : item.get<ESM::Armor>()->mBase->mParts.mParts)
145             {
146                 // Assume all creatures use the male mesh.
147                 if (part.mPart != ESM::PRT_Shield || part.mMale.empty())
148                     continue;
149                 const ESM::BodyPart *bodypart = store.get<ESM::BodyPart>().search(part.mMale);
150                 if (bodypart && bodypart->mData.mType == ESM::BodyPart::MT_Armor && !bodypart->mModel.empty())
151                 {
152                     itemModel = "meshes\\" + bodypart->mModel;
153                     break;
154                 }
155             }
156         }
157     }
158 
159     try
160     {
161         osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(itemModel);
162 
163         const NodeMap& nodeMap = getNodeMap();
164         NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));
165         if (found == nodeMap.end())
166             throw std::runtime_error("Can't find attachment node " + bonename);
167         osg::ref_ptr<osg::Node> attached = SceneUtil::attach(node, mObjectRoot, bonename, found->second.get());
168 
169         scene.reset(new PartHolder(attached));
170 
171         if (!item.getClass().getEnchantment(item).empty())
172             mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, item.getClass().getEnchantmentColor(item));
173 
174         // Crossbows start out with a bolt attached
175         // FIXME: code duplicated from NpcAnimation
176         if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&
177                 item.getTypeName() == typeid(ESM::Weapon).name() &&
178                 item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
179         {
180             const ESM::WeaponType* weaponInfo = MWMechanics::getWeaponType(ESM::Weapon::MarksmanCrossbow);
181             MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
182             if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponInfo->mAmmoType)
183                 attachArrow();
184             else
185                 mAmmunition.reset();
186         }
187         else
188             mAmmunition.reset();
189 
190         std::shared_ptr<SceneUtil::ControllerSource> source;
191 
192         if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
193             source = mWeaponAnimationTime;
194         else
195             source.reset(new NullAnimationTime);
196 
197         SceneUtil::AssignControllerSourcesVisitor assignVisitor(source);
198         attached->accept(assignVisitor);
199     }
200     catch (std::exception& e)
201     {
202         Log(Debug::Error) << "Can not add creature part: " << e.what();
203     }
204 }
205 
isArrowAttached() const206 bool CreatureWeaponAnimation::isArrowAttached() const
207 {
208     return mAmmunition != nullptr;
209 }
210 
detachArrow()211 void CreatureWeaponAnimation::detachArrow()
212 {
213     WeaponAnimation::detachArrow(mPtr);
214     updateQuiver();
215 }
216 
attachArrow()217 void CreatureWeaponAnimation::attachArrow()
218 {
219     WeaponAnimation::attachArrow(mPtr);
220 
221     const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
222     MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
223     if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty())
224     {
225         osg::Group* bone = getArrowBone();
226         if (bone != nullptr && bone->getNumChildren())
227             SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));
228     }
229 
230     updateQuiver();
231 }
232 
releaseArrow(float attackStrength)233 void CreatureWeaponAnimation::releaseArrow(float attackStrength)
234 {
235     WeaponAnimation::releaseArrow(mPtr, attackStrength);
236     updateQuiver();
237 }
238 
getArrowBone()239 osg::Group *CreatureWeaponAnimation::getArrowBone()
240 {
241     if (!mWeapon)
242         return nullptr;
243 
244     if (!mPtr.getClass().hasInventoryStore(mPtr))
245         return nullptr;
246 
247     const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
248     MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
249     if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())
250         return nullptr;
251 
252     int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
253     int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;
254 
255     // Try to find and attachment bone in actor's skeleton, otherwise fall back to the ArrowBone in weapon's mesh
256     osg::Group* bone = getBoneByName(MWMechanics::getWeaponType(ammoType)->mAttachBone);
257     if (bone == nullptr)
258     {
259         SceneUtil::FindByNameVisitor findVisitor ("ArrowBone");
260         mWeapon->getNode()->accept(findVisitor);
261         bone = findVisitor.mFoundNode;
262     }
263     return bone;
264 }
265 
getWeaponNode()266 osg::Node *CreatureWeaponAnimation::getWeaponNode()
267 {
268     return mWeapon ? mWeapon->getNode().get() : nullptr;
269 }
270 
getResourceSystem()271 Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem()
272 {
273     return mResourceSystem;
274 }
275 
addControllers()276 void CreatureWeaponAnimation::addControllers()
277 {
278     Animation::addControllers();
279     WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
280 }
281 
runAnimation(float duration)282 osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration)
283 {
284     osg::Vec3f ret = Animation::runAnimation(duration);
285 
286     WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians());
287 
288     return ret;
289 }
290 
291 }
292