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 // interface header
14 #include "BoltSceneNode.h"
15 
16 // system headers
17 #include <stdlib.h>
18 #include <math.h>
19 
20 // common implementation headers
21 #include "StateDatabase.h"
22 #include "BZDBCache.h"
23 #include "TextureManager.h"
24 
25 // local implementation headers
26 #include "ViewFrustum.h"
27 
28 // FIXME (SceneRenderer.cxx is in src/bzflag)
29 #include "SceneRenderer.h"
30 
31 #include "TimeKeeper.h"
32 
BoltSceneNode(const GLfloat pos[3],const GLfloat vel[3],bool super)33 BoltSceneNode::BoltSceneNode(const GLfloat pos[3],const GLfloat vel[3], bool super) :
34     isSuper(super),
35     invisible(false),
36     drawFlares(false),
37     texturing(false),
38     colorblind(false),
39     size(1.0f),
40     renderNode(this),
41     azimuth(0),
42     elevation(0),
43     length(1.0f)
44 {
45 
46     OpenGLGStateBuilder builder(gstate);
47     builder.setBlending();
48     builder.setAlphaFunc();
49     //builder.setTextureEnvMode(GL_DECAL);
50     gstate = builder.getState();
51 
52     // prepare light
53     light.setAttenuation(0, 0.05f);
54     light.setAttenuation(1, 0.0f);
55     light.setAttenuation(2, 0.03f);
56 
57     // prepare geometry
58     move(pos, vel);
59     setSize(size);
60     setColor(1.0f, 1.0f, 1.0f);
61     teamColor = fvec4(1,1,1,1);
62 }
63 
~BoltSceneNode()64 BoltSceneNode::~BoltSceneNode()
65 {
66     // do nothing
67 }
68 
setFlares(bool on)69 void            BoltSceneNode::setFlares(bool on)
70 {
71     drawFlares = on;
72 }
73 
setSize(float radius)74 void            BoltSceneNode::setSize(float radius)
75 {
76     size = radius;
77     setRadius(size * size);
78 }
setTextureColor(GLfloat r,GLfloat g,GLfloat b,GLfloat a)79 void            BoltSceneNode::setTextureColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
80 {
81     color[0] = r;
82     color[1] = g;
83     color[2] = b;
84     color[3] = a;
85     light.setColor(1.5f * r, 1.5f * g, 1.5f * b);
86     renderNode.setTextureColor(color);
87 }
88 
setColor(GLfloat r,GLfloat g,GLfloat b,GLfloat a)89 void            BoltSceneNode::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
90 {
91     color[0] = r;
92     color[1] = g;
93     color[2] = b;
94     color[3] = a;
95     light.setColor(1.5f * r, 1.5f * g, 1.5f * b);
96     renderNode.setColor(color);
97 }
98 
setTeamColor(const GLfloat * c)99 void            BoltSceneNode::setTeamColor(const GLfloat *c)
100 {
101     teamColor.r = c[0];
102     teamColor.g = c[1];
103     teamColor.b = c[2];
104     teamColor.w = 1.0f;
105 }
106 
setColor(const GLfloat * rgb)107 void            BoltSceneNode::setColor(const GLfloat* rgb)
108 {
109     setColor(rgb[0], rgb[1], rgb[2]);
110 }
111 
getColorblind() const112 bool            BoltSceneNode::getColorblind() const
113 {
114     return colorblind;
115 }
116 
setColorblind(bool _colorblind)117 void            BoltSceneNode::setColorblind(bool _colorblind)
118 {
119     colorblind = _colorblind;
120 }
121 
setTexture(const int texture)122 void            BoltSceneNode::setTexture(const int texture)
123 {
124     OpenGLGStateBuilder builder(gstate);
125     builder.setTexture(texture);
126     builder.enableTexture(texture>=0);
127     gstate = builder.getState();
128 }
129 
setTextureAnimation(int cu,int cv)130 void            BoltSceneNode::setTextureAnimation(int cu, int cv)
131 {
132     renderNode.setAnimation(cu, cv);
133 }
134 
move(const GLfloat pos[3],const GLfloat vel[3])135 void            BoltSceneNode::move(const GLfloat pos[3],
136                                     const GLfloat vel[3])
137 {
138     setCenter(pos);
139     light.setPosition(pos);
140     velocity[0] = vel[0];
141     velocity[1] = vel[1];
142     velocity[2] = vel[2];
143     length = sqrtf((vel[0] * vel[0]) +
144                    (vel[1] * vel[1]) +
145                    (vel[2] * vel[2]));
146 
147     azimuth   = (float)(+RAD2DEG * atan2f(vel[1], vel[0]));
148     elevation = (float)(-RAD2DEG * atan2f(vel[2], sqrtf(vel[0]* vel[0] + vel[1] *vel[1])));
149 }
150 
addLight(SceneRenderer & renderer)151 void            BoltSceneNode::addLight(
152     SceneRenderer& renderer)
153 {
154     renderer.addLight(light);
155 }
156 
notifyStyleChange()157 void            BoltSceneNode::notifyStyleChange()
158 {
159     texturing = BZDBCache::texture && BZDBCache::blend;
160     OpenGLGStateBuilder builder(gstate);
161     builder.enableTexture(texturing);
162     if (BZDBCache::blend)
163     {
164         const int shotLength = (int)(BZDBCache::shotLength * 3.0f);
165         if (shotLength > 0 && !drawFlares)
166             builder.setBlending(GL_SRC_ALPHA, GL_ONE);
167         else
168             builder.setBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
169         builder.setStipple(1.0f);
170         builder.setAlphaFunc();
171         if ((RENDERER.useQuality() >= 3) && drawFlares)
172         {
173             builder.setShading(GL_SMOOTH);
174             builder.enableMaterial(false);
175         }
176         else
177             builder.setShading(texturing ? GL_FLAT : GL_SMOOTH);
178     }
179     else
180     {
181         builder.resetBlending();
182         builder.resetAlphaFunc();
183         builder.setStipple(0.5f);
184         builder.setShading(GL_FLAT);
185     }
186     gstate = builder.getState();
187 }
188 
addRenderNodes(SceneRenderer & renderer)189 void            BoltSceneNode::addRenderNodes(
190     SceneRenderer& renderer)
191 {
192     renderer.addRenderNode(&renderNode, &gstate);
193 }
194 
195 //
196 // BoltSceneNode::BoltRenderNode
197 //
198 
199 const GLfloat       BoltSceneNode::BoltRenderNode::CoreFraction = 0.4f;
200 const GLfloat       BoltSceneNode::BoltRenderNode::FlareSize = 1.0f;
201 const GLfloat       BoltSceneNode::BoltRenderNode::FlareSpread = 0.08f;
202 GLfloat         BoltSceneNode::BoltRenderNode::core[9][2];
203 GLfloat         BoltSceneNode::BoltRenderNode::corona[8][2];
204 const GLfloat       BoltSceneNode::BoltRenderNode::ring[8][2] =
205 {
206     { 1.0f, 0.0f },
207     { (float)M_SQRT1_2, (float)M_SQRT1_2 },
208     { 0.0f, 1.0f },
209     { (float)-M_SQRT1_2, (float)M_SQRT1_2 },
210     { -1.0f, 0.0f },
211     { (float)-M_SQRT1_2, (float)-M_SQRT1_2 },
212     { 0.0f, -1.0f },
213     { (float)M_SQRT1_2, (float)-M_SQRT1_2 }
214 };
215 
BoltRenderNode(const BoltSceneNode * _sceneNode)216 BoltSceneNode::BoltRenderNode::BoltRenderNode(
217     const BoltSceneNode* _sceneNode) :
218     sceneNode(_sceneNode),
219     numFlares(0)
220 {
221     // initialize core and corona if not already done
222     static bool init = false;
223     if (!init)
224     {
225         init = true;
226         core[0][0] = 0.0f;
227         core[0][1] = 0.0f;
228         for (int i = 0; i < 8; i++)
229         {
230             core[i+1][0] = CoreFraction * ring[i][0];
231             core[i+1][1] = CoreFraction * ring[i][1];
232             corona[i][0] = ring[i][0];
233             corona[i][1] = ring[i][1];
234         }
235     }
236 
237     textureColor[0] = 1.0f;
238     textureColor[1] = 1.0f;
239     textureColor[2] = 1.0f;
240     textureColor[3] = 1.0f;
241 
242     setAnimation(1, 1);
243 }
244 
~BoltRenderNode()245 BoltSceneNode::BoltRenderNode::~BoltRenderNode()
246 {
247     // do nothing
248 }
249 
setAnimation(int _cu,int _cv)250 void            BoltSceneNode::BoltRenderNode::setAnimation(
251     int _cu, int _cv)
252 {
253     cu = _cu;
254     cv = _cv;
255     du = 1.0f / (float)cu;
256     dv = 1.0f / (float)cv;
257 
258     // pick a random start frame
259     const int index = (int)((float)cu * (float)cv * bzfrand());
260     u = index % cu;
261     v = index / cu;
262     if (v >= cv) v = 0;
263 }
setTextureColor(const GLfloat * rgba)264 void            BoltSceneNode::BoltRenderNode::setTextureColor(const GLfloat* rgba)
265 {
266     textureColor[0] = rgba[0];
267     textureColor[1] = rgba[1];
268     textureColor[2] = rgba[2];
269     textureColor[3] = rgba[3];
270 }
271 
272 
setColor(const GLfloat * rgba)273 void            BoltSceneNode::BoltRenderNode::setColor(
274     const GLfloat* rgba)
275 {
276     mainColor[0] = rgba[0];
277     mainColor[1] = rgba[1];
278     mainColor[2] = rgba[2];
279     mainColor[3] = rgba[3];
280 
281     innerColor[0] = mainColor[0] + 0.5f * (1.0f - mainColor[0]);
282     innerColor[1] = mainColor[1] + 0.5f * (1.0f - mainColor[1]);
283     innerColor[2] = mainColor[2] + 0.5f * (1.0f - mainColor[2]);
284     innerColor[3] = rgba[3];
285 
286     outerColor[0] = mainColor[0];
287     outerColor[1] = mainColor[1];
288     outerColor[2] = mainColor[2];
289     outerColor[3] = (rgba[3] == 1.0f )? 0.1f: rgba[3];
290 
291     coronaColor[0] = mainColor[0];
292     coronaColor[1] = mainColor[1];
293     coronaColor[2] = mainColor[2];
294     coronaColor[3] = (rgba[3] == 1.0f )? 0.5f : rgba[3];
295 
296     flareColor[0] = mainColor[0];
297     flareColor[1] = mainColor[1];
298     flareColor[2] = mainColor[2];
299     flareColor[3] = (rgba[3] == 1.0f )? 0.667f : rgba[3];
300 }
301 
drawFin(float maxRad,float finRadius,float boosterLen,float finForeDelta,float finCapSize)302 void drawFin ( float maxRad, float finRadius, float boosterLen, float finForeDelta, float finCapSize)
303 {
304     glBegin(GL_TRIANGLE_STRIP);
305     glNormal3f(1,0,0);
306     glVertex3f(0,maxRad,0);
307     glVertex3f(0,maxRad,boosterLen);
308     glVertex3f(0,maxRad+finRadius,boosterLen-finForeDelta-finCapSize);
309     glVertex3f(0,maxRad+finRadius,boosterLen-finForeDelta);
310     glEnd();
311 
312     glBegin(GL_TRIANGLE_STRIP);
313     glNormal3f(-1,0,0);
314     glVertex3f(0,maxRad+finRadius,boosterLen-finForeDelta-finCapSize);
315     glVertex3f(0,maxRad+finRadius,boosterLen-finForeDelta);
316     glVertex3f(0,maxRad,0);
317     glVertex3f(0,maxRad,boosterLen);
318     glEnd();
319 }
320 
renderGeoGMBolt()321 void BoltSceneNode::BoltRenderNode::renderGeoGMBolt()
322 {
323     // bzdb these 2? they control the shot size
324     float gmMissleSize = BZDBCache::gmSize;
325 
326     // parametrics
327     float maxRad = gmMissleSize * 0.16f;
328     float noseRad = gmMissleSize * 0.086f;
329     float waistRad = gmMissleSize * 0.125f;
330     float engineRad = gmMissleSize * 0.1f;
331 
332     float noseLen = gmMissleSize * 0.1f;
333     float bodyLen = gmMissleSize * 0.44f;
334     float bevelLen = gmMissleSize * 0.02f;
335     float waistLen = gmMissleSize * 0.16f;
336     float boosterLen = gmMissleSize * 0.2f;
337     float engineLen = gmMissleSize * 0.08f;
338 
339     float finRadius = gmMissleSize * 0.16f;
340     float finCapSize = gmMissleSize * 0.15f;
341     float finForeDelta = gmMissleSize * 0.02f;
342 
343     int slices = 8;
344 
345     float rotSpeed = 90.0f;
346 
347     glDepthMask(GL_TRUE);
348     glPushMatrix();
349     glRotatef(sceneNode->azimuth, 0.0f, 0.0f, 1.0f);
350     glRotatef(sceneNode->elevation, 0.0f, 1.0f, 0.0f);
351     glRotatef(90, 0.0f, 1.0f, 0.0f);
352 
353     glDisable(GL_TEXTURE_2D);
354     //glEnable(GL_LIGHTING);
355 
356     fvec4 noseColor = sceneNode->teamColor;
357     fvec4 finColor(noseColor.r*0.5f,noseColor.g*0.5f,noseColor.b*0.5f,1);
358     fvec4 coneColor(0.125f,0.125f,0.125f,1);
359     fvec4 bodyColor(1,1,1,1);
360 
361     glPushMatrix();
362 
363     GLUquadric *q = gluNewQuadric();
364 
365     glColor4f(noseColor.r,noseColor.g,noseColor.b,1.0f);
366     glTranslatef(0, 0, gmMissleSize);
367     glRotatef((float)TimeKeeper::getCurrent().getSeconds() * rotSpeed,0,0,1);
368 
369     // nosecone
370     gluDisk(q,0,noseRad,slices,1);
371     glTranslatef(0, 0, -noseLen);
372     gluCylinder(q,maxRad,noseRad,noseLen,slices,1);
373     addTriangleCount(slices * 2);
374 
375     // body
376     myColor4fv(bodyColor);
377     glTranslatef(0, 0, -bodyLen);
378     gluCylinder(q,maxRad,maxRad,bodyLen,slices,1);
379     addTriangleCount(slices);
380 
381     glTranslatef(0, 0, -bevelLen);
382     gluCylinder(q,waistRad,maxRad,bevelLen,slices,1);
383     addTriangleCount(slices);
384 
385     // waist
386     myColor4fv(coneColor);
387     glTranslatef(0, 0, -waistLen);
388     gluCylinder(q,waistRad,waistRad,waistLen,slices,1);
389     addTriangleCount(slices);
390 
391     // booster
392     myColor3fv(bodyColor);
393     glTranslatef(0, 0, -bevelLen);
394     gluCylinder(q,maxRad,waistRad,bevelLen,slices,1);
395     addTriangleCount(slices);
396 
397     glTranslatef(0, 0, -boosterLen);
398     gluCylinder(q,maxRad,maxRad,boosterLen,slices,1);
399     addTriangleCount(slices);
400 
401     glTranslatef(0, 0, -bevelLen);
402     gluCylinder(q,waistRad,maxRad,bevelLen,slices,1);
403     addTriangleCount(slices);
404 
405     // engine
406     myColor3fv(coneColor);
407     glTranslatef(0, 0, -engineLen);
408     gluCylinder(q,engineRad,waistRad,engineLen,slices,1);
409     addTriangleCount(slices);
410 
411     // fins
412     myColor3fv(finColor);
413     glTranslatef(0, 0, engineLen + bevelLen);
414 
415     for ( int i = 0; i < 4; i++)
416     {
417         glRotatef(i*90.0f,0,0,1);
418         drawFin ( maxRad, finRadius, boosterLen, finForeDelta, finCapSize);
419     }
420 
421     glPopMatrix();
422 
423     gluDeleteQuadric(q);
424 
425     glEnable(GL_TEXTURE_2D);
426     // glDisable(GL_LIGHTING);
427 
428     glPopMatrix();
429 
430     glDepthMask(GL_FALSE);
431 }
432 
433 
renderGeoBolt()434 void BoltSceneNode::BoltRenderNode::renderGeoBolt()
435 {
436     // bzdb these 2? they control the shot size
437     float lenMod = 0.0675f + (BZDBCache::shotLength * 0.0125f);
438     float baseRadius = 0.225f;
439 
440     float len = sceneNode->length * lenMod;
441     glPushMatrix();
442     glRotatef(sceneNode->azimuth, 0.0f, 0.0f, 1.0f);
443     glRotatef(sceneNode->elevation, 0.0f, 1.0f, 0.0f);
444     glRotatef(90, 0.0f, 1.0f, 0.0f);
445 
446     float alphaMod = 1.0f;
447 // if (sceneNode->phasingShot)
448 //   alphaMod = 0.85f;
449 
450     glDisable(GL_TEXTURE_2D);
451 
452     float coreBleed = 4.5f;
453     float minimumChannelVal = 0.45f;
454 
455     fvec3 coreColor;
456     coreColor.r =  sceneNode->color[0] * coreBleed;
457     coreColor.g =  sceneNode->color[1] * coreBleed;
458     coreColor.b =  sceneNode->color[2] * coreBleed;
459     if (coreColor.r < minimumChannelVal)
460         coreColor.r = minimumChannelVal;
461     if (coreColor.g < minimumChannelVal)
462         coreColor.g = minimumChannelVal;
463     if (coreColor.b < minimumChannelVal)
464         coreColor.b = minimumChannelVal;
465 
466     myColor4fv(fvec4(coreColor, 0.85f * alphaMod));
467     renderGeoPill(baseRadius,len,16);
468 
469     float radInc = 1.5f * baseRadius - baseRadius;
470     glPushMatrix();
471     glTranslatef(0, 0, -radInc * 0.5f);
472     fvec4 c;
473     c.x = sceneNode->color[0];
474     c.y = sceneNode->color[1];
475     c.z = sceneNode->color[2];
476     c.w = 0.5f;
477 
478     myColor4fv(c);
479     renderGeoPill(1.5f * baseRadius, len + radInc, 25);
480     glPopMatrix();
481 
482     radInc = 2.7f * baseRadius - baseRadius;
483     glPushMatrix();
484     glTranslatef(0, 0, -radInc*0.5f);
485     c.w = 0.25f;
486     myColor4fv(c);
487     renderGeoPill(2.7f * baseRadius, len + radInc, 32);
488     glPopMatrix();
489 
490     radInc = 3.8f * baseRadius - baseRadius;
491     glPushMatrix();
492     glTranslatef(0, 0,-radInc*0.5f);
493     c.w = 0.125f;
494     myColor4fv(c);
495     renderGeoPill(3.8f * baseRadius, len + radInc, 48);
496     glPopMatrix();
497 
498     glEnable(GL_TEXTURE_2D);
499 
500     glPopMatrix();
501 }
502 
503 
renderGeoPill(float radius,float len,int segments,float endRad)504 void BoltSceneNode::BoltRenderNode::renderGeoPill(float radius, float len,
505         int segments, float endRad)
506 {
507     glPushMatrix();
508 
509     float assRadius = radius;
510     if (endRad >= 0)
511         assRadius = endRad;
512 
513     float lenMinusRads = len - (radius+assRadius);
514 
515     GLUquadric *q = gluNewQuadric();
516     if (assRadius > 0)
517     {
518         // 4 parts of the first hemisphere
519         gluCylinder(q,0,assRadius*0.43589,assRadius*0.1f,segments,1);
520         addTriangleCount(segments);
521         glTranslatef(0,0,assRadius*0.1f);
522 
523         gluCylinder(q,assRadius*0.43589,assRadius*0.66144,assRadius*0.15f,segments,1);
524         addTriangleCount(segments);
525         glTranslatef(0,0,assRadius*0.15f);
526 
527         gluCylinder(q,assRadius*0.66144f,assRadius*0.86603f,assRadius*0.25f,segments,1);
528         addTriangleCount(segments);
529         glTranslatef(0,0,assRadius*0.25f);
530 
531         gluCylinder(q,assRadius*0.86603,assRadius,assRadius*0.5f,segments,1);
532         addTriangleCount(segments);
533         glTranslatef(0,0,assRadius*0.5f);
534     }
535 
536     // the "shaft"
537     if (lenMinusRads > 0)
538     {
539         gluCylinder(q,assRadius,radius,lenMinusRads,segments,1);
540         addTriangleCount(segments);
541         glTranslatef(0,0,lenMinusRads);
542     }
543 
544     if (radius > 0)
545     {
546         // 4 parts of the last hemisphere
547         gluCylinder(q,radius,radius*0.86603,radius*0.5f,segments,1);
548         addTriangleCount(segments);
549         glTranslatef(0,0,radius*0.5f);
550 
551         gluCylinder(q,radius*0.86603f,radius*0.66144f,radius*0.25f,segments,1);
552         addTriangleCount(segments);
553         glTranslatef(0,0,radius*0.25f);
554 
555         gluCylinder(q,radius*0.66144,radius*0.43589,radius*0.15f,segments,1);
556         addTriangleCount(segments);
557         glTranslatef(0,0,radius*0.15f);
558 
559         gluCylinder(q,radius*0.43589,0,radius*0.1f,segments,1);
560         addTriangleCount(segments);
561         glTranslatef(0,0,radius*0.1f);
562     }
563 
564     gluDeleteQuadric(q);
565     glPopMatrix();
566 }
567 
render()568 void            BoltSceneNode::BoltRenderNode::render()
569 {
570     if (sceneNode->invisible)
571         return;
572     const float radius = sceneNode->size;
573     const int   shotLength = (int)(BZDBCache::shotLength * 3.0f);
574     const bool  experimental = (RENDERER.useQuality() >= 3);
575 
576     const bool blackFog = RENDERER.isFogActive() && BZDBCache::blend &&
577                           ((shotLength > 0) || experimental);
578     if (blackFog)
579         glFogfv(GL_FOG_COLOR, fvec4(0.0f, 0.0f, 0.0f, 0.0f));
580 
581     const float* sphere = sceneNode->getSphere();
582     glPushMatrix();
583     glTranslatef(sphere[0], sphere[1], sphere[2]);
584 
585     bool drawBillboardShot = false;
586     if (experimental)
587     {
588         if (sceneNode->isSuper)
589             renderGeoBolt();
590         else
591         {
592             if (sceneNode->drawFlares)
593             {
594                 if (BZDBCache::shotLength > 0)
595                     renderGeoGMBolt();
596                 drawBillboardShot = true;
597             }
598             else
599                 drawBillboardShot = true;
600         }
601     }
602     else
603         drawBillboardShot = true;
604 
605     if (drawBillboardShot)
606     {
607         RENDERER.getViewFrustum().executeBillboard();
608         glScalef(radius, radius, radius);
609         // draw some flares
610         if (sceneNode->drawFlares)
611         {
612             if (!RENDERER.isSameFrame())
613             {
614                 numFlares = 3 + int(3.0f * (float)bzfrand());
615                 for (int i = 0; i < numFlares; i++)
616                 {
617                     theta[i] = (float)(2.0 * M_PI * bzfrand());
618                     phi[i] = (float)bzfrand() - 0.5f;
619                     phi[i] *= (float)(2.0 * M_PI * fabsf(phi[i]));
620                 }
621             }
622 
623             if (sceneNode->texturing) glDisable(GL_TEXTURE_2D);
624             myColor4fv(flareColor);
625             if (!BZDBCache::blend) myStipple(flareColor[3]);
626             for (int i = 0; i < numFlares; i++)
627             {
628                 // pick random direction in 3-space.  picking a random theta with
629                 // a uniform distribution is fine, but doing so with phi biases
630                 // the directions toward the poles.  my correction doesn't remove
631                 // the bias completely, but moves it towards the equator, which is
632                 // really where i want it anyway cos the flares are more noticeable
633                 // there.
634                 const float c = FlareSize * cosf(phi[i]);
635                 const float s = FlareSize * sinf(phi[i]);
636                 const float ti = theta[i];
637                 const float fs = FlareSpread;
638                 glBegin(GL_TRIANGLE_STRIP);
639                 glVertex3fv(core[0]);
640                 glVertex3f(c * cosf(ti - fs),   c * sinf(ti - fs),   s);
641                 glVertex3f(c * cosf(ti + fs),   c * sinf(ti + fs),   s);
642                 glVertex3f(c * cosf(ti) * 2.0f, c * sinf(ti) * 2.0f, s * 2.0f);
643                 glEnd();
644             }
645             if (sceneNode->texturing) glEnable(GL_TEXTURE_2D);
646 
647             addTriangleCount(numFlares * 2);
648         }
649 
650         if (sceneNode->texturing)
651         {
652             // draw billboard square
653             const float u0 = (float)u * du;
654             const float v0 = (float)v * dv;
655             const float u1 = u0 + du;
656             const float v1 = v0 + dv;
657             myColor4fv(textureColor); // 1.0f all
658             glBegin(GL_TRIANGLE_STRIP);
659             glTexCoord2f(u0, v0);
660             glVertex2f(-1.0f, -1.0f);
661             glTexCoord2f(u1, v0);
662             glVertex2f(+1.0f, -1.0f);
663             glTexCoord2f(u0, v1);
664             glVertex2f(-1.0f, +1.0f);
665             glTexCoord2f(u1, v1);
666             glVertex2f(+1.0f, +1.0f);
667             glEnd();
668             addTriangleCount(2);
669 
670             // draw shot trail  (more billboarded quads)
671             if ((shotLength > 0) && (sceneNode->length > 1.0e-6f))
672             {
673                 const float startSize  = 0.6f;
674                 const float startAlpha = 0.8f;
675 
676                 glPushAttrib(GL_TEXTURE_BIT);
677                 TextureManager &tm = TextureManager::instance();
678                 const int texID = tm.getTextureID("shot_tail");
679                 const ImageInfo& texInfo = tm.getInfo(texID);
680                 if (texInfo.id >= 0)
681                     texInfo.texture->execute();
682 
683                 fvec3 vel(sceneNode->velocity[0],sceneNode->velocity[1],sceneNode->velocity[2]);
684                 const fvec3  dir = vel * (-1.0f / sceneNode->length);
685 
686                 const float invLenPlusOne = 1.0f / (float)(shotLength + 1);
687                 const float shiftScale = 90.0f / (150.0f + (float)shotLength);
688                 float Size = sceneNode->size * startSize;
689                 float alpha = startAlpha;
690                 const float sizeStep  = Size  * invLenPlusOne;
691                 const float alphaStep = alpha * invLenPlusOne;
692 
693                 fvec3 pos;
694                 pos.x = sphere[0];
695                 pos.y = sphere[1];
696                 pos.z = sphere[2];
697 
698                 int uvCell = rand() % 16;
699 
700                 for (int i = 0; i < shotLength; i++)
701                 {
702                     Size  -= sizeStep;
703                     const float s = Size * (0.65f + (1.0f * (float)bzfrand()));
704                     const float shift = s * shiftScale;
705 
706                     pos += (shift * dir);
707                     if (pos.z < 0.0f)
708                         continue;
709 
710                     uvCell = (uvCell + 1) % 16;
711                     const float U0 = (uvCell % 4 ) * 0.25f;
712                     const float V0 = (uvCell / 4 ) * 0.25f;
713                     const float U1 = U0 + 0.25f;
714                     const float V1 = V0 + 0.25f;
715 
716                     alpha -= alphaStep;
717                     glColor4f(mainColor[0],mainColor[1],mainColor[2], alpha);
718                     glPopMatrix();
719                     glPushMatrix();
720 
721                     glTranslatef(pos.x, pos.y, pos.z);
722                     RENDERER.getViewFrustum().executeBillboard();
723                     glScalef(s, s, s);
724 
725                     glBegin(GL_TRIANGLE_STRIP);
726                     glTexCoord2f(U0, V0);
727                     glVertex2f(-1.0f, -1.0f);
728                     glTexCoord2f(U1, V0);
729                     glVertex2f(+1.0f, -1.0f);
730                     glTexCoord2f(U0, V1);
731                     glVertex2f(-1.0f, +1.0f);
732                     glTexCoord2f(U1, V1);
733                     glVertex2f(+1.0f, +1.0f);
734                     glEnd();
735                 }
736 
737                 addTriangleCount(shotLength * 2);
738                 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
739                 glPopAttrib(); // revert the texture
740             }
741         }
742         else if (BZDBCache::blend)
743         {
744             // draw corona
745             glBegin(GL_TRIANGLE_STRIP);
746             myColor4fv(mainColor);
747             glVertex2fv(core[1]);
748             myColor4fv(outerColor);
749             glVertex2fv(corona[0]);
750             myColor4fv(mainColor);
751             glVertex2fv(core[2]);
752             myColor4fv(outerColor);
753             glVertex2fv(corona[1]);
754             myColor4fv(mainColor);
755             glVertex2fv(core[3]);
756             myColor4fv(outerColor);
757             glVertex2fv(corona[2]);
758             myColor4fv(mainColor);
759             glVertex2fv(core[4]);
760             myColor4fv(outerColor);
761             glVertex2fv(corona[3]);
762             myColor4fv(mainColor);
763             glVertex2fv(core[5]);
764             myColor4fv(outerColor);
765             glVertex2fv(corona[4]);
766             myColor4fv(mainColor);
767             glVertex2fv(core[6]);
768             myColor4fv(outerColor);
769             glVertex2fv(corona[5]);
770             myColor4fv(mainColor);
771             glVertex2fv(core[7]);
772             myColor4fv(outerColor);
773             glVertex2fv(corona[6]);
774             myColor4fv(mainColor);
775             glVertex2fv(core[8]);
776             myColor4fv(outerColor);
777             glVertex2fv(corona[7]);
778             myColor4fv(mainColor);
779             glVertex2fv(core[1]);
780             myColor4fv(outerColor);
781             glVertex2fv(corona[0]);
782             glEnd(); // 18 verts -> 16 tris
783 
784             // draw core
785             glBegin(GL_TRIANGLE_FAN);
786             myColor4fv(innerColor);
787             glVertex2fv(core[0]);
788             myColor4fv(mainColor);
789             glVertex2fv(core[1]);
790             glVertex2fv(core[2]);
791             glVertex2fv(core[3]);
792             glVertex2fv(core[4]);
793             glVertex2fv(core[5]);
794             glVertex2fv(core[6]);
795             glVertex2fv(core[7]);
796             glVertex2fv(core[8]);
797             glVertex2fv(core[1]);
798             glEnd(); // 10 verts -> 8 tris
799 
800             addTriangleCount(24);
801         }
802         else
803         {
804             // draw corona
805             myColor4fv(coronaColor);
806             myStipple(coronaColor[3]);
807             glBegin(GL_TRIANGLE_STRIP);
808             glVertex2fv(core[1]);
809             glVertex2fv(corona[0]);
810             glVertex2fv(core[2]);
811             glVertex2fv(corona[1]);
812             glVertex2fv(core[3]);
813             glVertex2fv(corona[2]);
814             glVertex2fv(core[4]);
815             glVertex2fv(corona[3]);
816             glVertex2fv(core[5]);
817             glVertex2fv(corona[4]);
818             glVertex2fv(core[6]);
819             glVertex2fv(corona[5]);
820             glVertex2fv(core[7]);
821             glVertex2fv(corona[6]);
822             glVertex2fv(core[8]);
823             glVertex2fv(corona[7]);
824             glVertex2fv(core[1]);
825             glVertex2fv(corona[0]);
826             glEnd(); // 18 verts -> 16 tris
827 
828             // draw core
829             myStipple(1.0f);
830             glBegin(GL_TRIANGLE_FAN);
831             myColor4fv(innerColor);
832             glVertex2fv(core[0]);
833             myColor4fv(mainColor);
834             glVertex2fv(core[1]);
835             glVertex2fv(core[2]);
836             glVertex2fv(core[3]);
837             glVertex2fv(core[4]);
838             glVertex2fv(core[5]);
839             glVertex2fv(core[6]);
840             glVertex2fv(core[7]);
841             glVertex2fv(core[8]);
842             glVertex2fv(core[1]);
843             glEnd(); // 10 verts -> 8 tris
844 
845             myStipple(0.5f);
846 
847             addTriangleCount(24);
848         }
849 
850     }
851 
852     glPopMatrix();
853 
854     if (blackFog)
855         glFogfv(GL_FOG_COLOR, RENDERER.getFogColor());
856 
857     if (RENDERER.isLastFrame())
858     {
859         if (++u == cu)
860         {
861             u = 0;
862             if (++v == cv)
863                 v = 0;
864         }
865     }
866 }
867 
868 // Local Variables: ***
869 // mode: C++ ***
870 // tab-width: 4 ***
871 // c-basic-offset: 4 ***
872 // indent-tabs-mode: nil ***
873 // End: ***
874 // ex: shiftwidth=4 tabstop=4
875