1 /*
2  * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 module demo.ChipmunkDemoTextSupport;
23 
24 import core.stdc.stdlib;
25 
26 import glad.gl.all;
27 
28 import demo.dchip;
29 
30 import demo.ChipmunkDemoShaderSupport;
31 import demo.VeraMoBd_ttf_sdf;
32 import demo.types;
33 
34 //#define ChipmunkDemoTextDrawString(...)
35 
36 //#define Scale 3.0f
37 enum Scale = 0.70f;
38 enum LineHeight = 18.0f * Scale;
39 
40 __gshared GLuint program;
41 __gshared GLuint texture;
42 
43 struct v2f
44 {
45     GLfloat x = 0, y = 0;
46 }
47 
48 struct Vertex
49 {
50     v2f vertex, tex_coord;
51     Color color;
52 }
53 
54 struct Triangle
55 {
56     Vertex a, b, c;
57 }
58 
59 __gshared GLuint vao = 0;
60 __gshared GLuint vbo = 0;
61 
62 // char -> glyph indexes generated by the lonesock tool.
63 __gshared int[256] glyph_indexes;
64 
65 void ChipmunkDemoTextInit()
66 {
67     GLint vshader = CompileShader(GL_VERTEX_SHADER,
68         q{
69             #version 110
70 
71             attribute vec2 vertex;
72             attribute vec2 tex_coord;
73             attribute vec4 color;
74 
75             varying vec2 v_tex_coord;
76             varying vec4 v_color;
77 
78             void main(){
79                 // TODO get rid of the GL 2.x matrix bit eventually?
80                 gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, 0.0, 1.0);
81 
82                 v_color = color;
83                 v_tex_coord = tex_coord;
84             }
85         });
86 
87     GLint fshader = CompileShader(GL_FRAGMENT_SHADER,
88         q{
89             #version 110
90 
91             uniform sampler2D u_texture;
92 
93             varying vec2 v_tex_coord;
94             varying vec4 v_color;
95 
96             float aa_step(float t1, float t2, float f)
97             {
98                 //return step(t2, f);
99                 return smoothstep(t1, t2, f);
100             }
101 
102             void main()
103             {
104                 float sdf = texture2D(u_texture, v_tex_coord).a;
105 
106                 //float fw = fwidth(sdf)*0.5;
107                 float fw = length(vec2(dFdx(sdf), dFdy(sdf))) * 0.5;
108 
109                 float alpha = aa_step(0.5 - fw, 0.5 + fw, sdf);
110                 gl_FragColor = v_color * (v_color.a * alpha);
111 
112                 //			gl_FragColor = vec4(1, 0, 0, 1);
113             }
114         });
115 
116     program = LinkProgram(vshader, fshader);
117     CheckGLErrors();
118 
119     //	GLint index = -1;//glGetUniformLocation(program, "u_texture");
120     //	glUniform1i(index, 0);
121     //	CheckGLErrors();
122 
123     // Setu VBO and VAO.
124     version (OSX)
125     {
126         glGenVertexArraysAPPLE(1, &vao);
127         glBindVertexArrayAPPLE(vao);
128     }
129     else
130     {
131         glGenVertexArrays(1, &vao);
132         glBindVertexArray(vao);
133     }
134 
135     glGenBuffers(1, &vbo);
136     glBindBuffer(GL_ARRAY_BUFFER, vbo);
137 
138     mixin(SET_ATTRIBUTE("program", "Vertex", "vertex", "GL_FLOAT"));
139     mixin(SET_ATTRIBUTE("program", "Vertex", "tex_coord", "GL_FLOAT"));
140     mixin(SET_ATTRIBUTE("program", "Vertex", "color", "GL_FLOAT"));
141 
142     glBindBuffer(GL_ARRAY_BUFFER, 0);
143 
144     version (OSX)
145     {
146         glBindVertexArrayAPPLE(0);
147     }
148     else
149     {
150         glBindVertexArray(0);
151     }
152 
153     CheckGLErrors();
154 
155     // Load the SDF font texture.
156     glGenTextures(1, &texture);
157     glBindTexture(GL_TEXTURE_2D, texture);
158     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, sdf_tex_width, sdf_tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, sdf_data.ptr);
159     glGenerateMipmap(GL_TEXTURE_2D);
160     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
161     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
162     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
163     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
164     CheckGLErrors();
165 
166     // Fill in the glyph index array.
167     for (int i = 0; i < sdf_num_chars; i++)
168     {
169         int char_index = sdf_spacing[i * 8];
170         glyph_indexes[char_index] = i;
171     }
172 }
173 
174 auto MAX(T)(T a, T b)
175 {
176     return a > b ? a : b;
177 }
178 
179 size_t triangle_capacity = 0;
180 GLsizei triangle_count     = 0;
181 Triangle* triangle_buffer = null;
182 
183 Triangle* PushTriangles(size_t count)
184 {
185     if (triangle_count + count > triangle_capacity)
186     {
187         triangle_capacity += MAX(triangle_capacity, count);
188         triangle_buffer    = cast(Triangle*)realloc(triangle_buffer, triangle_capacity * Triangle.sizeof);
189     }
190 
191     Triangle* buffer = triangle_buffer + triangle_count;
192     triangle_count += count;
193     return buffer;
194 }
195 
196 GLfloat PushChar(int character, GLfloat x, GLfloat y, Color color)
197 {
198     int i     = glyph_indexes[character];
199     GLfloat w = cast(GLfloat)sdf_tex_width;
200     GLfloat h = cast(GLfloat)sdf_tex_height;
201 
202     GLfloat gw = cast(GLfloat)sdf_spacing[i * 8 + 3];
203     GLfloat gh = cast(GLfloat)sdf_spacing[i * 8 + 4];
204 
205     GLfloat txmin = sdf_spacing[i * 8 + 1] / w;
206     GLfloat tymin = sdf_spacing[i * 8 + 2] / h;
207     GLfloat txmax = txmin + gw / w;
208     GLfloat tymax = tymin + gh / h;
209 
210     GLfloat s    = Scale / scale_factor;
211     GLfloat xmin = x + sdf_spacing[i * 8 + 5] / scale_factor * Scale;
212     GLfloat ymin = y + (sdf_spacing[i * 8 + 6] / scale_factor - gh) * Scale;
213     GLfloat xmax = xmin + gw * Scale;
214     GLfloat ymax = ymin + gh * Scale;
215 
216     Vertex a = { { xmin, ymin }, { txmin, tymax }, color };
217     Vertex b = { { xmin, ymax }, { txmin, tymin }, color };
218     Vertex c = { { xmax, ymax }, { txmax, tymin }, color };
219     Vertex d = { { xmax, ymin }, { txmax, tymax }, color };
220 
221     Triangle* triangles = PushTriangles(2);
222     Triangle  t0        = { a, b, c };
223     triangles[0] = t0;
224     Triangle t1 = { a, c, d };
225     triangles[1] = t1;
226 
227     return sdf_spacing[i * 8 + 7] * s;
228 }
229 
230 void ChipmunkDemoTextDrawString(cpVect pos, in char[] str)
231 {
232     Color c   = LAColor(1.0f, 1.0f);
233     GLfloat x = cast(GLfloat)pos.x, y = cast(GLfloat)pos.y;
234 
235     for (size_t i = 0, len = str.length; i < len; i++)
236     {
237         if (str[i] == '\n')
238         {
239             y -= LineHeight;
240             x  = cast(GLfloat)pos.x;
241 
242             //		} else if(str[i] == '*'){ // print out the last demo key
243             //			glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, 'A' + demoCount - 1);
244         }
245         else
246         if (str[i] == '\0')
247         {
248             break;
249         }
250         else
251         {
252             x += cast(GLfloat)PushChar(str[i], x, y, c);
253         }
254     }
255 }
256 
257 void ChipmunkDemoTextFlushRenderer()
258 {
259     //	triangle_count = 0;
260     //	ChipmunkDemoTextDrawString(cpv(-300, 0), "0.:,'");
261 
262     glBindBuffer(GL_ARRAY_BUFFER, vbo);
263     glBufferData(GL_ARRAY_BUFFER, Triangle.sizeof * triangle_count, triangle_buffer, GL_STREAM_DRAW);
264 
265     glUseProgram(program);
266 
267     version (OSX)
268     {
269         glBindVertexArrayAPPLE(vao);
270     }
271     else
272     {
273         glBindVertexArray(vao);
274     }
275 
276     glDrawArrays(GL_TRIANGLES, 0, cast(GLsizei)(triangle_count * 3));
277 
278     CheckGLErrors();
279 }
280 
281 void ChipmunkDemoTextClearRenderer()
282 {
283     triangle_count = 0;
284 }
285 
286 GLsizei pushed_triangle_count = 0;
287 
288 void ChipmunkDemoTextPushRenderer()
289 {
290     pushed_triangle_count = triangle_count;
291 }
292 
293 void ChipmunkDemoTextPopRenderer()
294 {
295     triangle_count = pushed_triangle_count;
296 }