1 module grape.renderer;
2 
3 import derelict.opengl3.gl3;
4 
5 import std.math;
6 import std.stdio;
7 import std.traits;
8 import std.conv;
9 import std.algorithm;
10 import std.array;
11 import std.range;
12 
13 import grape.buffer;
14 import grape.shader;
15 import grape.window;
16 import grape.scene;
17 import grape.camera;
18 import grape.mesh;
19 import grape.geometry;
20 import grape.material;
21 import grape.filter;
22 import grape.math;
23 
24 public import grape.image : ImageRenderer;
25 public import grape.font : FontRenderer;
26 
27 class Renderer {
28   public:
29     this() {
30       _ibo = new IBO;
31       for (int i; i<MaxNumVBO; ++i) {
32         _vboList ~= new VBO;
33       }
34 
35       _renderImplCaller["shader"] = (program, geometry, material, camera) { render_impl_shader(program, geometry, material, camera); };
36       _renderImplCaller["color"] = (program, geometry, material, camera) { render_impl_color(program, geometry, material, camera); };
37       _renderImplCaller["diffuse"] = (program, geometry, material, camera) { render_impl_diffuse(program, geometry, material, camera); };
38       _renderImplCaller["ads"] = (program, geometry, material, camera) { render_impl_ads(program, geometry, material, camera); };
39     }
40 
41     void enable_smooth(in string[] names...) {
42       if (cast(int)names.length == 0) {
43         glEnable(GL_POLYGON_SMOOTH);
44         glEnable(GL_LINE_SMOOTH);
45         return;
46       }
47 
48       foreach (name; names) {
49         if (name == "polygon") glEnable(GL_POLYGON_SMOOTH);
50         else if (name == "line") glEnable(GL_LINE_SMOOTH);
51         else writeln("Warning: " ~ name ~ " is not a smooth parameter's name. Check the args of the enable_smooth()");
52       }
53     }
54 
55     void enable_depth() {
56       glEnable(GL_DEPTH_TEST);
57     }
58 
59     void disable_smooth(in string[] names...) {
60       if (cast(int)names.length == 0) {
61         glDisable(GL_POLYGON_SMOOTH);
62         glDisable(GL_LINE_SMOOTH);
63         return;
64       }
65 
66       foreach (name; names) {
67         if (name == "polygon") glDisable(GL_POLYGON_SMOOTH);
68         else if (name == "line") glDisable(GL_LINE_SMOOTH);
69         else writeln("Warning: " ~ name ~ " is not a smooth parameter's name. Check your disable_smooth()");
70       }
71     }
72 
73     void disable_depth() {
74       glDisable(GL_DEPTH_TEST);
75     }
76 
77     void set_point_size(in float size) {
78       glPointSize(size);
79     }
80 
81     void set_MaxNumVBO(in int n) {
82       MaxNumVBO = n;
83       _vboList.length = 0;
84       for (int i; i<MaxNumVBO; ++i) {
85         _vboList ~= new VBO;
86       }
87     }
88 
89     void render(Scene scene, Camera camera) {
90       foreach (i, mesh; scene.meshes) {
91         auto geometry = mesh.geometry;
92         auto material = mesh.material;
93         auto program = material.program;
94         program.use();
95 
96         _renderImplCaller[material.name](program, geometry, material, camera);
97       }
98     }
99 
100     void render(Texture texture, Material material = new TextureMaterial) {
101       auto program = material.program;
102       program.use();
103 
104       float[] position = [ -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0 ];
105       float[] texCoord = [ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0 ];
106       int[] index = [ 0, 1, 2, 0, 2, 3 ];
107 
108       _ibo.create(index);
109       _vboList[0].set(program, position, "position", 2, 0);
110       _vboList[1].set(program, texCoord, "texCoord", 2, 1);
111 
112       UniformLocationN.attach(program, "tex", 0, "1i");
113 
114       texture.texture_scope({
115         auto drawModePtr = material.params["drawMode"].peek!(DrawMode);
116         _ibo.draw(*drawModePtr);
117       });
118     }
119 
120     /**
121      * Viewportの設定
122      *
123      * 内部でOpenGLのglViewport関数を呼んでいるだけです。
124      * x: 左下のx座標
125      * y: 左下のy座標
126      * w: 画面の幅
127      * h: 画面の高さ
128      */
129     void viewport(in int x, in int y, in int w, in int h) {
130       glViewport(x, y, w, h);
131     }
132 
133   private:
134     void render_impl_shader(ShaderProgram program, Geometry geometry, Material material, Camera camera) {
135       float[] position;
136 
137       // VBO: Position
138       foreach (vec3; geometry.vertices) {
139         position ~= vec3.coord;
140       }
141 
142       // IBO Setting
143       _ibo.create(geometry.indices);
144 
145       // Attach VBOs to the program
146       _vboList[0].set(program, position, "position", 3, 0);
147       auto attributes = *material.params["attributes"].peek!(AttributeType[string][string]);
148       int i = 1;
149       foreach(name, data; attributes) {
150         if (name == "position") break;
151         _vboList[i].set(program, *data["value"].peek!(float[]), name, *data["type"].peek!(int), i);
152         ++i;
153       }
154 
155       // Uniform Setting
156       //UniformLocationN.attach(program, "pvmMatrix", camera.pvMat4.mat, "mat4fv");
157       auto uniforms = *material.params["uniforms"].peek!(UniformType[string][string]);
158       foreach(name, data; uniforms) {
159         if (name == "pvmMatrix") break;
160         auto value = data["value"];
161         switch (value.type.toString) {
162           case "int":
163             UniformLocationN.attach(program, name, *value.peek!(int), *data["type"].peek!(string));
164             break;
165           case "float":
166             UniformLocationN.attach(program, name, *value.peek!(float), *data["type"].peek!(string));
167             break;
168           case "float[2]":
169             UniformLocationN.attach(program, name, *value.peek!(float[2]), *data["type"].peek!(string));
170             break;
171           case "float[8]":
172             UniformLocationN.attach(program, name, *value.peek!(float[8]), *data["type"].peek!(string), 8);
173             break;
174           case "float[16]":
175             UniformLocationN.attach(program, name, *value.peek!(float[16]), *data["type"].peek!(string));
176             break;
177           default:
178             writeln("ShaderMaterial.uniforms setting is wrong");
179             break;
180         }
181       }
182 
183       (*material.params["map"].peek!(Texture)).texture_scope({
184         auto drawModePtr = material.params["drawMode"].peek!(DrawMode);
185         _ibo.draw(*drawModePtr);
186       });
187     }
188 
189     void render_impl_color(ShaderProgram program, Geometry geometry, Material material, Camera camera) {
190       float[] position;
191       float[] color;
192 
193       // VBO: Position
194       foreach (vec3; geometry.vertices) {
195         position ~= vec3.coord;
196       }
197 
198       // VBO: Color
199       auto colorPtr = material.params["color"].peek!(int[]);
200       auto colorRGB = map!(x => x > ColorMax ? ColorMax : x)(map!(to!float)(*colorPtr)).array;
201       float[3] tmp = colorRGB[] / ColorMax;
202       float[4] colorBase = tmp ~ 1.0;
203       color = colorBase.cycle.take(colorBase.length * geometry.vertices.length).array;
204 
205       // IBO Setting
206       _ibo.create(geometry.indices);
207 
208       // Attach VBOs to the program
209       _vboList[0].set(program, position, "position", 3, 0);
210       _vboList[1].set(program, color, "color", 4, 1);
211 
212       // Uniform Setting
213       UniformLocationN.attach(program, "pvmMatrix", camera.pvMat4.mat, "mat4fv");
214 
215       // Wireframe Checking
216       auto wireframePtr = material.params["wireframe"].peek!(bool);
217       bool wireframe = *wireframePtr;
218       if (wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
219       scope(exit) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
220 
221       auto drawModePtr = material.params["drawMode"].peek!(DrawMode);
222       _ibo.draw(*drawModePtr);
223     }
224 
225     void render_impl_diffuse(ShaderProgram program, Geometry geometry, Material material, Camera camera) {
226       float[] position;
227       float[] normal;
228       float[] color;
229 
230       // VBO: Position
231       foreach (vec3; geometry.vertices) {
232         position ~= vec3.coord;
233       }
234 
235       // VBO: Normal
236       foreach (vec3; geometry.normals) {
237         normal ~= vec3.coord;
238       }
239 
240       // VBO: Color
241       auto colorPtr = material.params["color"].peek!(int[]);
242       auto colorRGB = map!(x => x > ColorMax ? ColorMax : x)(map!(to!float)(*colorPtr)).array;
243       float[3] tmp = colorRGB[] / ColorMax;
244       float[4] colorBase = tmp ~ 1.0;
245       color = colorBase.cycle.take(colorBase.length * geometry.vertices.length).array;
246 
247       // IBO Setting
248       _ibo.create(geometry.indices);
249 
250       // Attach VBOs to the program
251       _vboList[0].set(program, position, "position", 3, 0);
252       _vboList[1].set(program, color, "color", 4, 1);
253       _vboList[2].set(program, normal, "normal", 3, 2);
254 
255       // Uniform Setting
256       UniformLocationN.attach(program, "pvmMatrix", camera.pvMat4.mat, "mat4fv");
257       UniformLocationN.attach(program, "invMatrix", camera.pvMat4.inverse.mat, "mat4fv");
258       // TODO sceneの中にlight置くようにする
259       UniformLocationN.attach(program, "lightPosition", [2.0f, 2.0f, -2.0f], "3fv");
260 
261       // Wireframe Checking
262       auto wireframePtr = material.params["wireframe"].peek!(bool);
263       bool wireframe = *wireframePtr;
264       if (wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
265       scope(exit) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
266 
267       auto drawModePtr = material.params["drawMode"].peek!(DrawMode);
268       _ibo.draw(*drawModePtr);
269     }
270 
271     void render_impl_ads(ShaderProgram program, Geometry geometry, Material material, Camera camera) {
272       float[] position;
273       float[] color;
274       float[] normal;
275 
276       // VBO: Position
277       foreach (vec3; geometry.vertices) {
278         position ~= vec3.coord;
279       }
280 
281       // VBO: Color
282       auto colorPtr = material.params["color"].peek!(int[]);
283       auto colorRGB = map!(x => x > ColorMax ? ColorMax : x)(map!(to!float)(*colorPtr)).array;
284       float[3] tmp = colorRGB[] / ColorMax;
285       float[4] colorBase = tmp ~ 1.0;
286       color = colorBase.cycle.take(colorBase.length * geometry.vertices.length).array;
287 
288       // VBO: Normal
289       foreach (vec3; geometry.normals) {
290         normal ~= vec3.coord;
291       }
292 
293       // IBO Setting
294       _ibo.create(geometry.indices);
295 
296       // Attach VBOs to the program
297       _vboList[0].set(program, position, "position", 3, 0);
298       _vboList[1].set(program, color, "color", 4, 1);
299       _vboList[2].set(program, normal, "normal", 3, 2);
300 
301       // Uniform: ambientColor
302       auto ambientColorPtr = material.params["ambientColor"].peek!(int[]);
303       auto ambientColorRGB = map!(x => x > ColorMax ? ColorMax : x)(map!(to!float)(*ambientColorPtr)).array;
304       float[3] tmp2 = ambientColorRGB[] / ColorMax;
305       float[4] ambientColor = tmp2 ~ 1.0;
306 
307       // Uniform Setting
308       UniformLocationN.attach(program, "pvmMatrix", camera.pvMat4.mat, "mat4fv");
309       UniformLocationN.attach(program, "invMatrix", camera.pvMat4.inverse.mat, "mat4fv");
310       // TODO sceneの中にlight置くようにする
311       UniformLocationN.attach(program, "lightPosition", [2.0f, 2.0f, -2.0f], "3fv");
312       // TODO camera実装してcameraの位置入れる
313       UniformLocationN.attach(program, "eyePosition", [0.0f, 1.0f, 3.0f], "3fv");
314       UniformLocationN.attach(program, "ambientColor", ambientColor, "4fv");
315 
316       // Wireframe Checking
317       auto wireframePtr = material.params["wireframe"].peek!(bool);
318       bool wireframe = *wireframePtr;
319       if (wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
320       scope(exit) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
321 
322       auto drawModePtr = material.params["drawMode"].peek!(DrawMode);
323       _ibo.draw(*drawModePtr);
324     }
325 
326     static immutable ColorMax = 255;
327     int MaxNumVBO = 10;
328     VBO[] _vboList;
329     IBO _ibo;
330     static void delegate(ShaderProgram, Geometry, Material, Camera)[string] _renderImplCaller;
331 }
332 
333 /**
334  * 描画クラス
335  *
336  * デフォルトセットのRendererじゃ物足りない、自作のシェーダを使いたい
337  * 等といった時にRendererを継承して新たなRendererのSubClass作成してください。
338  */
339 deprecated abstract class Old_Renderer {
340   public:
341     /**
342      * Rendererの初期化
343      *
344      * Rendererを継承したSubClassは必ずこの関数をコンストラクタで呼ぶ必要があります。
345      * dg:       Shaderのソース
346      * locNames: Shaderのattributesの名前の配列
347      * strides:  Shaderのattributesのストライド
348      * drawMode: 描画モード
349      *
350      * TODO:
351      * _iboはここで初期化されるべき
352      * 引数を減らしたい
353      */
354     final void init(in void delegate(out string, out string) dg, in string[] locNames, in int[] strides, in DrawMode drawMode) {
355       assert(strides.length == locNames.length);
356 
357       init_program(dg);
358       _vboHdr = new VBOHdr(strides.length, _program); // TODO Detect the number of attributes from a ShaderSource.
359       _uniLoc = new UniformLocation(_program);
360       _locNames = locNames.dup;
361       _strides = strides.dup;
362       _drawMode = drawMode;
363     }
364 
365     final void set_vbo(in float[][] list...) {
366       _program.use();
367       _vboHdr.create_vbo(list);
368       _vboHdr.enable_vbo(_locNames, _strides);
369     }
370 
371     /**
372      * IBOをセット
373      *
374      * IBOを使って描画する場合、この関数を呼ぶ必要
375      * _ibo must be initialized before calling this function, or cause segv.
376      *
377      * TODO:
378      * final修飾子つけるか
379      */
380     void set_ibo(in int[] index) {
381       _program.use();
382       _ibo.create(index);
383     }
384 
385     /**
386      * Uniform変数のセット
387      *
388      * name:    uniformの場所の名前
389      * value:   セットする値
390      * type:
391      * num:
392      */
393     final void set_uniform(T)(in string name, in T value, in string type, in int num=1) {
394       _program.use();
395       _uniLoc.attach(name, value, type, num);
396     }
397 
398     /**
399      * 描画関数
400      *
401      * SubClassで必ずoverrideする必要があります。
402      *
403      * TODO:
404      * ImageRendererでこれ使ってない
405      */
406     abstract void render();
407 
408   protected:
409     ShaderProgram _program;
410     DrawMode _drawMode;
411     IBO _ibo; // Must be initialized in SubClass when rendering model using IBO.
412 
413   private:
414     final void init_program(in void delegate(out string, out string) dg) {
415       dg(_vShader, _fShader);
416       Shader vs = new Shader(ShaderType.Vertex, _vShader);
417       Shader fs = new Shader(ShaderType.Fragment, _fShader);
418       _program = new ShaderProgram(vs, fs);
419     }
420 
421     string _vShader;
422     string _fShader;
423     UniformLocation _uniLoc;
424     VBOHdr _vboHdr;
425 
426     // TODO delete
427     string[] _locNames;
428     int[] _strides;
429 }
430 
431 deprecated class FilterRenderer : Old_Renderer {
432   public:
433     this() {
434       string[] locNames = [ "pos", "texCoord" ];
435       int[] strides = [ 3, 2 ];
436       mixin FilterShaderSource;
437       init(FilterShader, locNames, strides, DrawMode.Triangles);
438 
439       init_vbo();
440       init_ibo();
441       _program.use();
442       set_uniform("tex", 0, "1i");
443     }
444 
445     override void render() {
446       _program.use();
447       set_vbo(_mesh, _texCoord);
448       _ibo.draw(_drawMode);
449     }
450 
451   private:
452     void init_vbo() {
453       _mesh = [ -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0 ];
454       _texCoord = [ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0 ];
455     }
456 
457     void init_ibo() {
458       int[] index = [ 0, 1, 2, 0, 2, 3 ];
459       _ibo = new IBO;
460       _ibo.create(index);
461     }
462 
463     float[] _mesh;
464     float[] _texCoord;
465 }
466 
467 /**
468  * GaussBlur用のRenderer
469  *
470  * ユーザーが使うことはまずないと思われる
471  *
472  * TODO:
473  * BlurFilter,GlowFilterの内部に移動させるか
474  */
475 deprecated class GaussianRenderer : Old_Renderer {
476   public:
477     this(in float[2] resolution) {
478       string[] locNames = [ "pos", "texCoord" ];
479       int[] strides = [ 2, 2 ];
480       mixin GaussianShaderSource;
481       init(GaussianShader, locNames, strides, DrawMode.Triangles);
482 
483       init_vbo();
484       init_ibo();
485 
486       _program.use();
487       float[8] weight = gauss_weight(50.0);
488       set_uniform("tex", 0, "1i");
489       set_uniform("weight", weight, "1fv", 8);
490       set_uniform("resolution", resolution, "2fv");
491     }
492 
493     void set_type(in int type) {
494       _program.use();
495       set_uniform("type", type, "1i");
496     }
497 
498     override void render() {
499       _program.use();
500       set_vbo(_mesh, _texCoord);
501       _ibo.draw(_drawMode);
502     }
503 
504   private:
505     void init_vbo() {
506       _mesh = [ -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0 ];
507       _texCoord = [ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0 ];
508     }
509 
510     void init_ibo() {
511       int[] index = [ 0, 1, 2, 0, 2, 3 ];
512       _ibo = new IBO;
513       _ibo.create(index);
514     }
515 
516     float[8] gauss_weight(in float eRange) {
517       float[8] weight;
518       float t = 0.0;
519       float d = eRange^^2 / 100;
520       for (int i=0; i<weight.length; ++i) {
521         float r = 1.0 + 2.0*i;
522         float w = exp(-0.5 * r^^2 / d);
523         weight[i] = w;
524         if (i > 0) w *= 2.0;
525           t += w;
526       }
527       for (int i=0; i<weight.length; ++i){
528         weight[i] /= t;
529       }
530       return weight;
531     }
532 
533     float[] _mesh;
534     float[] _texCoord;
535 }
536