1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 // bzflag common headers
14 #include "common.h"
15 #include "global.h"
16 
17 // interface header
18 #include "TankGeometryMgr.h"
19 
20 // system headers
21 #include <stdlib.h>
22 #include <math.h>
23 #include <string>
24 
25 // common implementation headers
26 #include "SceneRenderer.h"
27 #include "StateDatabase.h"
28 #include "BZDBCache.h"
29 #include "OpenGLGState.h"
30 
31 
32 // use the namespaces
33 using namespace TankGeometryMgr;
34 using namespace TankGeometryEnums;
35 using namespace TankGeometryUtils;
36 
37 
38 // Local Variables
39 // ---------------
40 
41 // the display lists
42 static GLuint displayLists[LastTankShadow][LastTankLOD]
43 [LastTankSize][LastTankPart];
44 
45 // triangle counts
46 static int partTriangles[LastTankShadow][LastTankLOD]
47 [LastTankSize][LastTankPart];
48 
49 // the scaling factors
50 static GLfloat scaleFactors[LastTankSize][3] =
51 {
52     {1.0f, 1.0f, 1.0f},   // Normal
53     {1.0f, 1.0f, 1.0f},   // Obese
54     {1.0f, 1.0f, 1.0f},   // Tiny
55     {1.0f, 0.001f, 1.0f}, // Narrow
56     {1.0f, 1.0f, 1.0f}    // Thief
57 };
58 // the current scaling factors
59 static const float* currentScaleFactor = scaleFactors[Normal];
60 
61 // the current shadow mode (used to remove glNormal3f and glTexcoord2f calls)
62 static TankShadow shadowMode = ShadowOn;
63 
64 // arrays of functions to avoid large switch statements
65 typedef int (*partFunction)(void);
66 static const partFunction partFunctions[LastTankLOD][BasicTankParts] =
67 {
68     {
69         buildLowBody,
70         buildLowBarrel,
71         buildLowTurret,
72         buildLowLCasing,
73         buildLowRCasing
74     },
75     {
76         buildMedBody,
77         NULL,
78         buildMedTurret,
79         buildMedLCasing,
80         buildMedRCasing
81     },
82     {
83         buildHighBody,
84         buildHighBarrel,
85         buildHighTurret,
86         buildHighLCasing,
87         buildHighRCasing
88     }
89 };
90 
91 
92 // Local Function Prototypes
93 // -------------------------
94 
95 static void setupScales();
96 static void freeContext(void *data);
97 static void initContext(void *data);
98 static void bzdbCallback(const std::string& str, void *data);
99 
100 
101 /****************************************************************************/
102 
103 // TankGeometryMgr Functions
104 // -------------------------
105 
106 
init()107 void TankGeometryMgr::init()
108 {
109     // initialize the lists to invalid
110     for (int shadow = 0; shadow < LastTankShadow; shadow++)
111     {
112         for (int lod = 0; lod < LastTankLOD; lod++)
113         {
114             for (int size = 0; size < LastTankSize; size++)
115             {
116                 for (int part = 0; part < LastTankPart; part++)
117                 {
118                     displayLists[shadow][lod][size][part] = INVALID_GL_LIST_ID;
119                     partTriangles[shadow][lod][size][part] = 0;
120                 }
121             }
122         }
123     }
124 
125     // install the BZDB callbacks
126     // This MUST be done after BZDB has been initialized in main()
127     BZDB.addCallback (StateDatabase::BZDB_OBESEFACTOR, bzdbCallback, NULL);
128     BZDB.addCallback (StateDatabase::BZDB_TINYFACTOR, bzdbCallback, NULL);
129     BZDB.addCallback (StateDatabase::BZDB_THIEFTINYFACTOR, bzdbCallback, NULL);
130     BZDB.addCallback ("animatedTreads", bzdbCallback, NULL);
131 
132     // install the context initializer
133     OpenGLGState::registerContextInitializer (freeContext, initContext, NULL);
134 
135     // setup the scaleFactors
136     setupScales();
137 
138     return;
139 }
140 
141 
kill()142 void TankGeometryMgr::kill()
143 {
144     // remove the BZDB callbacks
145     BZDB.removeCallback (StateDatabase::BZDB_OBESEFACTOR, bzdbCallback, NULL);
146     BZDB.removeCallback (StateDatabase::BZDB_TINYFACTOR, bzdbCallback, NULL);
147     BZDB.removeCallback (StateDatabase::BZDB_THIEFTINYFACTOR, bzdbCallback, NULL);
148     BZDB.removeCallback ("animatedTreads", bzdbCallback, NULL);
149 
150     // remove the context initializer callback
151     OpenGLGState::unregisterContextInitializer(freeContext, initContext, NULL);
152 
153     return;
154 }
155 
156 
deleteLists()157 void TankGeometryMgr::deleteLists()
158 {
159     // delete the lists that have been aquired
160     for (int shadow = 0; shadow < LastTankShadow; shadow++)
161     {
162         for (int lod = 0; lod < LastTankLOD; lod++)
163         {
164             for (int size = 0; size < LastTankSize; size++)
165             {
166                 for (int part = 0; part < LastTankPart; part++)
167                 {
168                     GLuint& list = displayLists[shadow][lod][size][part];
169                     if (list != INVALID_GL_LIST_ID)
170                     {
171                         glDeleteLists(list, 1);
172                         list = INVALID_GL_LIST_ID;
173                     }
174                 }
175             }
176         }
177     }
178     return;
179 }
180 
181 
buildLists()182 void TankGeometryMgr::buildLists()
183 {
184     // setup the tread style
185     setTreadStyle(BZDB.evalInt("treadStyle"));
186 
187     // setup the scale factors
188     setupScales();
189     currentScaleFactor = scaleFactors[Normal];
190     const bool animated = BZDBCache::animatedTreads;
191 
192     // setup the quality level
193     const int divisionLevels[4][2] =   // wheel divs, tread divs
194     {
195         {4, 4},   // low
196         {8, 16},  // med
197         {12, 24}, // high
198         {16, 32}  // experimental
199     };
200     int quality = RENDERER.useQuality();
201     if (quality < 0)
202         quality = 0;
203     else if (quality > 3)
204         quality = 3;
205     int wheelDivs = divisionLevels[quality][0];
206     int treadDivs = divisionLevels[quality][1];
207 
208     for (int shadow = 0; shadow < LastTankShadow; shadow++)
209     {
210         for (int lod = 0; lod < LastTankLOD; lod++)
211         {
212             for (int size = 0; size < LastTankSize; size++)
213             {
214 
215                 // only do the basics, unless we're making an animated tank
216                 int lastPart = BasicTankParts;
217                 if (animated)
218                     lastPart = HighTankParts;
219 
220                 // set the shadow mode for the doNormal3f() and doTexcoord2f() calls
221                 shadowMode = (TankShadow) shadow;
222 
223                 for (int part = 0; part < lastPart; part++)
224                 {
225 
226                     if ((part == Barrel) && (lod == MedTankLOD))
227                         continue;
228                     GLuint& list = displayLists[shadow][lod][size][part];
229                     int& count = partTriangles[shadow][lod][size][part];
230 
231                     // get a new OpenGL display list
232                     list = glGenLists(1);
233                     glNewList(list, GL_COMPILE);
234 
235                     // setup the scale factor
236                     currentScaleFactor = scaleFactors[size];
237 
238                     if ((part <= Turret) || (!animated))
239                     {
240                         // the basic parts
241                         count = partFunctions[lod][part]();
242                     }
243                     else
244                     {
245                         // the animated parts
246                         if (part == LeftCasing)
247                             count = buildHighLCasingAnim();
248                         else if (part == RightCasing)
249                             count = buildHighRCasingAnim();
250                         else if (part == LeftTread)
251                             count = buildHighLTread(treadDivs);
252                         else if (part == RightTread)
253                             count = buildHighRTread(treadDivs);
254                         else if ((part >= LeftWheel0) && (part <= LeftWheel3))
255                         {
256                             int wheel = part - LeftWheel0;
257                             count = buildHighLWheel(wheel, (float)wheel * (float)(M_PI / 2.0),
258                                                     wheelDivs);
259                         }
260                         else if ((part >= RightWheel0) && (part <= RightWheel3))
261                         {
262                             int wheel = part - RightWheel0;
263                             count = buildHighRWheel(wheel, (float)wheel * (float)(M_PI / 2.0),
264                                                     wheelDivs);
265                         }
266                     }
267 
268                     // end of the list
269                     glEndList();
270 
271                 } // part
272             } // size
273         } // lod
274     } // shadow
275 
276     return;
277 }
278 
279 
getPartList(TankGeometryEnums::TankShadow shadow,TankGeometryEnums::TankPart part,TankGeometryEnums::TankSize size,TankGeometryEnums::TankLOD lod)280 GLuint TankGeometryMgr::getPartList(TankGeometryEnums::TankShadow shadow,
281                                     TankGeometryEnums::TankPart part,
282                                     TankGeometryEnums::TankSize size,
283                                     TankGeometryEnums::TankLOD lod)
284 {
285     if ((part == Barrel) && (lod == MedTankLOD))
286         lod = LowTankLOD;
287 
288     return displayLists[shadow][lod][size][part];
289 }
290 
291 
getPartTriangleCount(TankGeometryEnums::TankShadow sh,TankGeometryEnums::TankPart part,TankGeometryEnums::TankSize size,TankGeometryEnums::TankLOD lod)292 int TankGeometryMgr::getPartTriangleCount(TankGeometryEnums::TankShadow sh,
293         TankGeometryEnums::TankPart part,
294         TankGeometryEnums::TankSize size,
295         TankGeometryEnums::TankLOD lod)
296 {
297     if ((part == Barrel) && (lod == MedTankLOD))
298         lod = LowTankLOD;
299 
300     return partTriangles[sh][lod][size][part];
301 }
302 
303 
getScaleFactor(TankSize size)304 const float* TankGeometryMgr::getScaleFactor(TankSize size)
305 {
306     return scaleFactors[size];
307 }
308 
309 
310 /****************************************************************************/
311 
312 // Local Functions
313 // ---------------
314 
315 
bzdbCallback(const std::string & UNUSED (name),void * UNUSED (data))316 static void bzdbCallback(const std::string& UNUSED(name), void * UNUSED(data))
317 {
318     deleteLists();
319     buildLists();
320     return;
321 }
322 
323 
freeContext(void * UNUSED (data))324 static void freeContext(void * UNUSED(data))
325 {
326     // delete all of the lists
327     deleteLists();
328     return;
329 }
330 
331 
initContext(void * UNUSED (data))332 static void initContext(void * UNUSED(data))
333 {
334     buildLists();
335     return;
336 }
337 
338 
setupScales()339 static void setupScales()
340 {
341     float scale;
342 
343     scaleFactors[Normal][0] = BZDBCache::tankLength;
344     scale = (float)atof(BZDB.getDefault(StateDatabase::BZDB_TANKLENGTH).c_str());
345     scaleFactors[Normal][0] /= scale;
346 
347     scaleFactors[Normal][1] = BZDBCache::tankWidth;
348     scale = (float)atof(BZDB.getDefault(StateDatabase::BZDB_TANKWIDTH).c_str());
349     scaleFactors[Normal][1] /= scale;
350 
351     scaleFactors[Normal][2] = BZDBCache::tankHeight;
352     scale = (float)atof(BZDB.getDefault(StateDatabase::BZDB_TANKHEIGHT).c_str());
353     scaleFactors[Normal][2] /= scale;
354 
355     scale = BZDB.eval(StateDatabase::BZDB_OBESEFACTOR);
356     scaleFactors[Obese][0] = scale * scaleFactors[Normal][0];
357     scaleFactors[Obese][1] = scale * scaleFactors[Normal][1];
358     scaleFactors[Obese][2] = scaleFactors[Normal][2];
359 
360     scale = BZDB.eval(StateDatabase::BZDB_TINYFACTOR);
361     scaleFactors[Tiny][0] = scale * scaleFactors[Normal][0];
362     scaleFactors[Tiny][1] = scale * scaleFactors[Normal][1];
363     scaleFactors[Tiny][2] = scaleFactors[Normal][2];
364 
365     scale = BZDB.eval(StateDatabase::BZDB_THIEFTINYFACTOR);
366     scaleFactors[Thief][0] = scale * scaleFactors[Normal][0];
367     scaleFactors[Thief][1] = scale * scaleFactors[Normal][1];
368     scaleFactors[Thief][2] = scaleFactors[Normal][2];
369 
370     scaleFactors[Narrow][0] = scaleFactors[Normal][0];
371     scaleFactors[Narrow][1] = 0.001f;
372     scaleFactors[Narrow][2] = scaleFactors[Normal][2];
373 
374     return;
375 }
376 
377 
378 /****************************************************************************/
379 
380 // TankGeometryUtils Functions
381 // ---------------------------
382 
383 
doVertex3f(GLfloat x,GLfloat y,GLfloat z)384 void TankGeometryUtils::doVertex3f(GLfloat x, GLfloat y, GLfloat z)
385 {
386     const float* scale = currentScaleFactor;
387     x = x * scale[0];
388     y = y * scale[1];
389     z = z * scale[2];
390     glVertex3f(x, y, z);
391     return;
392 }
393 
394 
doNormal3f(GLfloat x,GLfloat y,GLfloat z)395 void TankGeometryUtils::doNormal3f(GLfloat x, GLfloat y, GLfloat z)
396 {
397     if (shadowMode == ShadowOn)
398         return;
399     const float* scale = currentScaleFactor;
400     GLfloat sx = x * scale[0];
401     GLfloat sy = y * scale[1];
402     GLfloat sz = z * scale[2];
403     const GLfloat d = sqrtf ((sx * sx) + (sy * sy) + (sz * sz));
404     if (d > 1.0e-5f)
405     {
406         x *= scale[0] / d;
407         y *= scale[1] / d;
408         z *= scale[2] / d;
409     }
410     glNormal3f(x, y, z);
411     return;
412 }
413 
414 
doTexCoord2f(GLfloat x,GLfloat y)415 void TankGeometryUtils::doTexCoord2f(GLfloat x, GLfloat y)
416 {
417     if (shadowMode == ShadowOn)
418         return;
419     glTexCoord2f(x, y);
420     return;
421 }
422 
423 
424 // Local Variables: ***
425 // mode: C++ ***
426 // tab-width: 4 ***
427 // c-basic-offset: 4 ***
428 // indent-tabs-mode: nil ***
429 // End: ***
430 // ex: shiftwidth=4 tabstop=4
431