1 /** @file finaleanimwidget.cpp  InFine animation system, FinaleAnimWidget.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2014 Daniel Swanson <danij@dengine.net>
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 <de/vector1.h>
22 #include <doomsday/res/Textures>
23 #include "ui/infine/finaleanimwidget.h"
24 
25 #include "dd_main.h"   // App_Resources()
26 #include "api_sound.h"
27 
28 #ifdef __CLIENT__
29 #  include "gl/gl_main.h"
30 #  include "gl/gl_texmanager.h" // GL_PrepareRawTexture()
31 #  include "render/r_draw.h"    // Rend_PatchTextureSpec()
32 #  include "render/rend_main.h" // filterUI
33 #  include "MaterialAnimator"
34 #  include <de/GLInfo>
35 #endif
36 
37 using namespace de;
38 
Frame()39 FinaleAnimWidget::Frame::Frame()
40     : tics (0)
41     , type (PFT_MATERIAL)
42     , sound(0)
43 {
44     de::zap(flags);
45     de::zap(texRef);
46 }
47 
~Frame()48 FinaleAnimWidget::Frame::~Frame()
49 {
50 #ifdef __CLIENT__
51     if (type == PFT_XIMAGE)
52     {
53         DGL_DeleteTextures(1, (DGLuint *)&texRef.tex);
54     }
55 #endif
56 }
57 
DENG2_PIMPL_NOREF(FinaleAnimWidget)58 DENG2_PIMPL_NOREF(FinaleAnimWidget)
59 {
60     bool animComplete = true;
61     bool animLooping  = false;  ///< @c true= loop back to the start when the end is reached.
62     int tics          = 0;
63     int curFrame      = 0;
64     Frames frames;
65 
66     animatorvector4_t color;
67 
68     /// For rectangle-objects.
69     animatorvector4_t otherColor;
70     animatorvector4_t edgeColor;
71     animatorvector4_t otherEdgeColor;
72 
73     Impl()
74     {
75         AnimatorVector4_Init(color,          1, 1, 1, 1);
76         AnimatorVector4_Init(otherColor,     0, 0, 0, 0);
77         AnimatorVector4_Init(edgeColor,      0, 0, 0, 0);
78         AnimatorVector4_Init(otherEdgeColor, 0, 0, 0, 0);
79     }
80 
81     static Frame *makeFrame(Frame::Type type, int tics, void *texRef, short sound, bool flagFlipH)
82     {
83         Frame *f = new Frame;
84         f->flags.flip = flagFlipH;
85         f->type  = type;
86         f->tics  = tics;
87         f->sound = sound;
88 
89         switch (f->type)
90         {
91         case Frame::PFT_MATERIAL:  f->texRef.material = ((world::Material *)texRef); break;
92         case Frame::PFT_PATCH:     f->texRef.patch    = *((patchid_t *)texRef); break;
93         case Frame::PFT_RAW:       f->texRef.lumpNum  = *((lumpnum_t *)texRef); break;
94         case Frame::PFT_XIMAGE:    f->texRef.tex      = *((DGLuint *)texRef);   break;
95 
96         default: throw Error("FinaleAnimWidget::makeFrame", "Unknown frame type #" + String::number(type));
97         }
98 
99         return f;
100     }
101 };
102 
FinaleAnimWidget(String const & name)103 FinaleAnimWidget::FinaleAnimWidget(String const &name)
104     : FinaleWidget(name)
105     , d(new Impl)
106 {}
107 
~FinaleAnimWidget()108 FinaleAnimWidget::~FinaleAnimWidget()
109 {
110     clearAllFrames();
111 }
112 
animationComplete() const113 bool FinaleAnimWidget::animationComplete() const
114 {
115     return d->animComplete;
116 }
117 
setLooping(bool yes)118 FinaleAnimWidget &FinaleAnimWidget::setLooping(bool yes)
119 {
120     d->animLooping = yes;
121     return *this;
122 }
123 
124 #ifdef __CLIENT__
useColor(animator_t const * color,int components)125 static void useColor(animator_t const *color, int components)
126 {
127     if (components == 3)
128     {
129         DGL_Color3f(color[0].value, color[1].value, color[2].value);
130     }
131     else if (components == 4)
132     {
133         DGL_Color4f(color[0].value, color[1].value, color[2].value, color[3].value);
134     }
135 }
136 
buildGeometry(float const[3],dd_bool flipTextureS,Vector4f const & bottomColor,Vector4f const & topColor,Vector3f ** posCoords,Vector4f ** colorCoords,Vector2f ** texCoords)137 static int buildGeometry(float const /*dimensions*/[3], dd_bool flipTextureS,
138     Vector4f const &bottomColor, Vector4f const &topColor, Vector3f **posCoords,
139     Vector4f **colorCoords, Vector2f **texCoords)
140 {
141     static Vector3f posCoordBuf[4];
142     static Vector4f colorCoordBuf[4];
143     static Vector2f texCoordBuf[4];
144 
145     // 0 - 1
146     // | / |  Vertex layout
147     // 2 - 3
148 
149     posCoordBuf[0] = Vector3f(0, 0, 0);
150     posCoordBuf[1] = Vector3f(1, 0, 0);
151     posCoordBuf[2] = Vector3f(0, 1, 0);
152     posCoordBuf[3] = Vector3f(1, 1, 0);
153 
154     texCoordBuf[0] = Vector2f((flipTextureS? 1:0), 0);
155     texCoordBuf[1] = Vector2f((flipTextureS? 0:1), 0);
156     texCoordBuf[2] = Vector2f((flipTextureS? 1:0), 1);
157     texCoordBuf[3] = Vector2f((flipTextureS? 0:1), 1);
158 
159     colorCoordBuf[0] = bottomColor;
160     colorCoordBuf[1] = bottomColor;
161     colorCoordBuf[2] = topColor;
162     colorCoordBuf[3] = topColor;
163 
164     *posCoords   = posCoordBuf;
165     *texCoords   = texCoordBuf;
166     *colorCoords = colorCoordBuf;
167 
168     return 4;
169 }
170 
drawGeometry(int numVerts,Vector3f const * posCoords,Vector4f const * colorCoords,Vector2f const * texCoords)171 static void drawGeometry(int numVerts, Vector3f const *posCoords,
172     Vector4f const *colorCoords, Vector2f const *texCoords)
173 {
174     DGL_Begin(DGL_TRIANGLE_STRIP);
175     Vector3f const *posIt   = posCoords;
176     Vector4f const *colorIt = colorCoords;
177     Vector2f const *texIt   = texCoords;
178     for (int i = 0; i < numVerts; ++i, posIt++, colorIt++, texIt++)
179     {
180         if (texCoords)
181             DGL_TexCoord2f(0, texIt->x, texIt->y);
182 
183         if (colorCoords)
184             DGL_Color4f(colorIt->x, colorIt->y, colorIt->z, colorIt->w);
185 
186         DGL_Vertex3f(posIt->x, posIt->y, posIt->z);
187     }
188     DGL_End();
189 }
190 
uiMaterialSpec_FinaleAnim()191 static inline MaterialVariantSpec const &uiMaterialSpec_FinaleAnim()
192 {
193     return App_Resources().materialSpec(UiContext, 0, 0, 0, 0,
194                                              GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE,
195                                              0, -3, 0, false, false, false, false);
196 }
197 
drawPicFrame(FinaleAnimWidget * p,uint frame,float const _origin[3],float scale[3],float const rgba[4],float const rgba2[4],float angle,Vector3f const & worldOffset)198 static void drawPicFrame(FinaleAnimWidget *p, uint frame, float const _origin[3],
199     float /*const*/ scale[3], float const rgba[4], float const rgba2[4], float angle,
200     Vector3f const &worldOffset)
201 {
202     vec3f_t offset = { 0, 0, 0 }, dimensions, origin, originOffset, center;
203     vec2f_t texScale = { 1, 1 };
204     vec2f_t rotateCenter = { .5f, .5f };
205     dd_bool showEdges = true, flipTextureS = false;
206     dd_bool mustPopTextureMatrix = false;
207     dd_bool textureEnabled = false;
208     int numVerts;
209     Vector3f *posCoords;
210     Vector4f *colorCoords;
211     Vector2f *texCoords;
212 
213     if (p->frameCount())
214     {
215         /// @todo Optimize: Texture/Material searches should be NOT be done here -ds
216         FinaleAnimWidget::Frame *f = p->allFrames().at(frame);
217 
218         flipTextureS = (f->flags.flip != 0);
219         showEdges = false;
220 
221         switch (f->type)
222         {
223         case FinaleAnimWidget::Frame::PFT_RAW: {
224             rawtex_t *rawTex = ClientResources::get().declareRawTexture(f->texRef.lumpNum);
225             if (rawTex)
226             {
227                 DGLuint glName = GL_PrepareRawTexture(*rawTex);
228                 V3f_Set(offset, 0, 0, 0);
229                 // Raw images are always considered to have a logical size of 320x200
230                 // even though the actual texture resolution may be different.
231                 V3f_Set(dimensions, 320 /*rawTex->width*/, 200 /*rawTex->height*/, 0);
232                 // Rotation occurs around the center of the screen.
233                 V2f_Set(rotateCenter, 160, 100);
234                 GL_BindTextureUnmanaged(glName, gl::ClampToEdge, gl::ClampToEdge,
235                                         (filterUI ? gl::Linear : gl::Nearest));
236                 if (glName)
237                 {
238                     DGL_Enable(DGL_TEXTURE_2D);
239                     textureEnabled = true;
240                 }
241             }
242             break; }
243 
244         case FinaleAnimWidget::Frame::PFT_XIMAGE:
245             V3f_Set(offset, 0, 0, 0);
246             V3f_Set(dimensions, 1, 1, 0);
247             V2f_Set(rotateCenter, .5f, .5f);
248             GL_BindTextureUnmanaged(f->texRef.tex, gl::ClampToEdge, gl::ClampToEdge,
249                                     (filterUI ? gl::Linear : gl::Nearest));
250             if (f->texRef.tex)
251             {
252                 DGL_Enable(DGL_TEXTURE_2D);
253                 textureEnabled = true;
254             }
255             break;
256 
257         case FinaleAnimWidget::Frame::PFT_MATERIAL:
258             if (ClientMaterial *mat = static_cast<ClientMaterial *>(f->texRef.material))
259             {
260                 /// @todo Utilize *all* properties of the Material.
261                 MaterialAnimator &matAnimator      = mat->getAnimator(uiMaterialSpec_FinaleAnim());
262 
263                 // Ensure we've up to date info about the material.
264                 matAnimator.prepare();
265 
266                 Vector2ui const &matDimensions = matAnimator.dimensions();
267                 TextureVariant *tex            = matAnimator.texUnit(MaterialAnimator::TU_LAYER0).texture;
268                 int const texBorder            = tex->spec().variant.border;
269 
270                 GL_BindTexture(tex);
271                 DGL_Enable(DGL_TEXTURE_2D);
272                 textureEnabled = true;
273 
274                 V3f_Set(dimensions, matDimensions.x + texBorder * 2, matDimensions.y + texBorder * 2, 0);
275                 V2f_Set(rotateCenter, dimensions[VX] / 2, dimensions[VY] / 2);
276                 tex->glCoords(&texScale[VX], &texScale[VY]);
277 
278                 // Apply a sprite-texture origin offset?
279                 bool const texIsSprite = !tex->base().manifest().scheme().name().compareWithoutCase("Sprites");
280                 if (texIsSprite)
281                 {
282                     V3f_Set(offset, tex->base().origin().x, tex->base().origin().y, 0);
283                 }
284                 else
285                 {
286                     V3f_Set(offset, 0, 0, 0);
287                 }
288             }
289             break;
290 
291         case FinaleAnimWidget::Frame::PFT_PATCH: {
292             res::TextureManifest &manifest = res::Textures::get().textureScheme("Patches")
293                                             .findByUniqueId(f->texRef.patch);
294             if (manifest.hasTexture())
295             {
296                 res::Texture &tex = manifest.texture();
297                 TextureVariantSpec const &texSpec =
298                     Rend_PatchTextureSpec(0 | (tex.isFlagged(res::Texture::Monochrome)        ? TSF_MONOCHROME : 0)
299                                             | (tex.isFlagged(res::Texture::UpscaleAndSharpen) ? TSF_UPSCALE_AND_SHARPEN : 0));
300                 GL_BindTexture(static_cast<ClientTexture &>(tex).prepareVariant(texSpec));
301                 DGL_Enable(DGL_TEXTURE_2D);
302                 textureEnabled = true;
303 
304                 V3f_Set(offset, tex.origin().x, tex.origin().y, 0);
305                 V3f_Set(dimensions, tex.width(), tex.height(), 0);
306                 V2f_Set(rotateCenter, dimensions[VX]/2, dimensions[VY]/2);
307             }
308             break; }
309 
310         default:
311             App_Error("drawPicFrame: Invalid FI_PIC frame type %i.", int(f->type));
312         }
313     }
314 
315     // If we've not chosen a texture by now set some defaults.
316     /// @todo This is some seriously funky logic... refactor or remove.
317     if (!textureEnabled)
318     {
319         V3f_Copy(dimensions, scale);
320         V3f_Set(scale, 1, 1, 1);
321         V2f_Set(rotateCenter, dimensions[VX] / 2, dimensions[VY] / 2);
322     }
323 
324     V3f_Set(center, dimensions[VX] / 2, dimensions[VY] / 2, dimensions[VZ] / 2);
325 
326     V3f_Sum(origin, _origin, center);
327     V3f_Subtract(origin, origin, offset);
328     for (int i = 0; i < 3; ++i) { origin[i] += worldOffset[i]; }
329 
330     V3f_Subtract(originOffset, offset, center);
331     offset[VX] *= scale[VX]; offset[VY] *= scale[VY]; offset[VZ] *= scale[VZ];
332     V3f_Sum(originOffset, originOffset, offset);
333 
334     numVerts = buildGeometry(dimensions, flipTextureS, rgba, rgba2, &posCoords, &colorCoords, &texCoords);
335 
336     // Setup the transformation.
337     DGL_MatrixMode(DGL_MODELVIEW);
338     DGL_PushMatrix();
339     //glScalef(.1f/SCREENWIDTH, .1f/SCREENWIDTH, 1);
340 
341     // Move to the object origin.
342     DGL_Translatef(origin[VX], origin[VY], origin[VZ]);
343 
344     // Translate to the object center.
345     /// @todo Remove this; just go to origin directly. Rotation origin is
346     /// now separately in 'rotateCenter'. -jk
347     DGL_Translatef(originOffset[VX], originOffset[VY], originOffset[VZ]);
348 
349     DGL_Scalef(scale[VX], scale[VY], scale[VZ]);
350 
351     if (angle != 0)
352     {
353         DGL_Translatef(rotateCenter[VX], rotateCenter[VY], 0);
354 
355         // With rotation we must counter the VGA aspect ratio.
356         DGL_Scalef(1, 200.0f / 240.0f, 1);
357         DGL_Rotatef(angle, 0, 0, 1);
358         DGL_Scalef(1, 240.0f / 200.0f, 1);
359 
360         DGL_Translatef(-rotateCenter[VX], -rotateCenter[VY], 0);
361     }
362 
363     DGL_MatrixMode(DGL_MODELVIEW);
364     // Scale up our unit-geometry to the desired dimensions.
365     DGL_Scalef(dimensions[VX], dimensions[VY], dimensions[VZ]);
366 
367     if (texScale[0] != 1 || texScale[1] != 1)
368     {
369         DGL_MatrixMode(DGL_TEXTURE);
370         DGL_PushMatrix();
371         DGL_Scalef(texScale[0], texScale[1], 1);
372         mustPopTextureMatrix = true;
373     }
374 
375     drawGeometry(numVerts, posCoords, colorCoords, texCoords);
376 
377     GL_SetNoTexture();
378 
379     if (mustPopTextureMatrix)
380     {
381         DGL_MatrixMode(DGL_TEXTURE);
382         DGL_PopMatrix();
383     }
384 
385     if (showEdges)
386     {
387         DGL_Begin(DGL_LINES);
388             useColor(p->edgeColor(), 4);
389             DGL_Vertex2f(0, 0);
390             DGL_Vertex2f(1, 0);
391             DGL_Vertex2f(1, 0);
392 
393             useColor(p->otherEdgeColor(), 4);
394             DGL_Vertex2f(1, 1);
395             DGL_Vertex2f(1, 1);
396             DGL_Vertex2f(0, 1);
397             DGL_Vertex2f(0, 1);
398 
399             useColor(p->edgeColor(), 4);
400             DGL_Vertex2f(0, 0);
401         DGL_End();
402     }
403 
404     // Restore original transformation.
405     DGL_MatrixMode(DGL_MODELVIEW);
406     DGL_PopMatrix();
407 }
408 
draw(Vector3f const & offset)409 void FinaleAnimWidget::draw(Vector3f const &offset)
410 {
411     // Fully transparent pics will not be drawn.
412     if (!(d->color[3].value > 0)) return;
413 
414     vec3f_t _scale, _origin;
415     V3f_Set(_origin, origin()[VX].value, origin()[VY].value, origin()[VZ].value);
416     V3f_Set(_scale, scale()[VX].value, scale()[VY].value, scale()[VZ].value);
417 
418     vec4f_t rgba, rgba2;
419     V4f_Set(rgba, d->color[0].value, d->color[1].value, d->color[2].value, d->color[3].value);
420     if (!frameCount())
421     {
422         V4f_Set(rgba2, d->otherColor[0].value, d->otherColor[1].value, d->otherColor[2].value, d->otherColor[3].value);
423     }
424     else
425     {
426         V4f_Set(rgba2, 0, 0, 0, 0);
427     }
428     drawPicFrame(this, d->curFrame, _origin, _scale, rgba, (!frameCount()? rgba2 : rgba), angle().value, offset);
429 }
430 #endif
431 
runTicks()432 void FinaleAnimWidget::runTicks(/*timespan_t timeDelta*/)
433 {
434     FinaleWidget::runTicks(/*timeDelta*/);
435 
436     AnimatorVector4_Think(d->color);
437     AnimatorVector4_Think(d->otherColor);
438     AnimatorVector4_Think(d->edgeColor);
439     AnimatorVector4_Think(d->otherEdgeColor);
440 
441     if (!(d->frames.count() > 1)) return;
442 
443     // If animating, decrease the sequence timer.
444     if (d->frames.at(d->curFrame)->tics > 0)
445     {
446         if (--d->tics <= 0)
447         {
448             Frame *f;
449             // Advance the sequence position. k = next pos.
450             uint next = d->curFrame + 1;
451 
452             if (next == uint(d->frames.count()))
453             {
454                 // This is the end.
455                 d->animComplete = true;
456 
457                 // Stop the sequence?
458                 if (d->animLooping)
459                 {
460                     next = 0; // Rewind back to beginning.
461                 }
462                 else // Yes.
463                 {
464                     d->frames.at(next = d->curFrame)->tics = 0;
465                 }
466             }
467 
468             // Advance to the next pos.
469             f = d->frames.at(d->curFrame = next);
470             d->tics = f->tics;
471 
472             // Play a sound?
473             if (f->sound > 0)
474                 S_LocalSound(f->sound, 0);
475         }
476     }
477 }
478 
newFrame(Frame::Type type,int tics,void * texRef,short sound,bool flagFlipH)479 int FinaleAnimWidget::newFrame(Frame::Type type, int tics, void *texRef, short sound, bool flagFlipH)
480 {
481     d->frames.append(d->makeFrame(type, tics, texRef, sound, flagFlipH));
482     // The addition of a new frame means the animation has not yet completed.
483     d->animComplete = false;
484     return d->frames.count();
485 }
486 
allFrames() const487 FinaleAnimWidget::Frames const &FinaleAnimWidget::allFrames() const
488 {
489     return d->frames;
490 }
491 
clearAllFrames()492 FinaleAnimWidget &FinaleAnimWidget::clearAllFrames()
493 {
494     qDeleteAll(d->frames); d->frames.clear();
495     d->curFrame     = 0;
496     d->animComplete = true;  // Nothing to animate.
497     d->animLooping  = false; // Yeah?
498     return *this;
499 }
500 
resetAllColors()501 FinaleAnimWidget &FinaleAnimWidget::resetAllColors()
502 {
503     // Default colors.
504     AnimatorVector4_Init(d->color,      1, 1, 1, 1);
505     AnimatorVector4_Init(d->otherColor, 1, 1, 1, 1);
506 
507     // Edge alpha is zero by default.
508     AnimatorVector4_Init(d->edgeColor,      1, 1, 1, 0);
509     AnimatorVector4_Init(d->otherEdgeColor, 1, 1, 1, 0);
510     return *this;
511 }
512 
color() const513 animator_t const *FinaleAnimWidget::color() const
514 {
515     return d->color;
516 }
517 
setColor(Vector3f const & newColor,int steps)518 FinaleAnimWidget &FinaleAnimWidget::setColor(Vector3f const &newColor, int steps)
519 {
520     AnimatorVector3_Set(d->color, newColor.x, newColor.y, newColor.z, steps);
521     return *this;
522 }
523 
setAlpha(float newAlpha,int steps)524 FinaleAnimWidget &FinaleAnimWidget::setAlpha(float newAlpha, int steps)
525 {
526     Animator_Set(&d->color[3], newAlpha, steps);
527     return *this;
528 }
529 
setColorAndAlpha(Vector4f const & newColorAndAlpha,int steps)530 FinaleAnimWidget &FinaleAnimWidget::setColorAndAlpha(Vector4f const &newColorAndAlpha, int steps)
531 {
532     AnimatorVector4_Set(d->color, newColorAndAlpha.x, newColorAndAlpha.y, newColorAndAlpha.z, newColorAndAlpha.w, steps);
533     return *this;
534 }
535 
edgeColor() const536 animator_t const *FinaleAnimWidget::edgeColor() const
537 {
538     return d->edgeColor;
539 }
540 
setEdgeColor(Vector3f const & newColor,int steps)541 FinaleAnimWidget &FinaleAnimWidget::setEdgeColor(Vector3f const &newColor, int steps)
542 {
543     AnimatorVector3_Set(d->edgeColor, newColor.x, newColor.y, newColor.z, steps);
544     return *this;
545 }
546 
setEdgeAlpha(float newAlpha,int steps)547 FinaleAnimWidget &FinaleAnimWidget::setEdgeAlpha(float newAlpha, int steps)
548 {
549     Animator_Set(&d->edgeColor[3], newAlpha, steps);
550     return *this;
551 }
552 
setEdgeColorAndAlpha(Vector4f const & newColorAndAlpha,int steps)553 FinaleAnimWidget &FinaleAnimWidget::setEdgeColorAndAlpha(Vector4f const &newColorAndAlpha, int steps)
554 {
555     AnimatorVector4_Set(d->edgeColor, newColorAndAlpha.x, newColorAndAlpha.y, newColorAndAlpha.z, newColorAndAlpha.w, steps);
556     return *this;
557 }
558 
otherColor() const559 animator_t const *FinaleAnimWidget::otherColor() const
560 {
561     return d->otherColor;
562 }
563 
setOtherColor(de::Vector3f const & newColor,int steps)564 FinaleAnimWidget &FinaleAnimWidget::setOtherColor(de::Vector3f const &newColor, int steps)
565 {
566     AnimatorVector3_Set(d->otherColor, newColor.x, newColor.y, newColor.z, steps);
567     return *this;
568 }
569 
setOtherAlpha(float newAlpha,int steps)570 FinaleAnimWidget &FinaleAnimWidget::setOtherAlpha(float newAlpha, int steps)
571 {
572     Animator_Set(&d->otherColor[3], newAlpha, steps);
573     return *this;
574 }
575 
setOtherColorAndAlpha(Vector4f const & newColorAndAlpha,int steps)576 FinaleAnimWidget &FinaleAnimWidget::setOtherColorAndAlpha(Vector4f const &newColorAndAlpha, int steps)
577 {
578     AnimatorVector4_Set(d->otherColor, newColorAndAlpha.x, newColorAndAlpha.y, newColorAndAlpha.z, newColorAndAlpha.w, steps);
579     return *this;
580 }
581 
otherEdgeColor() const582 animator_t const *FinaleAnimWidget::otherEdgeColor() const
583 {
584     return d->otherEdgeColor;
585 }
586 
setOtherEdgeColor(de::Vector3f const & newColor,int steps)587 FinaleAnimWidget &FinaleAnimWidget::setOtherEdgeColor(de::Vector3f const &newColor, int steps)
588 {
589     AnimatorVector3_Set(d->otherEdgeColor, newColor.x, newColor.y, newColor.z, steps);
590     return *this;
591 }
592 
setOtherEdgeAlpha(float newAlpha,int steps)593 FinaleAnimWidget &FinaleAnimWidget::setOtherEdgeAlpha(float newAlpha, int steps)
594 {
595     Animator_Set(&d->otherEdgeColor[3], newAlpha, steps);
596     return *this;
597 }
598 
setOtherEdgeColorAndAlpha(Vector4f const & newColorAndAlpha,int steps)599 FinaleAnimWidget &FinaleAnimWidget::setOtherEdgeColorAndAlpha(Vector4f const &newColorAndAlpha, int steps)
600 {
601     AnimatorVector4_Set(d->otherEdgeColor, newColorAndAlpha.x, newColorAndAlpha.y, newColorAndAlpha.z, newColorAndAlpha.w, steps);
602     return *this;
603 }
604