Browse code

Add OpenGL boilerplate

Robert Cranston authored on 08/03/2026 00:46:06
Showing 4 changed files

1 1
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+/sdf-winding
0 2
new file mode 100644
... ...
@@ -0,0 +1,8 @@
1
+CXXFLAGS += -std=c++11
2
+CXXFLAGS += -Wall -Wextra -Wpedantic
3
+CXXFLAGS += -Wconversion -Wsign-conversion
4
+CXXFLAGS += -Wshadow
5
+CXXFLAGS += -Og -g -fsanitize=undefined,address
6
+LDLIBS   += -lGL -lGLEW -lglfw
7
+
8
+sdf-winding:
... ...
@@ -10,12 +10,20 @@ the inside from the outside of a glyph][TrueType]:
10 10
 
11 11
 I have not read a single word of that reference manual, proceed accordingly.
12 12
 
13
+To build, run `make`. You need the following dependencies on your system:
14
+
15
+-   OpenGL
16
+-   [GLEW][]
17
+-   [GLFW][]
18
+
13 19
 [`sdf-winding`]:  https://git.rcrnstn.net/rcrnstn/sdf-winding
14 20
 [glyph]:          https://en.wikipedia.org/wiki/Glyph
15 21
 [winding number]: https://en.wikipedia.org/wiki/Winding_number
16 22
 [SDF]:            https://en.wikipedia.org/wiki/Signed_distance_function
17 23
 [Figure 7]:       https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/FE6.gif
18 24
 [TrueType]:       https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#distinguishing
25
+[GLEW]:           https://glew.sourceforge.net
26
+[GLFW]:           https://www.glfw.org
19 27
 
20 28
 ## License
21 29
 
22 30
new file mode 100644
... ...
@@ -0,0 +1,199 @@
1
+/// Includes
2
+
3
+#include <ctime>
4
+#include <sstream>
5
+#include <fstream>
6
+#include <iostream>
7
+#include <iomanip>
8
+
9
+#include <GL/glew.h>
10
+#include <GLFW/glfw3.h>
11
+
12
+/// Constants
13
+
14
+constexpr auto width  = 640;
15
+constexpr auto height = 480;
16
+constexpr auto title  = "";
17
+
18
+/// Helpers
19
+
20
+//// Debug
21
+void GLAPIENTRY debug_message_callback(
22
+    GLenum          /* source   */,
23
+    GLenum          /* type     */,
24
+    GLuint          /* id       */,
25
+    GLenum          /* severity */,
26
+    GLsizei         /* length   */,
27
+    GLchar  const *    message,
28
+    void    const * /* param    */
29
+)
30
+{
31
+    std::cerr << message << std::endl;
32
+};
33
+
34
+//// Uniform
35
+void uniform(GLint location, int            value    ) { glUniform1i (location,    value); }
36
+void uniform(GLint location, bool           value    ) { glUniform1i (location,    value); }
37
+void uniform(GLint location, float          value    ) { glUniform1f (location,    value); }
38
+void uniform(GLint location, float const (& value)[1]) { glUniform1fv(location, 1, value); }
39
+void uniform(GLint location, float const (& value)[2]) { glUniform2fv(location, 1, value); }
40
+void uniform(GLint location, float const (& value)[3]) { glUniform3fv(location, 1, value); }
41
+void uniform(GLint location, float const (& value)[4]) { glUniform4fv(location, 1, value); }
42
+template<typename T>
43
+void uniform(char const * name, T const & value)
44
+{
45
+    GLuint program;   glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&program);
46
+    GLint  location = glGetUniformLocation(program, name);
47
+    uniform(location, value);
48
+}
49
+
50
+//// Write
51
+void write_tga(char const * base)
52
+{
53
+    char header[] = {
54
+        0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55
+        (char)(width  >> 0U),
56
+        (char)(width  >> 8U),
57
+        (char)(height >> 0U),
58
+        (char)(height >> 8U),
59
+        4*8, 0,
60
+    };
61
+    char data[4*width*height];
62
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
63
+    glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, data);
64
+    auto t  = std::time(nullptr);
65
+    auto pt = std::put_time(std::localtime(&t), "%Y-%m-%d_%H-%M-%S");
66
+    std::ofstream((std::ostringstream{} << base << "_" << pt << ".tga").str())
67
+        .write(header, sizeof(header))
68
+        .write(data,   sizeof(data));
69
+}
70
+
71
+/// Main
72
+
73
+int main(int /*argc*/, char * argv[])
74
+{
75
+    //// Window/context
76
+    glfwInit();
77
+    auto window = glfwCreateWindow(width, height, title, nullptr, nullptr);
78
+    glfwMakeContextCurrent(window);
79
+    glewInit();
80
+
81
+    //// Callbacks
82
+    glfwSetWindowUserPointer(window, argv[0]);
83
+    glfwSetKeyCallback(window,
84
+        [](GLFWwindow* w, int key, int /*scancode*/, int action, int /*mods*/)
85
+        {
86
+            auto ptr = glfwGetWindowUserPointer(w);
87
+            if (action != GLFW_RELEASE) return;
88
+            if (key == GLFW_KEY_Q) glfwSetWindowShouldClose(w, GLFW_TRUE);
89
+            if (key == GLFW_KEY_W) write_tga((char *)ptr);
90
+        }
91
+    );
92
+
93
+    //// Debug
94
+    glEnable(GL_DEBUG_OUTPUT);
95
+    glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
96
+    glDebugMessageCallback(debug_message_callback, nullptr);
97
+
98
+    //// Shader
99
+    auto make_program = [](char const * vert_source, char const * frag_source)
100
+    {
101
+        auto program = glCreateProgram();
102
+        auto vert    = glCreateShader(GL_VERTEX_SHADER);
103
+        auto frag    = glCreateShader(GL_FRAGMENT_SHADER);
104
+        glShaderSource(vert, 1, &vert_source, nullptr);
105
+        glShaderSource(frag, 1, &frag_source, nullptr);
106
+        glCompileShader(vert);
107
+        glCompileShader(frag);
108
+        glAttachShader(program, vert);
109
+        glAttachShader(program, frag);
110
+        glLinkProgram(program);
111
+        return program;
112
+    };
113
+    auto vert_source = R"(
114
+        #version 140
115
+
116
+        out vec2 tex_coord;
117
+
118
+        void main()
119
+        {
120
+            tex_coord = vec2[](
121
+                vec2(0, 0),
122
+                vec2(2, 0),
123
+                vec2(0, 2)
124
+            )[gl_VertexID];
125
+            gl_Position = vec4(tex_coord*2-1, 0, 1);
126
+        }
127
+    )";
128
+    auto tex_coord_program = make_program(vert_source, R"(
129
+        #version 140
130
+
131
+        uniform int   view;
132
+        uniform bool  debug;
133
+        uniform float time;
134
+
135
+        in vec2 tex_coord;
136
+
137
+        void main()
138
+        {
139
+            gl_FragColor = vec4(fract(tex_coord - float(!debug)*time), 0, 1);
140
+
141
+            // Exercise the debugging colors.
142
+            // <https://www.desmos.com/calculator/uijwefecqs>
143
+            if (view >= 2)
144
+            {
145
+                float x = tex_coord.x;
146
+                x -= 0.5;
147
+                x *= 10;
148
+                x  = clamp(x, -3, +3);
149
+                x /= (3 + x) * (3 - x);
150
+                x *= (3 + 1) * (3 - 1);
151
+                x *= 0.5;
152
+                x += 0.5;
153
+                gl_FragColor = vec4(x / (1 / float(tex_coord.y > 0.5)));
154
+            }
155
+
156
+            bvec3 debug_color = bvec3(
157
+                any(lessThan   (gl_FragColor, vec4(0))),
158
+                any(greaterThan(gl_FragColor, vec4(1))),
159
+                any(isinf      (gl_FragColor)) ||
160
+                any(isnan      (gl_FragColor))
161
+            );
162
+            if (debug && any(debug_color))
163
+                gl_FragColor = vec4(debug_color, 1);
164
+        }
165
+    )");
166
+
167
+    //// Main loop
168
+    int   view  = 1;
169
+    bool  debug = true;
170
+    float time  = (float)glfwGetTime();
171
+    while (glfwPollEvents(), !glfwWindowShouldClose(window))
172
+    {
173
+        ///// Time
174
+        float pt = time;
175
+        float dt = (time = (float)glfwGetTime()) - pt;
176
+
177
+        ///// Input
178
+        for (int num = 0; num <= 9; ++num)
179
+        if (glfwGetKey(window, GLFW_KEY_0 + num)) view  = num;
180
+        if (glfwGetKey(window, GLFW_KEY_R))       debug = false;
181
+        if (glfwGetKey(window, GLFW_KEY_E))       debug = true;
182
+
183
+        ///// Draw
184
+        glClear(GL_COLOR_BUFFER_BIT);
185
+        glUseProgram(tex_coord_program);
186
+        uniform("view",  view);
187
+        uniform("debug", debug);
188
+        uniform("time",  time);
189
+        uniform("dt",    dt);
190
+        glDrawArrays(GL_TRIANGLES, 0, 3);
191
+
192
+        ///// Swap
193
+        glfwSwapBuffers(window);
194
+    }
195
+
196
+    //// Cleanup
197
+    glfwDestroyWindow(window);
198
+    glfwTerminate();
199
+}