1 /** @file clientmaterial.cpp Client-side material.
2 *
3 * @authors Copyright © 2009-2015 Daniel Swanson <danij@dengine.net>
4 * @authors Copyright © 2016-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "resource/clientmaterial.h"
22
23 #include <QFlag>
24 #include <QtAlgorithms>
25 #include <de/Log>
26 #include <doomsday/console/cmd.h>
27 #include <doomsday/res/Textures>
28 #include <doomsday/world/Materials>
29 #include <doomsday/world/MaterialManifest>
30
31 #include "dd_main.h"
32 #include "MaterialAnimator"
33
34 using namespace de;
35
DENG2_PIMPL_NOREF(ClientMaterial::Decoration)36 DENG2_PIMPL_NOREF(ClientMaterial::Decoration)
37 {
38 ClientMaterial *material = nullptr; ///< Owning Material.
39 Vector2i patternSkip; ///< Pattern skip intervals.
40 Vector2i patternOffset; ///< Pattern skip interval offsets.
41 };
42
Decoration(Vector2i const & patternSkip,Vector2i const & patternOffset)43 ClientMaterial::Decoration::Decoration(Vector2i const &patternSkip, Vector2i const &patternOffset)
44 : d(new Impl)
45 {
46 d->patternSkip = patternSkip;
47 d->patternOffset = patternOffset;
48 }
49
~Decoration()50 ClientMaterial::Decoration::~Decoration()
51 {
52 qDeleteAll(_stages);
53 }
54
material()55 ClientMaterial &ClientMaterial::Decoration::material()
56 {
57 DENG2_ASSERT(d->material);
58 return *d->material;
59 }
60
material() const61 ClientMaterial const &ClientMaterial::Decoration::material() const
62 {
63 DENG2_ASSERT(d->material);
64 return *d->material;
65 }
66
setMaterial(ClientMaterial * newOwner)67 void ClientMaterial::Decoration::setMaterial(ClientMaterial *newOwner)
68 {
69 d->material = newOwner;
70 }
71
patternSkip() const72 Vector2i const &ClientMaterial::Decoration::patternSkip() const
73 {
74 return d->patternSkip;
75 }
76
patternOffset() const77 Vector2i const &ClientMaterial::Decoration::patternOffset() const
78 {
79 return d->patternOffset;
80 }
81
stageCount() const82 int ClientMaterial::Decoration::stageCount() const
83 {
84 return _stages.count();
85 }
86
stage(int index) const87 ClientMaterial::Decoration::Stage &ClientMaterial::Decoration::stage(int index) const
88 {
89 if (!stageCount())
90 {
91 /// @throw MissingStageError No stages are defined.
92 throw MissingStageError("ClientMaterial::Decoration::stage", "Decoration has no stages");
93 }
94 return *_stages[de::wrap(index, 0, _stages.count())];
95 }
96
describe() const97 String ClientMaterial::Decoration::describe() const
98 {
99 return "abstract Decoration";
100 }
101
description() const102 String ClientMaterial::Decoration::description() const
103 {
104 int const numStages = stageCount();
105 String str = _E(b) + describe() + _E(.) + " (" + String::number(numStages) + " stage" + DENG2_PLURAL_S(numStages) + "):";
106 for (int i = 0; i < numStages; ++i)
107 {
108 str += String("\n [%1] ").arg(i, 2) + _E(>) + stage(i).description() + _E(<);
109 }
110 return str;
111 }
112
DENG2_PIMPL(ClientMaterial)113 DENG2_PIMPL(ClientMaterial)
114 {
115 AudioEnvironmentId audioEnvironment { AE_NONE };
116
117 /// Decorations (owned), to be projected into the world (relative to a Surface).
118 QVector<Decoration *> decorations;
119
120 /// Set of draw-context animators (owned).
121 QVector<MaterialAnimator *> animators;
122
123 Impl(Public *i) : Base(i) {}
124
125 ~Impl()
126 {
127 self().clearAllAnimators();
128 self().clearAllDecorations();
129 }
130
131 MaterialAnimator *findAnimator(MaterialVariantSpec const &spec, bool canCreate = false)
132 {
133 for (auto iter = animators.constBegin(); iter != animators.constEnd(); ++iter)
134 {
135 if ((*iter)->variantSpec().compare(spec))
136 {
137 return const_cast<MaterialAnimator *>(*iter); // This will do fine.
138 }
139 }
140
141 if (!canCreate) return nullptr;
142
143 animators.append(new MaterialAnimator(self(), spec));
144 return animators.back();
145 }
146 };
147
ClientMaterial(world::MaterialManifest & manifest)148 ClientMaterial::ClientMaterial(world::MaterialManifest &manifest)
149 : world::Material(manifest)
150 , d(new Impl(this))
151 {}
152
~ClientMaterial()153 ClientMaterial::~ClientMaterial()
154 {}
155
isAnimated() const156 bool ClientMaterial::isAnimated() const
157 {
158 if (Material::isAnimated())
159 {
160 return true;
161 }
162 for (const auto &decor : d->decorations)
163 {
164 if (decor->isAnimated())
165 {
166 return true;
167 }
168 }
169 return false;
170 }
171
audioEnvironment() const172 AudioEnvironmentId ClientMaterial::audioEnvironment() const
173 {
174 return (isDrawable()? d->audioEnvironment : AE_NONE);
175 }
176
setAudioEnvironment(AudioEnvironmentId newEnvironment)177 void ClientMaterial::setAudioEnvironment(AudioEnvironmentId newEnvironment)
178 {
179 d->audioEnvironment = newEnvironment;
180 }
181
decorationCount() const182 int ClientMaterial::decorationCount() const
183 {
184 return d->decorations.count();
185 }
186
forAllDecorations(const std::function<LoopResult (Decoration &)> & func) const187 LoopResult ClientMaterial::forAllDecorations(const std::function<LoopResult (Decoration &)> &func) const
188 {
189 for (Decoration *decor : d.getConst()->decorations)
190 {
191 if (auto result = func(*decor)) return result;
192 }
193 return LoopContinue;
194 }
195
196 /// @todo Update client side MaterialAnimators?
addDecoration(Decoration * decor)197 void ClientMaterial::addDecoration(Decoration *decor)
198 {
199 if (!decor || d->decorations.contains(decor)) return;
200
201 decor->setMaterial(this);
202 d->decorations.append(decor);
203 }
204
clearAllDecorations()205 void ClientMaterial::clearAllDecorations()
206 {
207 qDeleteAll(d->decorations); d->decorations.clear();
208
209 // Animators refer to decorations.
210 clearAllAnimators();
211 }
212
animatorCount() const213 int ClientMaterial::animatorCount() const
214 {
215 return d.getConst()->animators.count();
216 }
217
animator(int index) const218 MaterialAnimator &ClientMaterial::animator(int index) const
219 {
220 return *d.getConst()->animators[index];
221 }
222
hasAnimator(MaterialVariantSpec const & spec)223 bool ClientMaterial::hasAnimator(MaterialVariantSpec const &spec)
224 {
225 return d->findAnimator(spec) != nullptr;
226 }
227
getAnimator(MaterialVariantSpec const & spec)228 MaterialAnimator &ClientMaterial::getAnimator(MaterialVariantSpec const &spec)
229 {
230 return *d->findAnimator(spec, true/*create*/);
231 }
232
forAllAnimators(const std::function<LoopResult (MaterialAnimator &)> & func) const233 LoopResult ClientMaterial::forAllAnimators(const std::function<LoopResult (MaterialAnimator &)> &func) const
234 {
235 for (MaterialAnimator *animator : d.getConst()->animators)
236 {
237 if (auto result = func(*animator)) return result;
238 }
239 return LoopContinue;
240 }
241
clearAllAnimators()242 void ClientMaterial::clearAllAnimators()
243 {
244 qDeleteAll(d->animators);
245 d->animators.clear();
246 }
247
find(de::Uri const & uri)248 ClientMaterial &ClientMaterial::find(de::Uri const &uri) // static
249 {
250 return world::Materials::get().material(uri).as<ClientMaterial>();
251 }
252
description() const253 String ClientMaterial::description() const
254 {
255 String str = world::Material::description();
256
257 str += _E(b) " x" + String::number(animatorCount()) + _E(.)
258 + _E(l) " EnvClass: \"" _E(.) + (audioEnvironment() == AE_NONE? "N/A" : S_AudioEnvironmentName(audioEnvironment())) + "\"";
259
260 // Add the decoration config:
261 for (Decoration const *decor : d->decorations)
262 {
263 str += "\n" + decor->description();
264 }
265
266 return str;
267 }
268