1 /** @file playerweaponanimator.cpp Player weapon animator.
2 *
3 * @authors Copyright (c) 2015-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 *
5 * @par License
6 * GPL: http://www.gnu.org/licenses/gpl.html
7 *
8 * <small>This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. This program is distributed in the hope that it
12 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14 * Public License for more details. You should have received a copy of the GNU
15 * General Public License along with this program; if not, see:
16 * http://www.gnu.org/licenses</small>
17 */
18
19 #include "render/playerweaponanimator.h"
20 #include "render/stateanimator.h"
21 #include "render/vissprite.h"
22 #include "clientplayer.h"
23 #include "clientapp.h"
24 #include "client/cl_def.h"
25 #include "def_main.h"
26 #include "render/r_main.h"
27 #include "render/rendersystem.h"
28
29 #include <de/timer.h>
30 #include <de/Garbage>
31 #include <de/AnimationVector>
32
33 using namespace de;
34
35 namespace render {
36
DENG2_PIMPL_NOREF(PlayerWeaponAnimator)37 DENG2_PIMPL_NOREF(PlayerWeaponAnimator)
38 , DENG2_OBSERVES(Asset, Deletion)
39 {
40 ClientPlayer *player;
41 String identifier;
42 std::unique_ptr<StateAnimator> animator;
43 AnimationVector2 angleOffset { Animation::Linear };
44
45 Impl(ClientPlayer *plr)
46 : player(plr)
47 {}
48
49 void setupAsset(String const &identifier)
50 {
51 this->identifier = identifier;
52 angleOffset = Vector2f();
53
54 if (animator)
55 {
56 animator->model().audienceForDeletion() -= this;
57 }
58
59 // Is there a model for the weapon?
60 if (modelBank().has(identifier))
61 {
62 // Prepare the animation state of the model.
63 auto &model = modelBank().model<Model>(identifier);
64 model.audienceForDeletion() += this;
65 animator.reset(new StateAnimator(identifier, model));
66 animator->setOwnerNamespace(player->info(), QStringLiteral("__player__"));
67 }
68 else
69 {
70 animator.reset();
71 this->identifier.clear();
72 }
73 }
74
75 void assetBeingDeleted(Asset &)
76 {
77 de::trash(animator.release());
78 }
79
80 static ModelBank &modelBank()
81 {
82 return ClientApp::renderSystem().modelRenderer().bank();
83 }
84 };
85
PlayerWeaponAnimator(ClientPlayer * plr)86 PlayerWeaponAnimator::PlayerWeaponAnimator(ClientPlayer *plr)
87 : d(new Impl(plr))
88 {}
89
setAsset(String const & identifier)90 void PlayerWeaponAnimator::setAsset(String const &identifier)
91 {
92 d->setupAsset(identifier);
93 }
94
assetId() const95 String PlayerWeaponAnimator::assetId() const
96 {
97 return d->identifier;
98 }
99
stateChanged(state_s const * state)100 void PlayerWeaponAnimator::stateChanged(state_s const *state)
101 {
102 if (d->animator)
103 {
104 d->animator->triggerByState(Def_GetStateName(state));
105 }
106 }
107
animator()108 StateAnimator &PlayerWeaponAnimator::animator()
109 {
110 DENG2_ASSERT(hasModel());
111 return *d->animator;
112 }
113
setupVisPSprite(vispsprite_t & spr) const114 void PlayerWeaponAnimator::setupVisPSprite(vispsprite_t &spr) const
115 {
116 DENG2_ASSERT(hasModel());
117
118 spr.type = VPSPR_MODEL2;
119 spr.data.model2.model = model();
120 spr.data.model2.animator = d->animator.get();
121
122 // Use the plain bob values.
123 float bob[2] = { *(float *) gx.GetPointer(DD_PSPRITE_BOB_X),
124 *(float *) gx.GetPointer(DD_PSPRITE_BOB_Y) };
125
126 Vector2f angles(
127 /* yaw: */ bob[0] * weaponOffsetScale,
128 /* pitch: */ (32 - bob[1]) * weaponOffsetScale * weaponOffsetScaleY / 1000.0f);
129
130 TimeSpan const span = 1.0 / Timer_TicksPerSecond();
131 d->angleOffset.setValueIfDifferentTarget(angles, span);
132
133 spr.data.model2.yawAngleOffset = d->angleOffset.x;
134 spr.data.model2.pitchAngleOffset = d->angleOffset.y;
135 }
136
advanceTime(TimeSpan const & elapsed)137 void PlayerWeaponAnimator::advanceTime(TimeSpan const &elapsed)
138 {
139 if (clientPaused) return;
140
141 if (d->animator)
142 {
143 d->animator->advanceTime(elapsed);
144 }
145 }
146
hasModel() const147 bool PlayerWeaponAnimator::hasModel() const
148 {
149 return bool(d->animator);
150 }
151
model() const152 Model const *PlayerWeaponAnimator::model() const
153 {
154 if (!hasModel()) return nullptr;
155 return &d->animator->model();
156 }
157
158 } // namespace render
159