| /* |
| * Copyright 2026 Behdad Esfahbod. All Rights Reserved. |
| */ |
| |
| #include "demo-renderer.h" |
| #include "demo-atlas.h" |
| #include "demo-glstate.h" |
| |
| |
| struct demo_renderer_gl_t : demo_renderer_t |
| { |
| GLFWwindow *window; |
| demo_glstate_t *st; |
| GLuint vao_name; |
| GLuint buf_name; |
| bool vao_ready; |
| unsigned int uploaded_count; |
| unsigned uploaded_generation; |
| |
| demo_renderer_gl_t (GLFWwindow *window_, bool draw_only) |
| : window (window_), vao_ready (false), |
| uploaded_count (0), uploaded_generation (0) |
| { |
| st = demo_glstate_create (draw_only); |
| glGenVertexArrays (1, &vao_name); |
| glGenBuffers (1, &buf_name); |
| } |
| |
| ~demo_renderer_gl_t () override |
| { |
| glDeleteVertexArrays (1, &vao_name); |
| glDeleteBuffers (1, &buf_name); |
| demo_glstate_destroy (st); |
| } |
| |
| void setup_vao () |
| { |
| if (vao_ready) |
| return; |
| vao_ready = true; |
| |
| GLint program; |
| glGetIntegerv (GL_CURRENT_PROGRAM, &program); |
| |
| glBindVertexArray (vao_name); |
| glBindBuffer (GL_ARRAY_BUFFER, buf_name); |
| |
| GLsizei stride = sizeof (glyph_vertex_t); |
| |
| GLint loc; |
| loc = glGetAttribLocation (program, "a_position"); |
| glEnableVertexAttribArray (loc); |
| glVertexAttribPointer (loc, 2, GL_FLOAT, GL_FALSE, stride, |
| (const void *) offsetof (glyph_vertex_t, x)); |
| |
| loc = glGetAttribLocation (program, "a_texcoord"); |
| glEnableVertexAttribArray (loc); |
| glVertexAttribPointer (loc, 2, GL_FLOAT, GL_FALSE, stride, |
| (const void *) offsetof (glyph_vertex_t, tx)); |
| |
| loc = glGetAttribLocation (program, "a_normal"); |
| glEnableVertexAttribArray (loc); |
| glVertexAttribPointer (loc, 2, GL_FLOAT, GL_FALSE, stride, |
| (const void *) offsetof (glyph_vertex_t, nx)); |
| |
| loc = glGetAttribLocation (program, "a_emPerPos"); |
| glEnableVertexAttribArray (loc); |
| glVertexAttribPointer (loc, 1, GL_FLOAT, GL_FALSE, stride, |
| (const void *) offsetof (glyph_vertex_t, emPerPos)); |
| |
| loc = glGetAttribLocation (program, "a_glyphLoc"); |
| glEnableVertexAttribArray (loc); |
| glVertexAttribIPointer (loc, 1, GL_UNSIGNED_INT, stride, |
| (const void *) offsetof (glyph_vertex_t, atlas_offset)); |
| |
| glBindVertexArray (0); |
| } |
| |
| demo_atlas_t *get_atlas () override |
| { |
| return demo_glstate_get_atlas (st); |
| } |
| |
| void setup () override |
| { |
| demo_glstate_setup (st); |
| } |
| |
| void set_gamma (float gamma) override |
| { |
| demo_glstate_set_gamma (st, gamma); |
| } |
| |
| void set_foreground (float r, float g, float b, float a) override |
| { |
| demo_glstate_set_foreground (st, r, g, b, a); |
| } |
| |
| void set_background (float r, float g, float b, float a) override |
| { |
| glClearColor (r, g, b, a); |
| } |
| |
| void set_debug (bool enabled) override |
| { |
| demo_glstate_set_debug (st, enabled); |
| } |
| |
| void set_stem_darkening (bool enabled) override |
| { |
| demo_glstate_set_stem_darkening (st, enabled); |
| } |
| |
| void toggle_vsync (bool &vsync) override |
| { |
| vsync = !vsync; |
| glfwSwapInterval (vsync ? 1 : 0); |
| } |
| |
| void display (glyph_vertex_t *vertices, unsigned int count, |
| unsigned generation, |
| int width, int height, float mat[16]) override |
| { |
| glViewport (0, 0, width, height); |
| demo_glstate_set_matrix (st, mat); |
| |
| glClear (GL_COLOR_BUFFER_BIT); |
| |
| if (count > 0) |
| { |
| setup_vao (); |
| |
| glBindVertexArray (vao_name); |
| |
| if (count != uploaded_count || generation != uploaded_generation) |
| { |
| glBindBuffer (GL_ARRAY_BUFFER, buf_name); |
| glBufferData (GL_ARRAY_BUFFER, |
| sizeof (glyph_vertex_t) * count, |
| (const char *) vertices, GL_STATIC_DRAW); |
| uploaded_count = count; |
| uploaded_generation = generation; |
| } |
| |
| glDrawArrays (GL_TRIANGLES, 0, count); |
| |
| glBindVertexArray (0); |
| } |
| |
| glfwSwapBuffers (window); |
| } |
| }; |
| |
| |
| demo_renderer_t * |
| demo_renderer_create_gl (GLFWwindow *window, bool draw_only) |
| { |
| return new demo_renderer_gl_t (window, draw_only); |
| } |