1 module ws.gl.draw;
2 
3 import
4     std.conv,
5     std.algorithm,
6     std.random,
7     ws.exception,
8     ws.draw,
9     ws.cache,
10     ws.gl.gl,
11     ws.gl.context,
12     ws.gl.batch,
13     ws.gl.shader,
14     ws.gl.font,
15     ws.gui.point;
16 
17 
18 class GlDraw: DrawEmpty {
19 
20     GlContext context;
21 
22     this(GlContext context){
23         this.context = context;
24         batchRectTexture = new Batch(context, gl.triangleFan, Batch.vert3 ~ Batch.tex2, [
25             0, 0, 0, 0, 0,
26             1, 0, 0, 1, 0,
27             1, 1, 0, 1, 1,
28             0, 1, 0, 0, 1
29         ]);
30         shaderRectTexture = new Shader(
31                 context,
32                 "2d_texture",
33                 [gl.attributeVertex: "vVertex", gl.attributeTexture: "vTexture0"],
34                 null,
35                 TextureShader.vertex,
36                 TextureShader.fragment
37         );
38         batchRect = new Batch(context, gl.triangleFan, Batch.vert3, [
39             0, 0, 0,
40             1, 0, 0,
41             1, 1, 0,
42             0, 1, 0
43         ]);
44         shaderRect = new Shader(
45                 context,
46                 "2d_rect",
47                 [gl.attributeVertex: "vVertex"],
48                 null,
49                 RectShader.vertex,
50                 RectShader.fragment
51         );
52         batchLine = new Batch(context, gl.lines, Batch.vert3, [0, 0, 0, 0, 10, 0]);
53         shaderLine = new Shader(
54                 context,
55                 "2d_rect",
56                 [gl.attributeVertex: "vVertex"],
57                 null,
58                 RectShader.vertex,
59                 RectShader.fragment
60         );
61         shaderText = new Shader(
62                 context,
63                 "2d_text",
64                 [gl.attributeVertex: "vVertex", gl.attributeTexture: "vTexture0"],
65                 null,
66                 TextShader.vertex,
67                 TextShader.fragment
68         );
69         fonts = new CachedFactory!Font;
70     }
71 
72     override void resize(int[2] size){
73         context.viewport(0,0,size.w,size.h);
74         screen = size.to!(float[2]) ~ 1;
75     }
76 
77 
78     override void setColor(float[3] rgb){
79         color = rgb ~ 1;
80     }
81 
82 
83     override void setColor(float[4] rgba){
84         color = rgba;
85     }
86 
87 
88     override void setFont(string f, int size){
89         font = fonts.get(context, f, size);
90     }
91 
92     override int fontHeight(){
93         return font.height.to!int;
94     }
95 
96     /+
97     override void setFont(Font f){
98         font = f;
99     }
100     +/
101 
102     override void rect(int[2] pos, int[2] size){
103         auto s = activateShader(type.rect);
104         float[3] offset = [pos.x, pos.y, 1];
105         float[3] scale = [size.w, size.h, 1];
106         s["Screen"] = screen;
107         s["Color"] = color;
108         s["Offset"] = offset;
109         s["Scale"] = scale;
110         s["Clip"] = clipStack.length ? clipStack[$-1] : [0, 0, screen.w, screen.h];
111         batchRect.draw();
112     }
113 
114     override void rectOutline(int[2] pos, int[2] size){
115         line(pos, pos.a + [size.w,0]);
116         line(pos, pos.a + [0,size.h]);
117         line(pos.a + [0,size.h], pos.a + size);
118         line(pos.a + [size.w,0], pos.a + size);
119     }
120 
121     override void clip(int[2] pos, int[2] size){
122         if(clipStack.length){
123             auto clipPos = clipStack[$-1][0..2].to!(int[2]);
124             auto clipSize = clipStack[$-1][2..4].to!(int[2]);
125             pos = [pos.x.max(clipPos.x), pos.y.max(clipPos.y)];
126             size = [size.w.min(clipSize.w - (pos.x-clipPos.x)), size.h.min(clipSize.h - (pos.y-clipPos.y))];
127         }
128         clipStack ~= [pos.x, pos.y, size.w, size.h];
129     }
130 
131     override void noclip(){
132         clipStack = clipStack[0..$-1];
133     }
134 
135     /+
136     override void texturedRect(Point pos, Point size){
137         if(!texture)
138             exception("No texture active");
139         auto s = activateShader(type.texture);
140         float[3] offset = [pos.x, pos.y, 0];
141         float[3] scale = [size.x, size.y, 0];
142         s["Screen"] = screen;
143         s["Color"] = color;
144         s["Offset"] = offset;
145         s["Scale"] = scale;
146         s["Image"] = 0;
147         glActiveTexture(GL_TEXTURE0);
148         glBindTexture(GL_TEXTURE_2D, texture.id);
149         batchRectTexture.draw();
150     }
151     +/
152 
153     override void line(int[2] start, int[2] end){
154         auto s = activateShader(type.line);
155         float[3] offset = [start.x+0.25,start.y+0.25,0];
156         float[3] scale = [1,1,0];
157         s["Screen"] = screen;
158         s["Color"] = color;
159         s["Offset"] = offset;
160         s["Scale"] = scale;
161         s["Clip"] = clipStack.length ? clipStack[$-1] : [0, 0, screen.w, screen.h];
162         batchLine.updateVertices([end.x-start.x+0.25, end.y-start.y+0.25, 0], 1);
163         batchLine.draw();
164     }
165 
166     override int width(string text){
167         return text.dtext.map!(a => font[a].advance).sum.to!int;
168     }
169 
170     override int text(int[2] pos, string text, double offset=-0.2){
171         if(!font)
172             exception("no font active");
173         context.enable(GL_BLEND);
174         context.blendFunc(GL_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR);
175         auto offsetRight = max(0.0,-offset)*fontHeight;
176         auto offsetLeft = max(0.0,offset-1)*fontHeight;
177         float x = pos.x - min(1,max(0,offset))*width(text) + offsetRight - offsetLeft;
178         auto s = activateShader(type.text);
179         float[3] scale = [1,1,0];
180         s["Screen"] = screen;
181         s["Image"] = 0;
182         s["Color"] = color;
183         s["Scale"] = scale;
184         s["Clip"] = clipStack.length ? clipStack[$-1] : [0, 0, screen.w, screen.h];
185         float y = pos.y;
186         context.activeTexture(GL_TEXTURE0);
187         foreach(dchar c; text){
188             if(c == '\n'){
189                 x = pos.x;
190                 y -= font.height;
191                 continue;
192             }
193             auto g = font[c];
194             context.bindTexture(GL_TEXTURE_2D, g.tex);
195             float[3] p = [cast(int)x, cast(int)y, 0];
196             s["Offset"] = p;
197             g.vao.draw();
198             x += g.advance;
199         }
200         context.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
201         return width(text);//x.to!int-pos.x;
202     }
203 
204     override int text(int[2] pos, int h, string text, double offset=-0.2){
205         pos[1] += cast(int)((h-font.height)/2.0);
206         return this.text(pos, text, offset);
207     }
208 
209     override void clear(){
210         glClearColor(0,0,0,0);
211         glClear(GL_COLOR_BUFFER_BIT);
212     }
213 
214     override void finishFrame(){
215         context.swapBuffers;
216     }
217 
218     alias Clip = float[4];
219     Clip[] clipStack;
220     float[3] screen = [10,10,1];
221     float[4] color = [1,1,1,1];
222 
223     private:
224 
225         CachedFactory!Font fonts;
226         Font font;
227         enum type {
228             rect = 1,
229             line,
230             text,
231             texture
232         }
233 
234         Shader activateShader(type t){
235             Shader shader;
236             final switch(t){
237                 case type.texture:
238                     shader = shaderRectTexture;
239                     break;
240                 case type.rect:
241                     shader = shaderRect;
242                     break;
243                 case type.line:
244                     shader = shaderLine;
245                     break;
246                 case type.text:
247                     shader = shaderText;
248                     break;
249             }
250             shader.use();
251             return shader;
252         }
253 
254         Batch batchRect;
255         Shader shaderRect;
256         Batch batchRectTexture;
257         Shader shaderRectTexture;
258         Batch batchLine;
259         Shader shaderLine;
260         Shader shaderText;
261 
262 }
263 
264 
265 enum TextShader {
266     vertex = "
267         #version 330
268 
269         in vec4 vVertex;
270         in vec2 vTexture0;
271 
272         uniform vec3 Screen;
273         uniform vec3 Offset;
274         uniform vec3 Scale;
275 
276         smooth out vec2 vVaryingTexCoord;
277 
278         void main(void){
279             vVaryingTexCoord = vTexture0.st;
280             vec4 t = vVertex;
281             t.x = (vVertex.x*Scale.x + Offset.x) / Screen.x * 2 - 1;
282             t.y = (vVertex.y*Scale.y + Offset.y) / Screen.y * 2 - 1;
283             gl_Position = t;
284         }
285     ",
286     fragment = "
287         #version 330
288 
289         uniform sampler2D Image;
290         uniform vec4 Color;
291         uniform vec4 Clip;
292         uniform vec3 Screen;
293         uniform vec3 Offset;
294         uniform vec3 Scale;
295 
296         smooth in vec2 vVaryingTexCoord;
297 
298         layout(location = 0, index = 0) out vec4 color;
299         layout(location = 0, index = 1) out vec4 colorMask;
300 
301         void main(void){
302             vec2 screenPos = gl_FragCoord.xy;
303             if(gl_FragCoord.x < Clip.x || gl_FragCoord.y < Clip.y
304                || gl_FragCoord.x > Clip.x+Clip.z || gl_FragCoord.y > Clip.y+Clip.w)
305                 discard;
306             vec4 pixel = texture(Image, vVaryingTexCoord);
307             if(pixel.r+pixel.g+pixel.b < 0.001)
308                 discard;
309             color = Color;
310             colorMask = Color.a*pixel;
311         }
312     "
313 }
314 
315 
316 enum TextureShader {
317     vertex = "
318         #version 130
319 
320         in vec4 vVertex;
321         in vec2 vTexture0;
322 
323         uniform vec3 Screen;
324         uniform vec3 Offset;
325         uniform vec3 Scale;
326 
327         smooth out vec2 vVaryingTexCoord;
328 
329         void main(void){
330             vVaryingTexCoord = vTexture0.st;
331             vec4 t = vVertex;
332             t.x = (vVertex.x*Scale.x + Offset.x) / Screen.x * 2 - 1;
333             t.y = (vVertex.y*Scale.y + Offset.y) / Screen.y * 2 - 1;
334             gl_Position = t;
335         }
336     ",
337     fragment = "
338         #version 130
339 
340         uniform sampler2D Image;
341         uniform vec4 Color;
342         uniform vec4 Clip;
343         uniform vec3 Screen;
344         uniform vec3 Offset;
345         uniform vec3 Scale;
346 
347         smooth in vec2 vVaryingTexCoord;
348 
349         out vec4 vFragColor;
350 
351         void main(void){
352             vec2 screenPos = gl_FragCoord.xy;
353             if(gl_FragCoord.x < Clip.x || gl_FragCoord.y < Clip.y
354                || gl_FragCoord.x > Clip.x+Clip.z || gl_FragCoord.y > Clip.y+Clip.w)
355                 discard;
356             vec4 color = texture(Image, vVaryingTexCoord);
357             if(color.a < 0.001)
358                 discard;
359             vFragColor = color * Color;
360         }
361     "
362 }
363 
364 
365 enum RectShader {
366     vertex = "
367         #version 130
368 
369         in vec4 vVertex;
370 
371         uniform vec3 Screen;
372         uniform vec3 Offset;
373         uniform vec3 Scale;
374 
375         void main(void){
376             vec4 t = vVertex;
377             t.x = (vVertex.x*Scale.x + Offset.x) / Screen.x * 2 - 1;
378             t.y = (vVertex.y*Scale.y + Offset.y) / Screen.y * 2 - 1;
379             gl_Position = t;
380         }
381     ",
382     fragment = "
383         #version 130
384 
385         uniform vec4 Color;
386         uniform vec4 Clip;
387         uniform vec3 Screen;
388         uniform vec3 Offset;
389         uniform vec3 Scale;
390 
391         out vec4 vFragColor;
392 
393         void main(void){
394             if(gl_FragCoord.x < Clip.x || gl_FragCoord.y < Clip.y
395                || gl_FragCoord.x > Clip.x+Clip.z || gl_FragCoord.y > Clip.y+Clip.w)
396                 discard;
397             if(Color.a < 0.001)
398                 discard;
399             vFragColor = Color;
400         }
401     "
402 }