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