1 module grape.effectComposer;
2 
3 import std.variant;
4 import std.stdio;
5 import std.math;
6 import grape.renderer;
7 import grape.scene;
8 import grape.camera;
9 import grape.shader;
10 import grape.buffer;
11 import grape.geometry;
12 import grape.material;
13 import grape.mesh;
14 import derelict.opengl3.gl3;
15 
16 class EffectComposer {
17   public:
18     this(Renderer renderer) {
19       _renderer = renderer;
20 
21       _width = WINDOW_WIDTH;
22       _height = WINDOW_HEIGHT;
23     }
24 
25     void add_pass(Pass pass) {
26       _passes ~= pass;
27 
28       auto tmp = new Texture;
29       tmp.create(_width, _height, null, GL_RGBA);
30       _textures ~= tmp;
31     }
32 
33     void render() {
34       foreach(i, pass; _passes) {
35         auto writeTexture = _textures[i];
36         auto readTexture = _textures[(i==0) ? i : i-1];
37 
38         pass.render(_renderer, writeTexture, readTexture);
39       }
40       _renderer.render(this.get_texture);
41     }
42 
43     void reset() {
44       _passes.length = 0;
45     }
46 
47     void set_size(in int width, in int height) {
48       _width = width;
49       _height = height;
50     }
51 
52     Texture get_texture(in size_t i) {
53       assert(i < _textures.length);
54       return _textures[i];
55     }
56 
57     Texture get_texture() {
58       return _textures[$-1];
59     }
60 
61   private:
62     Renderer _renderer;
63     Pass[] _passes;
64     Texture[] _textures;
65     int _width, _height;
66 }
67 
68 class Pass {
69   public:
70     this() {
71       _fbo = new FBO;
72     }
73 
74   protected:
75     abstract void render(Renderer, Texture, Texture);
76 
77     void create_fbo(Texture writeTexture) {
78       _fbo.create(writeTexture);
79     }
80 
81     void binded_scope(in void delegate() dg, in int width, in int height) {
82       _fbo.binded_scope({
83         GLenum[] drawBufs = [GL_COLOR_ATTACHMENT0];
84         glDrawBuffers(1, drawBufs.ptr);
85 
86         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
87         glEnable(GL_BLEND);
88         glBlendFunc(GL_ONE, GL_ZERO); // DefaultBlend
89         glViewport(0, 0, width, height);
90         dg();
91         glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
92         glDisable(GL_BLEND);
93       });
94     }
95 
96     Scene _scene;
97     Camera _camera;
98     PlaneGeometry _geometry;
99     Mesh _screen;
100 
101   private:
102     FBO _fbo;
103 }
104 
105 class RenderPass : Pass {
106   public:
107     this(Scene scene, Camera camera) {
108       _scene = scene;
109       _camera = camera;
110     }
111 
112     override void render(Renderer renderer, Texture writeTexture, Texture readTexture) {
113       create_fbo(writeTexture);
114 
115       binded_scope({
116         renderer.render(_scene, _camera);
117       }, writeTexture.w, writeTexture.h);
118     }
119 }
120 
121 class BlurPass : Pass {
122   public:
123     this(int width, int height, in float weight=50.0) {
124       _widthBlurTexture = new Texture;
125 
126       _scene = new Scene;
127       _camera = new OrthographicCamera(-cast(float)width/height, cast(float)width/height, -1, 1, 1, 100);
128       _geometry = new PlaneGeometry(2, 2);
129       float[2] resolution = [ WINDOW_WIDTH, WINDOW_HEIGHT ];
130       _material = new ShaderMaterial(
131         "vertexShader", vertexShaderSource,
132         "fragmentShader", fragmentShaderSource,
133         "uniforms", [
134           "tex": [ "type": UniformType("1i"), "value": UniformType(0) ],
135           "weight": [ "type": UniformType("1fv"), "value": UniformType(gauss_weight(weight)) ],
136           "type": [ "type": UniformType("1i"), "value": UniformType(0) ],
137           "resolution": [ "type": UniformType("2fv"), "value": UniformType(resolution) ]
138         ],
139         "attributes", [
140           "texCoord": [ "type": AttributeType(2), "value": AttributeType([ 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f ]) ]
141         ]
142       );
143       _screen = new Mesh(_geometry, _material);
144       _scene.add(_screen);
145     }
146 
147     override void render(Renderer renderer, Texture writeTexture, Texture readTexture) {
148       auto widthBlur = {
149         _widthBlurTexture.create(readTexture.w, readTexture.h, null, GL_RGBA);
150         create_fbo(_widthBlurTexture);
151         _material.set_param("map", readTexture);
152         _material.set_uniform("type", 0);
153 
154         binded_scope({
155           renderer.render(_scene, _camera);
156         }, _widthBlurTexture.w, _widthBlurTexture.h);
157       };
158 
159       auto heightBlur = {
160         create_fbo(writeTexture);
161         _material.set_param("map", _widthBlurTexture);
162         _material.set_uniform("type", 1);
163 
164         binded_scope({
165           renderer.render(_scene, _camera);
166         }, writeTexture.w, writeTexture.h);
167       };
168 
169      widthBlur();
170      heightBlur();
171     }
172 
173   private:
174     float[8] gauss_weight(in float eRange) {
175       float[8] weight;
176       float t = 0.0;
177       float d = eRange^^2 / 100;
178       for (int i=0; i<weight.length; ++i) {
179         float r = 1.0 + 2.0*i;
180         float w = exp(-0.5 * r^^2 / d);
181         weight[i] = w;
182         if (i > 0) w *= 2.0;
183           t += w;
184       }
185       for (int i=0; i<weight.length; ++i){
186         weight[i] /= t;
187       }
188       return weight;
189     }
190 
191     static immutable vertexShaderSource = q{
192       attribute vec3 position;
193       attribute vec2 texCoord;
194       varying vec2 vTexCoord;
195 
196       void main() {
197         vTexCoord = texCoord;
198         gl_Position = vec4(position, 1.0);
199       }
200     };
201 
202     static immutable fragmentShaderSource = q{
203       uniform sampler2D tex;
204       uniform float weight[8];
205       uniform int type;
206       uniform vec2 resolution;
207       varying vec2 vTexCoord;
208 
209       void main() {
210         vec2 t = vec2(1.0) / resolution;
211         vec4 color = texture(tex, vTexCoord) * weight[0];
212 
213         if (type == 0) {
214           for (int i=1; i<8; ++i) {
215             color += texture(tex, (gl_FragCoord.xy + vec2(-1*i, 0)) * t) * weight[i];
216             color += texture(tex, (gl_FragCoord.xy + vec2(1*i, 0)) * t) * weight[i];
217           }
218 
219         } else if (type == 1) {
220           for (int i=1; i<8; ++i) {
221             color += texture(tex, (gl_FragCoord.xy + vec2(0, -1*i)) * t) * weight[i];
222             color += texture(tex, (gl_FragCoord.xy + vec2(0, 1*i)) * t) * weight[i];
223           }
224         }
225 
226         gl_FragColor = color;
227       }
228     };
229 
230     ShaderMaterial _material;
231     Texture _widthBlurTexture;
232     int _width, _height;
233 }
234 
235 class GlowPass : Pass {
236   public:
237     this(in int width, in int height, in int resX, in int resY) {
238       _blurPass = new BlurPass(width, height);
239       _blurTexture= new Texture;
240       _blurTexture.create(WINDOW_WIDTH, WINDOW_HEIGHT, null, GL_RGBA);
241     }
242 
243     override void render(Renderer renderer, Texture writeTexture, Texture readTexture) {
244       create_fbo(writeTexture);
245       _blurPass.render(renderer, _blurTexture, readTexture);
246 
247       binded_scope({
248         glBlendFunc(GL_ONE, GL_ONE);
249         renderer.render(readTexture);
250         renderer.render(_blurTexture);
251       }, writeTexture.w, writeTexture.h);
252     }
253 
254   private:
255     TextureMaterial _material;
256     BlurPass _blurPass;
257     Texture _blurTexture;
258 }
259 
260 class ShaderPass : Pass {
261   public:
262     this(Shader vShader, Shader fShader) {
263     }
264 
265     override void render(Renderer renderer, Texture writeTexture, Texture readTexture) {
266     }
267 
268   private:
269 }
270