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