Line data Source code
1 : /**
2 : * @file postfx.cpp
3 : * @brief screenspace shader post effects
4 : */
5 : #include "../libprimis-headers/cube.h"
6 :
7 : #include "rendergl.h"
8 : #include "renderlights.h"
9 : #include "rendertimers.h"
10 : #include "shader.h"
11 : #include "shaderparam.h"
12 : #include "texture.h"
13 :
14 : #include "interface/console.h"
15 :
16 : class PostFx final
17 : {
18 : public:
19 2 : void cleanuppostfx(bool fullclean)
20 : {
21 2 : if(fullclean && postfxfb)
22 : {
23 0 : glDeleteFramebuffers(1, &postfxfb);
24 0 : postfxfb = 0;
25 : }
26 2 : for(const postfxtex &i : postfxtexs)
27 : {
28 0 : glDeleteTextures(1, &i.id);
29 : }
30 2 : postfxtexs.clear();
31 2 : postfxw = 0;
32 2 : postfxh = 0;
33 2 : }
34 :
35 2 : void clearpostfx()
36 : {
37 2 : postfxpasses.clear();
38 2 : cleanuppostfx(false);
39 2 : }
40 :
41 1 : void addpostfx(const char *name, const int *bind, const int *scale, const char *inputs, const float &x, const float &y, const float &z, const float &w)
42 : {
43 1 : int inputmask = inputs[0] ? 0 : 1,
44 1 : freemask = inputs[0] ? 0 : 1;
45 1 : bool freeinputs = true;
46 1 : for(; *inputs; inputs++)
47 : {
48 0 : if(isdigit(*inputs))
49 : {
50 0 : inputmask |= 1<<(*inputs-'0');
51 0 : if(freeinputs)
52 : {
53 0 : freemask |= 1<<(*inputs-'0');
54 : }
55 : }
56 0 : else if(*inputs=='+')
57 : {
58 0 : freeinputs = false;
59 : }
60 0 : else if(*inputs=='-')
61 : {
62 0 : freeinputs = true;
63 : }
64 : }
65 1 : inputmask &= (1<<numpostfxbinds)-1;
66 1 : freemask &= (1<<numpostfxbinds)-1;
67 1 : addpostfx(name, std::clamp(*bind, 0, numpostfxbinds-1), std::max(*scale, 0), inputmask, freemask, vec4<float>(x, y, z, w));
68 1 : }
69 :
70 1 : void setpostfx(const char *name, const float &x, const float &y, const float &z, const float &w)
71 : {
72 1 : clearpostfx();
73 1 : if(name[0])
74 : {
75 0 : addpostfx(name, 0, 0, 1, 1, vec4<float>(x, y, z, w));
76 : }
77 1 : } //add a postfx shader to the class field, with name & 4d pos vector
78 :
79 0 : GLuint setuppostfx(const GBuffer &buf, int w, int h, GLuint outfbo)
80 : {
81 0 : if(postfxpasses.empty())
82 : {
83 0 : return outfbo;
84 : }
85 0 : if(postfxw != w || postfxh != h)
86 : {
87 0 : cleanuppostfx(false);
88 0 : postfxw = w;
89 0 : postfxh = h;
90 : }
91 0 : for(int i = 0; i < numpostfxbinds; ++i)
92 : {
93 0 : postfxbinds[i] = -1;
94 : }
95 0 : for(postfxtex &i : postfxtexs)
96 : {
97 0 : i.used = -1;
98 : }
99 0 : if(!postfxfb)
100 : {
101 0 : glGenFramebuffers(1, &postfxfb);
102 : }
103 0 : glBindFramebuffer(GL_FRAMEBUFFER, postfxfb);
104 0 : int tex = allocatepostfxtex(0);
105 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, postfxtexs[tex].id, 0);
106 0 : buf.bindgdepth();
107 :
108 0 : postfxbinds[0] = tex;
109 0 : postfxtexs[tex].used = 0;
110 :
111 0 : return postfxfb;
112 : }
113 :
114 0 : void renderpostfx(GLuint outfbo)
115 : {
116 0 : if(postfxpasses.empty())
117 : {
118 0 : return;
119 : }
120 0 : timer *postfxtimer = begintimer("postfx");
121 0 : for(size_t i = 0; i < postfxpasses.size(); i++)
122 : {
123 0 : PostFxPass &p = postfxpasses[i];
124 :
125 0 : int tex = -1;
126 0 : if(!(postfxpasses.size() < i+1))
127 : {
128 0 : glBindFramebuffer(GL_FRAMEBUFFER, outfbo);
129 : }
130 : else
131 : {
132 0 : tex = allocatepostfxtex(p.outputscale);
133 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, postfxtexs[tex].id, 0);
134 : }
135 0 : const int w = tex >= 0 ? std::max(postfxw>>postfxtexs[tex].scale, 1) : postfxw,
136 0 : h = tex >= 0 ? std::max(postfxh>>postfxtexs[tex].scale, 1) : postfxh;
137 0 : glViewport(0, 0, w, h);
138 0 : p.shader->set();
139 0 : LOCALPARAM(params, p.params);
140 0 : int tw = w,
141 0 : th = h,
142 0 : tmu = 0;
143 0 : for(int j = 0; j < numpostfxbinds; ++j)
144 : {
145 0 : if(p.inputs&(1<<j) && postfxbinds[j] >= 0)
146 : {
147 0 : if(!tmu)
148 : {
149 0 : tw = std::max(postfxw>>postfxtexs[postfxbinds[j]].scale, 1);
150 0 : th = std::max(postfxh>>postfxtexs[postfxbinds[j]].scale, 1);
151 : }
152 : else
153 : {
154 0 : glActiveTexture(GL_TEXTURE0 + tmu);
155 : }
156 0 : glBindTexture(GL_TEXTURE_RECTANGLE, postfxtexs[postfxbinds[j]].id);
157 0 : ++tmu;
158 : }
159 : }
160 0 : if(tmu)
161 : {
162 0 : glActiveTexture(GL_TEXTURE0);
163 : }
164 0 : screenquad(tw, th);
165 0 : for(int j = 0; j < numpostfxbinds; ++j)
166 : {
167 0 : if(p.freeinputs&(1<<j) && postfxbinds[j] >= 0)
168 : {
169 0 : postfxtexs[postfxbinds[j]].used = -1;
170 0 : postfxbinds[j] = -1;
171 : }
172 : }
173 0 : if(tex >= 0)
174 : {
175 0 : if(postfxbinds[p.outputbind] >= 0)
176 : {
177 0 : postfxtexs[postfxbinds[p.outputbind]].used = -1;
178 : }
179 0 : postfxbinds[p.outputbind] = tex;
180 0 : postfxtexs[tex].used = p.outputbind;
181 : }
182 : }
183 0 : endtimer(postfxtimer);
184 : }
185 :
186 : private:
187 : static constexpr int numpostfxbinds = 10;
188 :
189 0 : int allocatepostfxtex(int scale)
190 : {
191 0 : for(size_t i = 0; i < postfxtexs.size(); i++)
192 : {
193 0 : const postfxtex &t = postfxtexs[i];
194 0 : if(t.scale==scale && t.used < 0)
195 : {
196 0 : return i;
197 : }
198 : }
199 0 : postfxtex t;
200 0 : t.scale = scale;
201 0 : glGenTextures(1, &t.id);
202 0 : createtexture(t.id, std::max(postfxw>>scale, 1), std::max(postfxh>>scale, 1), nullptr, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE);
203 0 : postfxtexs.push_back(t);
204 0 : return postfxtexs.size()-1;
205 : }
206 :
207 : //adds to the global postfxpasses vector a postfx by the given name
208 1 : bool addpostfx(const char *name, int outputbind, int outputscale, uint inputs, uint freeinputs, const vec4<float> ¶ms)
209 : {
210 1 : if(!*name)
211 : {
212 1 : return false;
213 : }
214 0 : Shader *s = useshaderbyname(name);
215 0 : if(!s)
216 : {
217 0 : conoutf(Console_Error, "no such postfx shader: %s", name);
218 0 : return false;
219 : }
220 0 : PostFxPass p;
221 0 : p.shader = s;
222 0 : p.outputbind = outputbind;
223 0 : p.outputscale = outputscale;
224 0 : p.inputs = inputs;
225 0 : p.freeinputs = freeinputs;
226 0 : p.params = params;
227 0 : postfxpasses.push_back(p);
228 0 : return true;
229 : }
230 :
231 : struct postfxtex final
232 : {
233 : GLuint id;
234 : int scale, used;
235 0 : postfxtex() : id(0), scale(0), used(-1) {}
236 : };
237 : std::vector<postfxtex> postfxtexs;
238 : std::array<int, numpostfxbinds> postfxbinds;
239 : GLuint postfxfb = 0;
240 : int postfxw = 0,
241 : postfxh = 0;
242 : struct PostFxPass final
243 : {
244 : Shader *shader;
245 : vec4<float> params;
246 : uint inputs, freeinputs;
247 : int outputbind, outputscale;
248 :
249 0 : PostFxPass() : shader(nullptr), inputs(1), freeinputs(1), outputbind(0), outputscale(0) {}
250 : };
251 : std::vector<PostFxPass> postfxpasses;
252 : };
253 :
254 : namespace
255 : {
256 : PostFx pfx;
257 :
258 1 : void addpostfxcmd(const char *name, const int *bind, const int *scale, const char *inputs, const float *x, const float *y, const float *z, const float *w)
259 : {
260 1 : pfx.addpostfx(name, bind, scale, inputs, *x, *y, *z, *w);
261 1 : }
262 :
263 1 : void clearpostfx()
264 : {
265 1 : pfx.clearpostfx();
266 1 : }
267 :
268 1 : void setpostfx(const char *name, const float *x, const float *y, const float *z, const float *w)
269 : {
270 1 : pfx.setpostfx(name, *x, *y, *z, *w);
271 1 : }
272 : }
273 :
274 : //extern functions
275 :
276 0 : GLuint setuppostfx(const GBuffer &buf, int w, int h, GLuint outfbo)
277 : {
278 0 : return pfx.setuppostfx(buf, w, h, outfbo);
279 : }
280 :
281 0 : void renderpostfx(GLuint outfbo)
282 : {
283 0 : pfx.renderpostfx(outfbo);
284 0 : }
285 :
286 0 : void cleanuppostfx(bool fullclean)
287 : {
288 0 : pfx.cleanuppostfx(fullclean);
289 0 : }
290 :
291 1 : void initpostfxcmds()
292 : {
293 1 : addcommand("clearpostfx", reinterpret_cast<identfun>(clearpostfx), "", Id_Command);
294 1 : addcommand("addpostfx", reinterpret_cast<identfun>(addpostfxcmd), "siisffff", Id_Command);
295 1 : addcommand("setpostfx", reinterpret_cast<identfun>(setpostfx), "sffff", Id_Command);
296 1 : }
|