1 static struct flaretype
2 {
3     int type;             /* flaretex index, 0..5, -1 for 6+random shine */
4     float loc;            /* postion on axis */
5     float scale;          /* texture scaling */
6     uchar alpha;          /* color alpha */
7 } flaretypes[] =
8 {
9     {2,  1.30f, 0.04f, 153}, //flares
10     {3,  1.00f, 0.10f, 102},
11     {1,  0.50f, 0.20f, 77},
12     {3,  0.20f, 0.05f, 77},
13     {0,  0.00f, 0.04f, 77},
14     {5, -0.25f, 0.07f, 127},
15     {5, -0.40f, 0.02f, 153},
16     {5, -0.60f, 0.04f, 102},
17     {5, -1.00f, 0.03f, 51},
18     {-1, 1.00f, 0.30f, 255}, //shine - red, green, blue
19     {-2, 1.00f, 0.20f, 255},
20     {-3, 1.00f, 0.25f, 255}
21 };
22 
23 struct flare
24 {
25     vec o, center;
26     float size;
27     bvec color;
28     int sparkle; // 0 = off, 1 = sparkles and flares, 2 = only sparkles
29 };
30 
31 VAR(IDF_PERSIST, flarelights, 0, 3, 15); // 0 = off, &1 = defined suns, &2 = defined lights, &4 = all suns, &8 = all lights
32 VAR(IDF_PERSIST, flarecutoff, 0, 1000, VAR_MAX);
33 VAR(IDF_PERSIST, flaresize, 1, 100, VAR_MAX);
34 VAR(IDF_PERSIST, flaresundist, 1, 4096, VAR_MAX);
35 VAR(IDF_PERSIST, flareshine, 1, 50, VAR_MAX);
36 FVAR(IDF_PERSIST, flareblend, 0, 0.5f, 1);
37 FVAR(IDF_PERSIST, flareadjust, 0, 0.7f, 1);
38 
39 struct flarerenderer : partrenderer
40 {
41     int maxflares, numflares;
42     unsigned int shinetime;
43     flare *flares;
44 
flarerendererflarerenderer45     flarerenderer(const char *texname, int maxflares)
46         : partrenderer(texname, 3, PT_FLARE|PT_SHADER), maxflares(maxflares), numflares(0), shinetime(0)
47     {
48         flares = new flare[maxflares];
49     }
~flarerendererflarerenderer50     ~flarerenderer()
51     {
52         delete[] flares;
53     }
54 
resetflarerenderer55     void reset()
56     {
57         numflares = 0;
58     }
59 
newflareflarerenderer60     void newflare(const vec &o,  const vec &center, uchar r, uchar g, uchar b, float mod, float size, bool sun, int sparkle)
61     {
62         if(numflares >= maxflares) return;
63         vec target; //occlusion check (neccessary as depth testing is turned off)
64         if(!raycubelos(o, camera1->o, target)) return;
65         flare &f = flares[numflares++];
66         f.o = o;
67         f.center = center;
68         f.size = size;
69         f.color = bvec(uchar(r*mod), uchar(g*mod), uchar(b*mod));
70         f.sparkle = sparkle;
71     }
72 
generateflarerenderer73     bool generate(const vec &o, vec &flaredir, float &mod, float &size, bool sun, float radius)
74     {
75         flaredir = vec(o).sub(camera1->o);
76         if(sun) //fixed size
77         {
78             mod = 1.0f;
79             size = flaresundist*flaresize/100.0f;
80         }
81         else
82         {
83             float len = flaredir.magnitude();
84             if(len > radius) return false;
85             mod = (radius-len)/radius;
86             size = flaresize/10.0f;
87         }
88         return isvisiblesphere(size, o) <= (sun ? VFC_FOGGED : VFC_FULL_VISIBLE);
89     }
90 
addflareflarerenderer91     void addflare(const vec &o, uchar r, uchar g, uchar b, bool sun, bool project, int sparkle)
92     {
93         //frustrum + fog check
94         //find closest point between camera line of sight and flare pos
95         vec flaredir;
96         float mod = 0, size = 0;
97         if(generate(o, flaredir, mod, size, sun || project, flarecutoff))
98             newflare(o, vec(camdir).mul(flaredir.dot(camdir)).add(camera1->o), r, g, b, mod, size, sun, sparkle);
99     }
100 
setupflaresflarerenderer101     void setupflares()
102     {
103         numflares = 0; //regenerate flarelist each frame
104         shinetime = lastmillis/flareshine;
105     }
106 
drawflaresflarerenderer107     void drawflares()
108     {
109         if(flarelights)
110         {
111             const vector<extentity *> &ents = entities::getents();
112             int numents = entities::lastent(ET_SUNLIGHT);
113             loopi(numents)
114             {
115                 extentity &e = *ents[i];
116                 bool sun = false, project = false;
117                 int sparkle = 0;
118                 vec o = e.o;
119                 uchar r = 255, g = 255, b = 255;
120                 float scale = 1.f;
121                 switch(e.type)
122                 {
123                     case ET_LIGHT:
124                         if(flarelights&8 || (flarelights&2 && e.attrs[4]))
125                         {
126                             if(!e.attrs[0] || e.attrs[4]&1) sun = true;
127                             if(!e.attrs[0] || e.attrs[4]&2) sparkle = sun ? 1 : 2;
128                             r = e.attrs[1];
129                             g = e.attrs[2];
130                             b = e.attrs[3];
131                             if(e.attrs[5] > 0) scale = e.attrs[5]/100.f;
132                             break;
133                         }
134                         else continue;
135                     case ET_SUNLIGHT:
136                         if(flarelights&4 || (flarelights&1 && e.attrs[6]))
137                         {
138                             if(!e.attrs[6] || e.attrs[6]&1) sun = true;
139                             if(!e.attrs[6] || e.attrs[6]&2) sparkle = sun ? 1 : 2;
140                             r = e.attrs[2];
141                             g = e.attrs[3];
142                             b = e.attrs[4];
143                             o = vec(camera1->o).add(vec(e.attrs[0]*RAD, (e.attrs[1]+90)*RAD).mul(getworldsize()*2));
144                             project = true;
145                             if(e.attrs[7] > 0) scale = e.attrs[7]/100.f;
146                             break;
147                         }
148                         else continue;
149                     default: continue;
150                 }
151                 vec flaredir;
152                 float mod = 0, size = 0, radius = project ? 0.f : e.attrs[0]*flaresize/100.f;
153                 if(generate(o, flaredir, mod, size, sun || project, radius))
154                     newflare(o, vec(camdir).mul(flaredir.dot(camdir)).add(camera1->o), r, g, b, mod, size*scale, sun, sparkle);
155             }
156         }
157     }
158 
countflarerenderer159     int count()
160     {
161         return numflares;
162     }
163 
hasworkflarerenderer164     bool haswork()
165     {
166         return (numflares != 0) && !glaring && !reflecting  && !refracting;
167     }
168 
renderflarerenderer169     void render()
170     {
171         textureshader->set();
172         glDisable(GL_DEPTH_TEST);
173         preload();
174         if(tex) glBindTexture(GL_TEXTURE_2D, tex->id);
175         gle::defattrib(gle::ATTRIB_VERTEX, 3, GL_FLOAT);
176         gle::defattrib(gle::ATTRIB_TEXCOORD0, 2, GL_FLOAT);
177         gle::defattrib(gle::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE);
178         gle::begin(GL_QUADS);
179         loopi(numflares)
180         {
181             const flare &f = flares[i];
182             float blend = flareblend;
183             vec center = f.center, axis = vec(f.o).sub(center);
184             if(flareadjust > 0)
185             {
186                 float yaw, pitch;
187                 vec dir = vec(f.o).sub(camera1->o).normalize();
188                 vectoyawpitch(dir, yaw, pitch);
189                 yaw -= camera1->yaw;
190                 while(yaw < -180.0f) yaw += 360.0f;
191                 while(yaw >= 180.0f) yaw -= 360.0f;
192                 if(yaw < 0) yaw = -yaw;
193                 blend *= 1-min(yaw/(curfov*0.5f)*flareadjust, 1.f);
194                 pitch -= camera1->pitch;
195                 while(pitch < -180.0f) pitch += 360.0f;
196                 while(pitch >= 180.0f) pitch -= 360.0f;
197                 if(pitch < 0) pitch = -pitch;
198                 blend *= 1-min(pitch/(fovy*0.5f)*flareadjust, 1.f);
199             }
200             bvec4 color(f.color, 255);
201             loopj(f.sparkle ? (f.sparkle != 2 ? 12 : 3) : 9)
202             {
203                 int q = f.sparkle != 2 ? j : j+9;
204                 const flaretype &ft = flaretypes[q];
205                 vec o = vec(axis).mul(ft.loc).add(center);
206                 float sz = ft.scale*f.size;
207                 int tex = ft.type;
208                 if(ft.type < 0) //sparkles - always done last
209                 {
210                     tex = 6+((shinetime+1)%10);
211                     color.r = 0;
212                     color.g = 0;
213                     color.b = 0;
214                     color[-ft.type-1] = f.color[-ft.type-1]; //only want a single channel
215                 }
216                 color.a = uchar(ceilf(ft.alpha*blend));
217                 const float tsz = 0.25f; //flares are aranged in 4x4 grid
218                 float tx = tsz*(tex&0x03), ty = tsz*((tex>>2)&0x03);
219                 gle::attribf(o.x+(-camright.x+camup.x)*sz, o.y+(-camright.y+camup.y)*sz, o.z+(-camright.z+camup.z)*sz);
220                     gle::attribf(tx,     ty+tsz);
221                     gle::attrib(color);
222                 gle::attribf(o.x+( camright.x+camup.x)*sz, o.y+( camright.y+camup.y)*sz, o.z+( camright.z+camup.z)*sz);
223                     gle::attribf(tx+tsz, ty+tsz);
224                     gle::attrib(color);
225                 gle::attribf(o.x+( camright.x-camup.x)*sz, o.y+( camright.y-camup.y)*sz, o.z+( camright.z-camup.z)*sz);
226                     gle::attribf(tx+tsz, ty);
227                     gle::attrib(color);
228                 gle::attribf(o.x+(-camright.x-camup.x)*sz, o.y+(-camright.y-camup.y)*sz, o.z+(-camright.z-camup.z)*sz);
229                     gle::attribf(tx,     ty);
230                     gle::attrib(color);
231             }
232         }
233         gle::end();
234         glEnable(GL_DEPTH_TEST);
235     }
236 
237     //square per round hole - use addflare(..) instead
238     particle *addpart(const vec &o, const vec &d, int fade, int color, float size, float blend = 1, int grav = 0, int collide = 0, physent *pl = NULL) { return NULL; }
239 };
240 static flarerenderer flares("<grey>particles/lensflares", 128);
241