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