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     bool sparkle;
29 };
30 
31 VAR(flarelights, 0, 0, 1);
32 VARP(flarecutoff, 0, 1000, 10000);
33 VARP(flaresize, 20, 100, 500);
34 
35 struct flarerenderer : partrenderer
36 {
37     int maxflares, numflares;
38     unsigned int shinetime;
39     flare *flares;
40 
flarerendererflarerenderer41     flarerenderer(const char *texname, int maxflares)
42         : partrenderer(texname, 3, PT_FLARE|PT_SHADER), maxflares(maxflares), numflares(0), shinetime(0)
43     {
44         flares = new flare[maxflares];
45     }
~flarerendererflarerenderer46     ~flarerenderer()
47     {
48         delete[] flares;
49     }
50 
resetflarerenderer51     void reset()
52     {
53         numflares = 0;
54     }
55 
newflareflarerenderer56     void newflare(vec &o,  const vec &center, uchar r, uchar g, uchar b, float mod, float size, bool sun, bool sparkle)
57     {
58         if(numflares >= maxflares) return;
59         vec target; //occlusion check (neccessary as depth testing is turned off)
60         if(!raycubelos(o, camera1->o, target)) return;
61         flare &f = flares[numflares++];
62         f.o = o;
63         f.center = center;
64         f.size = size;
65         f.color = bvec(uchar(r*mod), uchar(g*mod), uchar(b*mod));
66         f.sparkle = sparkle;
67     }
68 
addflareflarerenderer69     void addflare(vec &o, uchar r, uchar g, uchar b, bool sun, bool sparkle)
70     {
71         //frustrum + fog check
72         if(isvisiblesphere(0.0f, o) > (sun?VFC_FOGGED:VFC_FULL_VISIBLE)) return;
73         //find closest point between camera line of sight and flare pos
74         vec flaredir = vec(o).sub(camera1->o);
75         vec center = vec(camdir).mul(flaredir.dot(camdir)).add(camera1->o);
76         float mod, size;
77         if(sun) //fixed size
78         {
79             mod = 1.0;
80             size = flaredir.magnitude() * flaresize / 100.0f;
81         }
82         else
83         {
84             mod = (flarecutoff-vec(o).sub(center).squaredlen())/flarecutoff;
85             if(mod < 0.0f) return;
86             size = flaresize / 5.0f;
87         }
88         newflare(o, center, r, g, b, mod, size, sun, sparkle);
89     }
90 
makelightflaresflarerenderer91     void makelightflares()
92     {
93         numflares = 0; //regenerate flarelist each frame
94         shinetime = lastmillis/10;
95 
96         if(editmode || !flarelights) return;
97 
98         const vector<extentity *> &ents = entities::getents();
99         extern const vector<int> &checklightcache(int x, int y);
100         const vector<int> &lights = checklightcache(int(camera1->o.x), int(camera1->o.y));
101         loopv(lights)
102         {
103             entity &e = *ents[lights[i]];
104             if(e.type != ET_LIGHT) continue;
105             bool sun = (e.attr1==0);
106             float radius = float(e.attr1);
107             vec flaredir = vec(e.o).sub(camera1->o);
108             float len = flaredir.magnitude();
109             if(!sun && (len > radius)) continue;
110             if(isvisiblesphere(0.0f, e.o) > (sun?VFC_FOGGED:VFC_FULL_VISIBLE)) continue;
111             vec center = vec(camdir).mul(flaredir.dot(camdir)).add(camera1->o);
112             float mod, size;
113             if(sun) //fixed size
114             {
115                 mod = 1.0;
116                 size = len * flaresize / 100.0f;
117             }
118             else
119             {
120                 mod = (radius-len)/radius;
121                 size = flaresize / 5.0f;
122             }
123             newflare(e.o, center, e.attr2, e.attr3, e.attr4, mod, size, sun, sun);
124         }
125     }
126 
countflarerenderer127     int count()
128     {
129         return numflares;
130     }
131 
hasworkflarerenderer132     bool haswork()
133     {
134         return (numflares != 0) && !glaring && !reflecting  && !refracting;
135     }
136 
renderflarerenderer137     void render()
138     {
139         textureshader->set();
140         glDisable(GL_DEPTH_TEST);
141         if(!tex) tex = textureload(texname);
142         glBindTexture(GL_TEXTURE_2D, tex->id);
143         gle::defattrib(gle::ATTRIB_VERTEX, 3, GL_FLOAT);
144         gle::defattrib(gle::ATTRIB_TEXCOORD0, 2, GL_FLOAT);
145         gle::defattrib(gle::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE);
146         gle::begin(GL_QUADS);
147         loopi(numflares)
148         {
149             const flare &f = flares[i];
150             vec center = f.center;
151             vec axis = vec(f.o).sub(center);
152             bvec4 color(f.color, 255);
153             loopj(f.sparkle?12:9)
154             {
155                 const flaretype &ft = flaretypes[j];
156                 vec o = vec(axis).mul(ft.loc).add(center);
157                 float sz = ft.scale * f.size;
158                 int tex = ft.type;
159                 if(ft.type < 0) //sparkles - always done last
160                 {
161                     shinetime = (shinetime + 1) % 10;
162                     tex = 6+shinetime;
163                     color.r = 0;
164                     color.g = 0;
165                     color.b = 0;
166                     color[-ft.type-1] = f.color[-ft.type-1]; //only want a single channel
167                 }
168                 color.a = ft.alpha;
169                 const float tsz = 0.25; //flares are aranged in 4x4 grid
170                 float tx = tsz*(tex&0x03), ty = tsz*((tex>>2)&0x03);
171                 gle::attribf(o.x+(-camright.x+camup.x)*sz, o.y+(-camright.y+camup.y)*sz, o.z+(-camright.z+camup.z)*sz);
172                     gle::attribf(tx,     ty+tsz);
173                     gle::attrib(color);
174                 gle::attribf(o.x+( camright.x+camup.x)*sz, o.y+( camright.y+camup.y)*sz, o.z+( camright.z+camup.z)*sz);
175                     gle::attribf(tx+tsz, ty+tsz);
176                     gle::attrib(color);
177                 gle::attribf(o.x+( camright.x-camup.x)*sz, o.y+( camright.y-camup.y)*sz, o.z+( camright.z-camup.z)*sz);
178                     gle::attribf(tx+tsz, ty);
179                     gle::attrib(color);
180                 gle::attribf(o.x+(-camright.x-camup.x)*sz, o.y+(-camright.y-camup.y)*sz, o.z+(-camright.z-camup.z)*sz);
181                     gle::attribf(tx,     ty);
182                     gle::attrib(color);
183             }
184         }
185         gle::end();
186         glEnable(GL_DEPTH_TEST);
187     }
188 
189     //square per round hole - use addflare(..) instead
190     particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity = 0) { return NULL; }
191 };
192 static flarerenderer flares("<grey>packages/particles/lensflares.png", 64);
193 
194