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