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