1 /** @file rend_model.cpp  3D Model Rendering (frame models).
2  *
3  * @note Light vectors and triangle normals are in an entirely independent,
4  *       right-handed coordinate system.
5  *
6  * There is some more confusion with Y and Z axes as the game uses Z as the
7  * vertical axis and the rendering code and model definitions use the Y axis.
8  *
9  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
10  * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
11  *
12  * @par License
13  * GPL: http://www.gnu.org/licenses/gpl.html
14  *
15  * <small>This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by the
17  * Free Software Foundation; either version 2 of the License, or (at your
18  * option) any later version. This program is distributed in the hope that it
19  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
20  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
21  * Public License for more details. You should have received a copy of the GNU
22  * General Public License along with this program; if not, write to the Free
23  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA</small>
25  */
26 
27 #include "clientapp.h"
28 #include "dd_def.h"
29 #include "dd_main.h" // App_World()
30 
31 #include "world/p_players.h"
32 #include "world/clientmobjthinkerdata.h"
33 #include "render/rend_model.h"
34 #include "render/rend_main.h"
35 #include "render/rendersystem.h"
36 #include "render/vissprite.h"
37 #include "render/vectorlightdata.h"
38 #include "render/modelrenderer.h"
39 #include "gl/gl_main.h"
40 #include "gl/gl_texmanager.h"
41 #include "MaterialVariantSpec"
42 #include "ClientTexture"
43 #include "ClientMaterial"
44 
45 #include <doomsday/console/var.h>
46 #include <doomsday/world/Materials>
47 #include <de/Log>
48 #include <de/ArrayValue>
49 #include <de/GLInfo>
50 #include <de/binangle.h>
51 #include <de/memory.h>
52 #include <de/concurrency.h>
53 #include <cstdlib>
54 #include <cmath>
55 #include <cstring>
56 
57 using namespace de;
58 
59 #define QATAN2(y,x)         qatan2(y,x)
60 #define QASIN(x)            asin(x) // @todo Precalculate arcsin.
61 
qatan2(float y,float x)62 static inline float qatan2(float y, float x)
63 {
64     float ang = BANG2RAD(bamsAtan2(y * 512, x * 512));
65     if (ang > PI) ang -= 2 * (float) PI;
66     return ang;
67 }
68 
69 enum rendcmd_t
70 {
71     RC_COMMAND_COORDS,
72     RC_OTHER_COORDS,
73     RC_BOTH_COORDS
74 };
75 
76 byte useModels         = true;
77 int modelLight         = 4;
78 int frameInter         = true;
79 float modelAspectMod   = 1 / 1.2f; //.833334f;
80 int mirrorHudModels;
81 //int modelShinyMultitex = true;
82 float modelShinyFactor = 1.0f;
83 float modelSpinSpeed   = 1;
84 int maxModelDistance   = 1500;
85 float rend_model_lod   = 256;
86 byte precacheSkins     = true;
87 
88 static bool inited;
89 
90 struct array_t
91 {
92     bool enabled;
93     void *data;
94 };
95 #define MAX_ARRAYS (2 + MAX_TEX_UNITS)
96 static array_t arrays[MAX_ARRAYS];
97 
98 // The global vertex render buffer.
99 static Vector3f *modelPosCoords;
100 static Vector3f *modelNormCoords;
101 static Vector4ub *modelColorCoords;
102 static Vector2f *modelTexCoords;
103 
104 // Global variables for ease of use. (Egads!)
105 static Vector3f modelCenter;
106 static FrameModelLOD *activeLod;
107 
108 static uint vertexBufferMax; ///< Maximum number of vertices we'll be required to render per submodel.
109 static uint vertexBufferSize; ///< Current number of vertices supported by the render buffer.
110 #ifdef DENG_DEBUG
111 static bool announcedVertexBufferMaxBreach; ///< @c true if an attempt has been made to expand beyond our capability.
112 #endif
113 
114 /*static void modelAspectModChanged()
115 {
116     /// @todo Reload and resize all models.
117 }*/
118 
Rend_ModelRegister()119 void Rend_ModelRegister()
120 {
121     C_VAR_BYTE ("rend-model",                &useModels,            0, 0, 1);
122     C_VAR_INT  ("rend-model-lights",         &modelLight,           0, 0, 10);
123     C_VAR_INT  ("rend-model-inter",          &frameInter,           0, 0, 1);
124     C_VAR_FLOAT("rend-model-aspect",         &modelAspectMod,       CVF_NO_MAX | CVF_NO_MIN, 0, 0);
125     C_VAR_INT  ("rend-model-distance",       &maxModelDistance,     CVF_NO_MAX, 0, 0);
126     C_VAR_BYTE ("rend-model-precache",       &precacheSkins,        0, 0, 1);
127     C_VAR_FLOAT("rend-model-lod",            &rend_model_lod,       CVF_NO_MAX, 0, 0);
128     C_VAR_INT  ("rend-model-mirror-hud",     &mirrorHudModels,      0, 0, 1);
129     C_VAR_FLOAT("rend-model-spin-speed",     &modelSpinSpeed,       CVF_NO_MAX | CVF_NO_MIN, 0, 0);
130     C_VAR_FLOAT("rend-model-shiny-strength", &modelShinyFactor,     0, 0, 10);
131     C_VAR_FLOAT("rend-model-fov",            &weaponFixedFOV,       0, 0, 180);
132 }
133 
Rend_ModelInit()134 void Rend_ModelInit()
135 {
136     if (inited) return; // Already been here.
137 
138     modelPosCoords   = 0;
139     modelNormCoords  = 0;
140     modelColorCoords = 0;
141     modelTexCoords   = 0;
142 
143     vertexBufferMax = vertexBufferSize = 0;
144 #ifdef DENG_DEBUG
145     announcedVertexBufferMaxBreach = false;
146 #endif
147 
148     inited = true;
149 }
150 
Rend_ModelShutdown()151 void Rend_ModelShutdown()
152 {
153     if (!inited) return;
154 
155     M_Free(modelPosCoords); modelPosCoords = 0;
156     M_Free(modelNormCoords); modelNormCoords = 0;
157     M_Free(modelColorCoords); modelColorCoords = 0;
158     M_Free(modelTexCoords); modelTexCoords = 0;
159 
160     vertexBufferMax = vertexBufferSize = 0;
161 #ifdef DENG_DEBUG
162     announcedVertexBufferMaxBreach = false;
163 #endif
164 
165     inited = false;
166 }
167 
Rend_ModelExpandVertexBuffers(uint numVertices)168 bool Rend_ModelExpandVertexBuffers(uint numVertices)
169 {
170     DENG2_ASSERT(inited);
171 
172     LOG_AS("Rend_ModelExpandVertexBuffers");
173 
174     if (numVertices <= vertexBufferMax) return true;
175 
176     // Sanity check a sane maximum...
177     if (numVertices >= RENDER_MAX_MODEL_VERTS)
178     {
179 #ifdef DENG_DEBUG
180         if (!announcedVertexBufferMaxBreach)
181         {
182             LOGDEV_GL_WARNING("Attempted to expand to %u vertices (max %u)")
183                 << numVertices << RENDER_MAX_MODEL_VERTS;
184             announcedVertexBufferMaxBreach = true;
185         }
186 #endif
187         return false;
188     }
189 
190     // Defer resizing of the render buffer until draw time as it may be repeatedly expanded.
191     vertexBufferMax = numVertices;
192     return true;
193 }
194 
195 /// @return  @c true= Vertex buffer is large enough to handle @a numVertices.
resizeVertexBuffer(uint numVertices)196 static bool resizeVertexBuffer(uint numVertices)
197 {
198     // Mark the vertex buffer if a resize is necessary.
199     Rend_ModelExpandVertexBuffers(numVertices);
200 
201     // Do we need to resize the buffers?
202     if (vertexBufferMax != vertexBufferSize)
203     {
204         /// @todo Align access to this memory along a 4-byte boundary?
205         modelPosCoords   =  (Vector3f *) M_Realloc(modelPosCoords,   sizeof(*modelPosCoords)   * vertexBufferMax);
206         modelNormCoords  =  (Vector3f *) M_Realloc(modelNormCoords,  sizeof(*modelNormCoords)  * vertexBufferMax);
207         modelColorCoords = (Vector4ub *) M_Realloc(modelColorCoords, sizeof(*modelColorCoords) * vertexBufferMax);
208         modelTexCoords   =  (Vector2f *) M_Realloc(modelTexCoords,   sizeof(*modelTexCoords)   * vertexBufferMax);
209 
210         vertexBufferSize = vertexBufferMax;
211     }
212 
213     // Is the buffer large enough?
214     return vertexBufferSize >= numVertices;
215 }
216 
disableArrays(int vertices,int colors,int coords)217 static void disableArrays(int vertices, int colors, int coords)
218 {
219     DENG_ASSERT_IN_MAIN_THREAD();
220     DENG_ASSERT_GL_CONTEXT_ACTIVE();
221 
222     if (vertices)
223     {
224         arrays[AR_VERTEX].enabled = false;
225     }
226 
227     if (colors)
228     {
229         arrays[AR_COLOR].enabled = false;
230     }
231 
232     for (int i = 0; i < MAX_TEX_UNITS; ++i)
233     {
234         if (coords & (1 << i))
235         {
236             arrays[AR_TEXCOORD0 + i].enabled = false;
237         }
238     }
239 
240     DENG_ASSERT(!Sys_GLCheckError());
241 }
242 
enableTexUnit(int id)243 static inline void enableTexUnit(int id)
244 {
245     DENG_ASSERT_IN_MAIN_THREAD();
246     DENG_ASSERT_GL_CONTEXT_ACTIVE();
247 
248     DGL_Enable(DGL_TEXTURE0 + id);
249 }
250 
disableTexUnit(int id)251 static inline void disableTexUnit(int id)
252 {
253     DENG_ASSERT_IN_MAIN_THREAD();
254     DENG_ASSERT_GL_CONTEXT_ACTIVE();
255 
256     DGL_Disable(DGL_TEXTURE0 + id);
257 
258     // Implicit disabling of texcoord array.
259     disableArrays(0, 0, 1 << id);
260 }
261 
262 /**
263  * The first selected unit is active after this call.
264  */
selectTexUnits(int count)265 static void selectTexUnits(int count)
266 {
267     if (count < MAX_TEX_UNITS)
268     {
269         for (int i = MAX_TEX_UNITS - 1; i >= count; i--)
270         {
271             disableTexUnit(i);
272         }
273     }
274 
275     // Enable the selected units.
276     for (int i = count - 1; i >= 0; i--)
277     {
278         if (i >= MAX_TEX_UNITS) continue;
279         enableTexUnit(i);
280     }
281 }
282 
283 /**
284  * Enable, set and optionally lock all enabled arrays.
285  */
configureArrays(void * vertices,void * colors,int numCoords=0,void ** coords=0)286 static void configureArrays(void *vertices, void *colors, int numCoords = 0,
287                             void **coords = 0)
288 {
289     DENG_ASSERT_IN_MAIN_THREAD();
290     DENG_ASSERT_GL_CONTEXT_ACTIVE();
291 
292     if (vertices)
293     {
294         arrays[AR_VERTEX].enabled = true;
295         arrays[AR_VERTEX].data = vertices;
296     }
297 
298     if (colors)
299     {
300         arrays[AR_COLOR].enabled = true;
301         arrays[AR_COLOR].data = colors;
302     }
303 
304     for (int i = 0; i < numCoords && i < MAX_TEX_UNITS; ++i)
305     {
306         if (coords[i])
307         {
308             arrays[AR_TEXCOORD0 + i].enabled = true;
309             arrays[AR_TEXCOORD0 + i].data = coords[i];
310         }
311     }
312 
313     DENG_ASSERT(!Sys_GLCheckError());
314 }
315 
drawArrayElement(int index)316 static void drawArrayElement(int index)
317 {
318     DENG_ASSERT_IN_MAIN_THREAD();
319     DENG_ASSERT_GL_CONTEXT_ACTIVE();
320 
321     for (int i = 0; i < MAX_TEX_UNITS; ++i)
322     {
323         if (arrays[AR_TEXCOORD0 + i].enabled)
324         {
325             Vector2f const &texCoord = reinterpret_cast<Vector2f const *>(arrays[AR_TEXCOORD0 + i].data)[index];
326             DGL_TexCoord2fv(byte(i), texCoord.constPtr());
327         }
328     }
329 
330     if (arrays[AR_COLOR].enabled)
331     {
332         Vector4ub const &colorCoord = reinterpret_cast<Vector4ub const *>(arrays[AR_COLOR].data)[index];
333         DGL_Color4ubv(colorCoord.constPtr());
334     }
335 
336     if (arrays[AR_VERTEX].enabled)
337     {
338         Vector3f const &posCoord = reinterpret_cast<Vector3f const *>(arrays[AR_VERTEX].data)[index];
339         DGL_Vertex3fv(posCoord.constPtr());
340     }
341 }
342 
343 /**
344  * Return a pointer to the visible model frame.
345  */
visibleModelFrame(FrameModelDef & modef,int subnumber,int mobjId)346 static FrameModelFrame &visibleModelFrame(FrameModelDef &modef, int subnumber, int mobjId)
347 {
348     if (subnumber >= int(modef.subCount()))
349     {
350         throw Error("Rend_DrawModel.visibleFrame",
351                     QString("Model has %1 submodels, but submodel #%2 was requested")
352                         .arg(modef.subCount()).arg(subnumber));
353     }
354     SubmodelDef const &sub = modef.subModelDef(subnumber);
355 
356     int curFrame = sub.frame;
357     if (modef.flags & MFF_IDFRAME)
358     {
359         curFrame += mobjId % sub.frameRange;
360     }
361 
362     return App_Resources().model(sub.modelId).frame(curFrame);
363 }
364 
365 /**
366  * Render a set of 3D model primitives using the given data.
367  */
drawPrimitives(rendcmd_t mode,FrameModel::Primitives const & primitives,Vector3f * posCoords,Vector4ub * colorCoords,Vector2f * texCoords=0)368 static void drawPrimitives(rendcmd_t mode,
369                            FrameModel::Primitives const &primitives,
370                            Vector3f *posCoords,
371                            Vector4ub *colorCoords,
372                            Vector2f *texCoords = 0)
373 {
374     DENG_ASSERT_IN_MAIN_THREAD();
375     DENG_ASSERT_GL_CONTEXT_ACTIVE();
376 
377     // Disable all vertex arrays.
378     disableArrays(true, true, DDMAXINT);
379 
380     // Load the vertex array.
381     void *coords[2];
382     switch (mode)
383     {
384     case RC_OTHER_COORDS:
385         coords[0] = texCoords;
386         configureArrays(posCoords, colorCoords, 1, coords);
387         break;
388 
389     case RC_BOTH_COORDS:
390         coords[0] = NULL;
391         coords[1] = texCoords;
392         configureArrays(posCoords, colorCoords, 2, coords);
393         break;
394 
395     default:
396         configureArrays(posCoords, colorCoords);
397         break;
398     }
399 
400     FrameModel::Primitive::Element const *firstElem = nullptr;
401     bool joining = false;
402 
403     auto submitElement = [mode, &firstElem] (FrameModel::Primitive::Element const &elem)
404     {
405         if (!firstElem)
406         {
407             firstElem = &elem;
408         }
409         if (mode != RC_OTHER_COORDS)
410         {
411             DGL_TexCoord2fv(0, elem.texCoord.constPtr());
412         }
413         drawArrayElement(elem.index);
414     };
415 
416     int lastLength = 0;
417 
418     // Combine all triangle strips and fans into one big strip. Fans are converted
419     // to strips. When joining strips, winding is retained so that each sub-strip
420     // begins with the same winding.
421 
422     DGL_Begin(DGL_TRIANGLE_STRIP);
423     foreach (FrameModel::Primitive const &prim, primitives)
424     {
425         DGLenum const primType = (prim.triFan? DGL_TRIANGLE_FAN : DGL_TRIANGLE_STRIP);
426 
427         joining = false;
428         if (lastLength > 0)
429         {
430             // Disconnect strip.
431             DGL_Vertex3fv(nullptr);
432             if (lastLength & 1) DGL_Vertex3fv(nullptr); // Retain the winding.
433             joining = true;
434         }
435         firstElem = nullptr;
436 
437         if (primType == DGL_TRIANGLE_STRIP)
438         {
439             lastLength = prim.elements.size();
440             foreach (FrameModel::Primitive::Element const &elem, prim.elements)
441             {
442                 submitElement(elem);
443                 if (joining)
444                 {
445                     DGL_Vertex3fv(nullptr);
446                     joining = false;
447                 }
448             }
449         }
450         else
451         {
452             lastLength = 2; // just make it even, so it doesn't affect winding (see above)
453             for (int i = 1; i < prim.elements.size(); ++i)
454             {
455                 submitElement(prim.elements.at(0));
456                 if (joining)
457                 {
458                     DGL_Vertex3fv(nullptr);
459                     joining = false;
460                 }
461                 submitElement(prim.elements.at(i));
462             }
463         }
464     }
465     DGL_End();
466 }
467 
468 /**
469  * Interpolate linearly between two sets of vertices.
470  */
Mod_LerpVertices(float inter,int count,FrameModelFrame const & from,FrameModelFrame const & to,Vector3f * posOut,Vector3f * normOut)471 static void Mod_LerpVertices(float inter, int count, FrameModelFrame const &from,
472     FrameModelFrame const &to, Vector3f *posOut, Vector3f *normOut)
473 {
474     DENG2_ASSERT(&from.model == &to.model); // sanity check.
475     DENG2_ASSERT(!activeLod || &activeLod->model == &from.model); // sanity check.
476     DENG2_ASSERT(from.vertices.count() == to.vertices.count()); // sanity check.
477 
478     FrameModelFrame::VertexBuf::const_iterator startIt = from.vertices.begin();
479     FrameModelFrame::VertexBuf::const_iterator endIt   = to.vertices.begin();
480 
481     if (&from == &to || de::fequal(inter, 0))
482     {
483         for (int i = 0; i < count; ++i, startIt++, posOut++, normOut++)
484         {
485             if (!activeLod || activeLod->hasVertex(i))
486             {
487                 *posOut  = startIt->pos;
488                 *normOut = startIt->norm;
489             }
490         }
491     }
492     else
493     {
494         for (int i = 0; i < count; ++i, startIt++, endIt++, posOut++, normOut++)
495         {
496             if (!activeLod || activeLod->hasVertex(i))
497             {
498                 *posOut  = de::lerp(startIt->pos,  endIt->pos,  inter);
499                 *normOut = de::lerp(startIt->norm, endIt->norm, inter);
500             }
501         }
502     }
503 }
504 
Mod_MirrorCoords(dint count,Vector3f * coords,dint axis)505 static void Mod_MirrorCoords(dint count, Vector3f *coords, dint axis)
506 {
507     DENG2_ASSERT(coords);
508     for (; count-- > 0; coords++)
509     {
510         (*coords)[axis] = -(*coords)[axis];
511     }
512 }
513 
514 /**
515  * Rotate a VectorLight direction vector from world space to model space.
516  *
517  * @param vlight  Light to process.
518  * @param yaw     Yaw rotation angle.
519  * @param pitch   Pitch rotation angle.
520  * @param invert  @c true= flip light normal (for use with inverted models).
521  *
522  * @todo Construct a rotation matrix once and use it for all lights.
523  */
rotateLightVector(VectorLightData const & vlight,dfloat yaw,dfloat pitch,bool invert=false)524 static Vector3f rotateLightVector(VectorLightData const &vlight, dfloat yaw, dfloat pitch,
525     bool invert = false)
526 {
527     dfloat rotated[3]; vlight.direction.decompose(rotated);
528     M_RotateVector(rotated, yaw, pitch);
529 
530     // Quick hack: Flip light normal if model inverted.
531     if (invert)
532     {
533         rotated[0] = -rotated[0];
534         rotated[1] = -rotated[1];
535     }
536 
537     return Vector3f(rotated);
538 }
539 
540 /**
541  * Calculate vertex lighting.
542  */
Mod_VertexColors(Vector4ub * out,dint count,Vector3f const * normCoords,duint lightListIdx,duint maxLights,Vector4f const & ambient,bool invert,dfloat rotateYaw,dfloat rotatePitch)543 static void Mod_VertexColors(Vector4ub *out, dint count, Vector3f const *normCoords,
544     duint lightListIdx, duint maxLights, Vector4f const &ambient, bool invert,
545     dfloat rotateYaw, dfloat rotatePitch)
546 {
547     Vector4f const saturated(1, 1, 1, 1);
548 
549     for (dint i = 0; i < count; ++i, out++, normCoords++)
550     {
551         if (activeLod && !activeLod->hasVertex(i))
552             continue;
553 
554         Vector3f const &normal = *normCoords;
555 
556         // Accumulate contributions from all affecting lights.
557         dint numProcessed = 0;
558         Vector3f accum[2];  // Begin with total darkness [color, extra].
559         ClientApp::renderSystem().forAllVectorLights(lightListIdx, [&maxLights, &invert, &rotateYaw
560                                                       , &rotatePitch, &normal
561                                                       , &accum, &numProcessed] (VectorLightData const &vlight)
562         {
563             numProcessed += 1;
564 
565             // We must transform the light vector to model space.
566             Vector3f const lightDirection
567                     = rotateLightVector(vlight, rotateYaw, rotatePitch, invert);
568 
569             dfloat strength = lightDirection.dot(normal)
570                             + vlight.offset;  // Shift a bit towards the light.
571 
572             // Ability to both light and shade.
573             if (strength > 0) strength *= vlight.lightSide;
574             else             strength *= vlight.darkSide;
575 
576             accum[vlight.affectedByAmbient? 0 : 1]
577                 += vlight.color * de::clamp(-1.f, strength, 1.f);
578 
579             // Time to stop?
580             return (maxLights && duint(numProcessed) == maxLights);
581         });
582 
583         // Check for ambient and convert to ubyte.
584         Vector4f color(accum[0].max(ambient) + accum[1], ambient[3]);
585 
586         *out = (color.min(saturated) * 255).toVector4ub();
587     }
588 }
589 
590 /**
591  * Set all the colors in the array to bright white.
592  */
Mod_FullBrightVertexColors(dint count,Vector4ub * colorCoords,dfloat alpha)593 static void Mod_FullBrightVertexColors(dint count, Vector4ub *colorCoords, dfloat alpha)
594 {
595     DENG2_ASSERT(colorCoords);
596     for (; count-- > 0; colorCoords++)
597     {
598         *colorCoords = Vector4ub(255, 255, 255, 255 * alpha);
599     }
600 }
601 
602 /**
603  * Set all the colors into the array to the same values.
604  */
Mod_FixedVertexColors(dint count,Vector4ub * colorCoords,Vector4ub const & color)605 static void Mod_FixedVertexColors(dint count, Vector4ub *colorCoords, Vector4ub const &color)
606 {
607     DENG2_ASSERT(colorCoords);
608     for (; count-- > 0; colorCoords++)
609     {
610         *colorCoords = color;
611     }
612 }
613 
614 /**
615  * Calculate cylindrically mapped, shiny texture coordinates.
616  */
Mod_ShinyCoords(Vector2f * out,int count,Vector3f const * normCoords,float normYaw,float normPitch,float shinyAng,float shinyPnt,float reactSpeed)617 static void Mod_ShinyCoords(Vector2f *out, int count, Vector3f const *normCoords,
618     float normYaw, float normPitch, float shinyAng, float shinyPnt, float reactSpeed)
619 {
620     for (int i = 0; i < count; ++i, out++, normCoords++)
621     {
622         if (activeLod && !activeLod->hasVertex(i))
623             continue;
624 
625         float rotatedNormal[3] = { normCoords->x, normCoords->y, normCoords->z };
626 
627         // Rotate the normal vector so that it approximates the
628         // model's orientation compared to the viewer.
629         M_RotateVector(rotatedNormal,
630                        (shinyPnt + normYaw) * 360 * reactSpeed,
631                        (shinyAng + normPitch - .5f) * 180 * reactSpeed);
632 
633         *out = Vector2f(rotatedNormal[0] + 1, rotatedNormal[2]);
634     }
635 }
636 
chooseSelSkin(FrameModelDef & mf,int submodel,int selector)637 static int chooseSelSkin(FrameModelDef &mf, int submodel, int selector)
638 {
639     if (mf.def.hasSub(submodel))
640     {
641         Record &subDef = mf.def.sub(submodel);
642 
643         int i = (selector >> DDMOBJ_SELECTOR_SHIFT) & subDef.geti("selSkinMask");
644         int c = subDef.geti("selSkinShift");
645 
646         if (c > 0) i >>= c;
647         else       i <<= -c;
648 
649         if (i > 7) i = 7; // Maximum number of skins for selskin.
650         if (i < 0) i = 0; // Improbable (impossible?), but doesn't hurt.
651 
652         return subDef.geta("selSkins")[i].asInt();
653     }
654     return 0;
655 }
656 
chooseSkin(FrameModelDef & mf,int submodel,int id,int selector,int tmap)657 static int chooseSkin(FrameModelDef &mf, int submodel, int id, int selector, int tmap)
658 {
659     if (submodel >= int(mf.subCount()))
660     {
661         return 0;
662     }
663 
664     SubmodelDef &smf = mf.subModelDef(submodel);
665     FrameModel &mdl = App_Resources().model(smf.modelId);
666     int skin = smf.skin;
667 
668     // Selskin overrides the skin range.
669     if (smf.testFlag(MFF_SELSKIN))
670     {
671         skin = chooseSelSkin(mf, submodel, selector);
672     }
673 
674     // Is there a skin range for this frame?
675     // (During model setup skintics and skinrange are set to >0.)
676     if (smf.skinRange > 1)
677     {
678         // What rule to use for determining the skin?
679         int offset;
680         if (smf.testFlag(MFF_IDSKIN))
681         {
682             offset = id;
683         }
684         else
685         {
686             offset = SECONDS_TO_TICKS(App_World().time()) / mf.skinTics;
687         }
688 
689         skin += offset % smf.skinRange;
690     }
691 
692     // Need translation?
693     if (smf.testFlag(MFF_SKINTRANS))
694     {
695         skin = tmap;
696     }
697 
698     if (skin < 0 || skin >= mdl.skinCount())
699     {
700         skin = 0;
701     }
702 
703     return skin;
704 }
705 
modelSkinMaterialSpec()706 static inline MaterialVariantSpec const &modelSkinMaterialSpec()
707 {
708     return ClientApp::resources().materialSpec(ModelSkinContext, 0, 0, 0, 0, GL_REPEAT, GL_REPEAT,
709                                  1, -2, -1, true, true, false, false);
710 }
711 
drawSubmodel(uint number,vissprite_t const & spr)712 static void drawSubmodel(uint number, vissprite_t const &spr)
713 {
714     drawmodelparams_t const &parm = *VS_MODEL(&spr);
715     int const zSign = (spr.pose.mirrored? -1 : 1);
716     FrameModelDef *mf = parm.mf, *mfNext = parm.nextMF;
717     SubmodelDef const &smf = mf->subModelDef(number);
718 
719     FrameModel &mdl = App_Resources().model(smf.modelId);
720 
721     // Do not bother with infinitely small models...
722     if (mf->scale == Vector3f(0, 0, 0))
723         return;
724 
725     float alpha = spr.light.ambientColor[CA];
726 
727     // Is the submodel-defined alpha multiplier in effect?
728     // With df_brightshadow2, the alpha multiplier will be applied anyway.
729     if (smf.testFlag(MFF_BRIGHTSHADOW2) ||
730        !(parm.flags & (DDMF_BRIGHTSHADOW|DDMF_SHADOW|DDMF_ALTSHADOW)))
731     {
732         alpha *= smf.alpha / 255.f;
733     }
734 
735     // Would this be visible?
736     if (alpha <= 0) return;
737 
738     blendmode_t blending = smf.blendMode;
739     // Is the submodel-defined blend mode in effect?
740     if (parm.flags & DDMF_BRIGHTSHADOW)
741     {
742         blending = BM_ADD;
743     }
744 
745     int useSkin = chooseSkin(*mf, number, parm.id, parm.selector, parm.tmap);
746 
747     // Scale interpos. Intermark becomes zero and endmark becomes one.
748     // (Full sub-interpolation!) But only do it for the standard
749     // interrange. If a custom one is defined, don't touch interpos.
750     float endPos = 0;
751     float inter = parm.inter;
752     if ((mf->interRange[0] == 0 && mf->interRange[1] == 1) || smf.testFlag(MFF_WORLD_TIME_ANIM))
753     {
754         endPos = (mf->interNext ? mf->interNext->interMark : 1);
755         inter = (parm.inter - mf->interMark) / (endPos - mf->interMark);
756     }
757 
758     FrameModelFrame *frame = &visibleModelFrame(*mf, number, parm.id);
759     FrameModelFrame *nextFrame = 0;
760     // Do we have a sky/particle model here?
761     if (parm.alwaysInterpolate)
762     {
763         // Always interpolate, if there's animation.
764         // Used with sky and particle models.
765         nextFrame = &mdl.frame((smf.frame + 1) % mdl.frameCount());
766         mfNext = mf;
767     }
768     else
769     {
770         // Check for possible interpolation.
771         if (frameInter && mfNext && !smf.testFlag(MFF_DONT_INTERPOLATE))
772         {
773             if (mfNext->hasSub(number) && mfNext->subModelId(number) == smf.modelId)
774             {
775                 nextFrame = &visibleModelFrame(*mfNext, number, parm.id);
776             }
777         }
778     }
779 
780     // Clamp interpolation.
781     inter = de::clamp(0.f, inter, 1.f);
782 
783     if (!nextFrame)
784     {
785         // If not interpolating, use the same frame as interpolation target.
786         // The lerp routines will recognize this special case.
787         nextFrame = frame;
788         mfNext = mf;
789     }
790 
791     // Determine the total number of vertices we have.
792     int numVerts = mdl.vertexCount();
793 
794     // Ensure our vertex render buffers can accommodate this.
795     if (!resizeVertexBuffer(numVerts))
796     {
797         // No can do, we aint got the power!
798         return;
799     }
800 
801     // Setup transformation.
802     DGL_MatrixMode(DGL_MODELVIEW);
803     DGL_PushMatrix();
804 
805     // Model space => World space
806     DGL_Translatef(spr.pose.origin[VX] + spr.pose.srvo[VX] +
807                    de::lerp(mf->offset.x, mfNext->offset.x, inter),
808                    spr.pose.origin[VZ] + spr.pose.srvo[VZ] +
809                    de::lerp(mf->offset.y, mfNext->offset.y, inter),
810                    spr.pose.origin[VY] + spr.pose.srvo[VY] + zSign *
811                    de::lerp(mf->offset.z, mfNext->offset.z, inter));
812 
813     if (spr.pose.extraYawAngle || spr.pose.extraPitchAngle)
814     {
815         // Sky models have an extra rotation.
816         DGL_Scalef(1, 200 / 240.0f, 1);
817         DGL_Rotatef(spr.pose.extraYawAngle, 1, 0, 0);
818         DGL_Rotatef(spr.pose.extraPitchAngle, 0, 0, 1);
819         DGL_Scalef(1, 240 / 200.0f, 1);
820     }
821 
822     // Model rotation.
823     DGL_Rotatef(spr.pose.viewAligned? spr.pose.yawAngleOffset   : spr.pose.yaw,   0, 1, 0);
824     DGL_Rotatef(spr.pose.viewAligned? spr.pose.pitchAngleOffset : spr.pose.pitch, 0, 0, 1);
825 
826     // Scaling and model space offset.
827     DGL_Scalef(de::lerp(mf->scale.x, mfNext->scale.x, inter),
828              de::lerp(mf->scale.y, mfNext->scale.y, inter),
829              de::lerp(mf->scale.z, mfNext->scale.z, inter));
830     if (spr.pose.extraScale)
831     {
832         // Particle models have an extra scale.
833         DGL_Scalef(spr.pose.extraScale, spr.pose.extraScale, spr.pose.extraScale);
834     }
835     DGL_Translatef(smf.offset.x, smf.offset.y, smf.offset.z);
836 
837     // Determine the suitable LOD.
838     if (mdl.lodCount() > 1 && rend_model_lod != 0)
839     {
840         float lodFactor = rend_model_lod * DENG_GAMEVIEW_WIDTH / 640.0f / (Rend_FieldOfView() / 90.0f);
841         if (!de::fequal(lodFactor, 0))
842         {
843             lodFactor = 1 / lodFactor;
844         }
845 
846         // Determine the LOD we will be using.
847         activeLod = &mdl.lod(de::clamp<int>(0, lodFactor * spr.pose.distance, mdl.lodCount() - 1));
848     }
849     else
850     {
851         activeLod = 0;
852     }
853 
854     // Interpolate vertices and normals.
855     Mod_LerpVertices(inter, numVerts, *frame, *nextFrame,
856                      modelPosCoords, modelNormCoords);
857 
858     if (zSign < 0)
859     {
860         Mod_MirrorCoords(numVerts, modelPosCoords, 2);
861         Mod_MirrorCoords(numVerts, modelNormCoords, 1);
862     }
863 
864     // Coordinates to the center of the model (game coords).
865     modelCenter = Vector3f(spr.pose.origin[VX], spr.pose.origin[VY], spr.pose.midZ())
866             + Vector3d(spr.pose.srvo) + Vector3f(mf->offset.x, mf->offset.z, mf->offset.y);
867 
868     // Calculate lighting.
869     Vector4f ambient;
870     if (smf.testFlag(MFF_FULLBRIGHT) && !smf.testFlag(MFF_DIM))
871     {
872         // Submodel-specific lighting override.
873         ambient = Vector4f(1, 1, 1, 1);
874         Mod_FullBrightVertexColors(numVerts, modelColorCoords, alpha);
875     }
876     else if (!spr.light.vLightListIdx)
877     {
878         // Lit uniformly.
879         ambient = Vector4f(spr.light.ambientColor, alpha);
880         Mod_FixedVertexColors(numVerts, modelColorCoords,
881                               (ambient * 255).toVector4ub());
882     }
883     else
884     {
885         // Lit normally.
886         ambient = Vector4f(spr.light.ambientColor, alpha);
887 
888         Mod_VertexColors(modelColorCoords, numVerts,
889                          modelNormCoords, spr.light.vLightListIdx, modelLight + 1,
890                          ambient, (mf->scale[VY] < 0), -spr.pose.yaw, -spr.pose.pitch);
891     }
892 
893     TextureVariant *shinyTexture = 0;
894     float shininess = 0;
895     if (mf->def.hasSub(number))
896     {
897         shininess = float(de::clamp(0.0, mf->def.sub(number).getd("shiny") * modelShinyFactor, 1.0));
898         // Ensure we've prepared the shiny skin.
899         if (shininess > 0)
900         {
901             if (ClientTexture *tex = static_cast<ClientTexture *>(mf->subModelDef(number).shinySkin))
902             {
903                 shinyTexture = tex->prepareVariant(Rend_ModelShinyTextureSpec());
904             }
905             else
906             {
907                 shininess = 0;
908             }
909         }
910     }
911 
912     Vector4f color;
913     if (shininess > 0)
914     {
915         // Calculate shiny coordinates.
916         Vector3f shinyColor = mf->def.sub(number).get("shinyColor");
917 
918         // With psprites, add the view angle/pitch.
919         float offset = parm.shineYawOffset;
920 
921         // Calculate normalized (0,1) model yaw and pitch.
922         float normYaw = M_CycleIntoRange(((spr.pose.viewAligned? spr.pose.yawAngleOffset
923                                                                : spr.pose.yaw) + offset) / 360, 1);
924 
925         offset = parm.shinePitchOffset;
926 
927         float normPitch = M_CycleIntoRange(((spr.pose.viewAligned? spr.pose.pitchAngleOffset
928                                                                  : spr.pose.pitch) + offset) / 360, 1);
929 
930         float shinyAng = 0;
931         float shinyPnt = 0;
932         if (parm.shinepspriteCoordSpace)
933         {
934             // This is a hack to accommodate the psprite coordinate space.
935             shinyPnt = 0.5;
936         }
937         else
938         {
939             Vector3f delta = modelCenter;
940 
941             if (!parm.shineTranslateWithViewerPos)
942             {
943                 delta -= Rend_EyeOrigin().xzy();
944             }
945 
946             shinyAng = QATAN2(delta.z, M_ApproxDistancef(delta.x, delta.y)) / PI + 0.5f; // shinyAng is [0,1]
947 
948             shinyPnt = QATAN2(delta.y, delta.x) / (2 * PI);
949         }
950 
951         Mod_ShinyCoords(modelTexCoords, numVerts,
952                         modelNormCoords, normYaw, normPitch, shinyAng, shinyPnt,
953                         mf->def.sub(number).getf("shinyReact"));
954 
955         // Shiny color.
956         if (smf.testFlag(MFF_SHINY_LIT))
957         {
958             color = Vector4f(ambient * shinyColor, shininess);
959         }
960         else
961         {
962             color = Vector4f(shinyColor, shininess);
963         }
964     }
965 
966     TextureVariant *skinTexture = 0;
967     if (renderTextures == 2)
968     {
969         // For lighting debug, render all surfaces using the gray texture.
970         MaterialAnimator &matAnimator = ClientMaterial::find(de::Uri("System", Path("gray")))
971                 .getAnimator(modelSkinMaterialSpec());
972 
973         // Ensure we've up to date info about the material.
974         matAnimator.prepare();
975 
976         skinTexture = matAnimator.texUnit(MaterialAnimator::TU_LAYER0).texture;
977     }
978     else
979     {
980         skinTexture = 0;
981         if (ClientTexture *tex = static_cast<ClientTexture *>(mdl.skin(useSkin).texture))
982         {
983             skinTexture = tex->prepareVariant(Rend_ModelDiffuseTextureSpec(mdl.flags().testFlag(FrameModel::NoTextureCompression)));
984         }
985     }
986 
987     // If we mirror the model, triangles have a different orientation.
988     if (zSign < 0)
989     {
990         LIBGUI_GL.glFrontFace(GL_CCW);
991     }
992 
993     // Twosided models won't use backface culling.
994     if (smf.testFlag(MFF_TWO_SIDED))
995     {
996         //glDisable(GL_CULL_FACE);
997         DGL_CullFace(DGL_NONE);
998     }
999     DGL_Enable(DGL_TEXTURE_2D);
1000 
1001     FrameModel::Primitives const &primitives =
1002         activeLod? activeLod->primitives : mdl.primitives();
1003 
1004     // Render using multiple passes?
1005     if (shininess <= 0 || alpha < 1 ||
1006         blending != BM_NORMAL || !smf.testFlag(MFF_SHINY_SPECULAR))
1007     {
1008         // The first pass can be skipped if it won't be visible.
1009         if (shininess < 1 || smf.testFlag(MFF_SHINY_SPECULAR))
1010         {
1011             selectTexUnits(1);
1012             GL_BlendMode(blending);
1013             GL_BindTexture(renderTextures? skinTexture : 0);
1014 
1015             drawPrimitives(RC_COMMAND_COORDS, primitives,
1016                            modelPosCoords, modelColorCoords);
1017         }
1018 
1019         if (shininess > 0)
1020         {
1021             DGL_DepthFunc(DGL_LEQUAL);
1022 
1023             // Set blending mode, two choices: reflected and specular.
1024             if (smf.testFlag(MFF_SHINY_SPECULAR))
1025                 GL_BlendMode(BM_ADD);
1026             else
1027                 GL_BlendMode(BM_NORMAL);
1028 
1029             // Shiny color.
1030             Mod_FixedVertexColors(numVerts, modelColorCoords,
1031                                   (color * 255).toVector4ub());
1032 
1033             // We'll use multitexturing to clear out empty spots in
1034             // the primary texture.
1035             selectTexUnits(2);
1036             DGL_ModulateTexture(11);
1037 
1038             DGL_SetInteger(DGL_ACTIVE_TEXTURE, 1);
1039             GL_BindTexture(renderTextures? shinyTexture : 0);
1040 
1041             DGL_SetInteger(DGL_ACTIVE_TEXTURE, 0);
1042             GL_BindTexture(renderTextures? skinTexture : 0);
1043 
1044             drawPrimitives(RC_BOTH_COORDS, primitives,
1045                            modelPosCoords, modelColorCoords, modelTexCoords);
1046 
1047             selectTexUnits(1);
1048             DGL_ModulateTexture(1);
1049         }
1050     }
1051     else
1052     {
1053         // A special case: specular shininess on an opaque object.
1054         // Multitextured shininess with the normal blending.
1055         GL_BlendMode(blending);
1056         selectTexUnits(2);
1057 
1058         // Tex1*Color + Tex2RGB*ConstRGB
1059         DGL_ModulateTexture(10);
1060 
1061         DGL_SetInteger(DGL_ACTIVE_TEXTURE, 1);
1062         GL_BindTexture(renderTextures? shinyTexture : 0);
1063 
1064         // Multiply by shininess.
1065         float colorv1[] = { color.x * color.w, color.y * color.w, color.z * color.w, color.w };
1066         DGL_SetModulationColor(colorv1);
1067 
1068         DGL_SetInteger(DGL_ACTIVE_TEXTURE, 0);
1069         GL_BindTexture(renderTextures? skinTexture : 0);
1070 
1071         drawPrimitives(RC_BOTH_COORDS, primitives,
1072                        modelPosCoords, modelColorCoords, modelTexCoords);
1073 
1074         selectTexUnits(1);
1075         DGL_ModulateTexture(1);
1076     }
1077 
1078     // We're done!
1079     DGL_Disable(DGL_TEXTURE_2D);
1080     DGL_MatrixMode(DGL_MODELVIEW);
1081     DGL_PopMatrix();
1082 
1083     // Normally culling is always enabled.
1084     if (smf.testFlag(MFF_TWO_SIDED))
1085     {
1086         //glEnable(GL_CULL_FACE);
1087         DGL_CullFace(DGL_BACK);
1088     }
1089 
1090     if (zSign < 0)
1091     {
1092         LIBGUI_GL.glFrontFace(GL_CW);
1093     }
1094     //glDepthFunc(GL_LESS);
1095     DGL_DepthFunc(DGL_LESS);
1096 
1097     GL_BlendMode(BM_NORMAL);
1098 }
1099 
Rend_DrawModel(vissprite_t const & spr)1100 void Rend_DrawModel(vissprite_t const &spr)
1101 {
1102     drawmodelparams_t const &parm = *VS_MODEL(&spr);
1103 
1104     DENG2_ASSERT(inited);
1105     DENG_ASSERT_IN_MAIN_THREAD();
1106     DENG_ASSERT_GL_CONTEXT_ACTIVE();
1107 
1108     if (!parm.mf) return;
1109 
1110     DENG2_ASSERT(parm.mf->select == (parm.selector & DDMOBJ_SELECTOR_MASK))
1111 
1112     // Render all the submodels of this model.
1113     for (uint i = 0; i < parm.mf->subCount(); ++i)
1114     {
1115         if (parm.mf->subModelId(i))
1116         {
1117             bool disableZ = (parm.mf->flags & MFF_DISABLE_Z_WRITE ||
1118                              parm.mf->testSubFlag(i, MFF_DISABLE_Z_WRITE));
1119 
1120             if (disableZ)
1121             {
1122                 DGL_Disable(DGL_DEPTH_WRITE);
1123             }
1124 
1125             drawSubmodel(i, spr);
1126 
1127             if (disableZ)
1128             {
1129                 DGL_Enable(DGL_DEPTH_WRITE);
1130             }
1131         }
1132     }
1133 
1134     if (devMobjVLights && spr.light.vLightListIdx)
1135     {
1136         // Draw the vlight vectors, for debug.
1137         //glDisable(GL_DEPTH_TEST);
1138         //glDisable(GL_CULL_FACE);
1139         DGL_PushState();
1140         DGL_Disable(DGL_DEPTH_TEST);
1141         DGL_CullFace(DGL_NONE);
1142 
1143         DGL_MatrixMode(DGL_MODELVIEW);
1144         DGL_PushMatrix();
1145 
1146         DGL_Translatef(spr.pose.origin[0], spr.pose.origin[2], spr.pose.origin[1]);
1147 
1148         coord_t const distFromViewer = de::abs(spr.pose.distance);
1149         ClientApp::renderSystem().forAllVectorLights(spr.light.vLightListIdx, [&distFromViewer] (VectorLightData const &vlight)
1150         {
1151             if (distFromViewer < 1600 - 8)
1152             {
1153                 Rend_DrawVectorLight(vlight, 1 - distFromViewer / 1600);
1154             }
1155             return LoopContinue;
1156         });
1157 
1158         DGL_MatrixMode(DGL_MODELVIEW);
1159         DGL_PopMatrix();
1160 
1161         DGL_PopState();
1162     }
1163 }
1164 
Rend_ModelDiffuseTextureSpec(bool noCompression)1165 TextureVariantSpec const &Rend_ModelDiffuseTextureSpec(bool noCompression)
1166 {
1167     return ClientApp::resources().textureSpec(TC_MODELSKIN_DIFFUSE,
1168         (noCompression? TSF_NO_COMPRESSION : 0), 0, 0, 0, GL_REPEAT, GL_REPEAT,
1169         1, -2, -1, true, true, false, false);
1170 }
1171 
Rend_ModelShinyTextureSpec()1172 TextureVariantSpec const &Rend_ModelShinyTextureSpec()
1173 {
1174     return ClientApp::resources().textureSpec(TC_MODELSKIN_REFLECTION,
1175         TSF_NO_COMPRESSION, 0, 0, 0, GL_REPEAT, GL_REPEAT, 1, -2, -1, false,
1176         false, false, false);
1177 }
1178