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 }