diff --git a/shaders/triangle.frag.glsl b/shaders/triangle.frag.glsl new file mode 100644 index 0000000..bdf74d8 --- /dev/null +++ b/shaders/triangle.frag.glsl @@ -0,0 +1,10 @@ +#version 320 es +precision mediump float; + +uniform vec4 uColor; // RGBA + +out vec4 FragColor; + +void main() { + FragColor = uColor; +} \ No newline at end of file diff --git a/shaders/triangle.vert.glsl b/shaders/triangle.vert.glsl new file mode 100644 index 0000000..8650660 --- /dev/null +++ b/shaders/triangle.vert.glsl @@ -0,0 +1,11 @@ +#version 320 es +precision mediump float; + +layout(location = 0) in vec2 aPos; + +uniform mat4 uProjection; + +void main() +{ + gl_Position = uProjection * vec4(aPos, 0.0, 1.0); +} \ No newline at end of file diff --git a/src/core/raster.c b/src/core/raster.c index ab9f5ef..0b0b653 100644 --- a/src/core/raster.c +++ b/src/core/raster.c @@ -1,7 +1,7 @@ #include "raster.h" +#include "epoxy/gl.h" #include "primitives.h" #include "stddef.h" -#include void vektor_edgebuffer_add_edge(EdgeBuffer* buffer, Edge edge) { if (buffer->count >= buffer->capacity) { @@ -114,7 +114,7 @@ void vektor_framebuffer_rasterize(VektorFramebuffer* fb, break; default: - // TODO fill in all primitives + // TODO: fill in all primitives break; } } @@ -123,4 +123,69 @@ void vektor_framebuffer_rasterize(VektorFramebuffer* fb, vektor_framebuffer_drawline(fb, edges.edges[i].p1, edges.edges[i].p2, vektor_color_solid(255, 0, 255), 4); } +} + +VertexBuffer vektor_rasterize(VektorPrimitiveBuffer* prims) { + EdgeBuffer edges = {0}; + for (size_t i = 0; i < prims->count; i++) { + VektorPrimitive* p = &prims->primitives[i]; + + switch (p->kind) { + case VEKTOR_LINE: + vektor_line_flatten(&edges, p->line); + break; + + case VEKTOR_POLYLINE: + vektor_polyline_flatten(&edges, p->polyline); + break; + + case VEKTOR_POLYGON: + vektor_polygon_flatten(&edges, p->polygon); + break; + + default: + // TODO: fill in all primitives + break; + } + } + + VertexBuffer vb = vektor_edges_to_triangles(&edges, 0.1f); + return vb; +} + +void vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2) { + if (vb->count + 3 >= vb->capacity) { + vb->capacity = vb->capacity ? vb->capacity * 2 : 8; + vb->vertices = realloc(vb->vertices, sizeof(V2) * vb->capacity); + } + vb->vertices[vb->count++] = v0; + vb->vertices[vb->count++] = v1; + vb->vertices[vb->count++] = v2; +} + +void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, float thickness) { + float dx = e.p2.x - e.p1.x; + float dy = e.p2.y - e.p1.y; + float len = sqrtf(dx * dx + dy * dy); + if (len == 0) + return; + + float px = -dy / len * (thickness / 2); + float py = dx / len * (thickness / 2); + + V2 v0 = {e.p1.x + px, e.p1.y + py}; + V2 v1 = {e.p1.x - px, e.p1.y - py}; + V2 v2 = {e.p2.x + px, e.p2.y + py}; + V2 v3 = {e.p2.x - px, e.p2.y - py}; + + vb_add_triangle(vb, v0, v1, v2); + vb_add_triangle(vb, v2, v1, v3); +} + +VertexBuffer vektor_edges_to_triangles(EdgeBuffer* edges, float thickness) { + VertexBuffer vb = {0}; + for (size_t i = 0; i < edges->count; i++) { + vektor_edge_to_triangles(&vb, edges->edges[i], thickness); + } + return vb; } \ No newline at end of file diff --git a/src/core/raster.h b/src/core/raster.h index 5e3b0e9..08ec9b0 100644 --- a/src/core/raster.h +++ b/src/core/raster.h @@ -43,4 +43,15 @@ void vektor_framebuffer_drawline(VektorFramebuffer* fb, V2 a, V2 b, void vektor_framebuffer_rasterize(VektorFramebuffer* fb, VektorPrimitiveBuffer* primitives); +typedef struct { + V2* vertices; + size_t count; + size_t capacity; +} VertexBuffer; + +void vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2); +void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, float thickness); +VertexBuffer vektor_edges_to_triangles(EdgeBuffer* edges, float thickness); +VertexBuffer vektor_rasterize(VektorPrimitiveBuffer* prims); + #endif // RASTER_H_ diff --git a/src/core/vector.h b/src/core/vector.h index 0038ead..d483711 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -4,8 +4,8 @@ #include "math.h" typedef struct { - double x; - double y; + float x; + float y; } V2; typedef struct { diff --git a/src/ui/uicontroller.c b/src/ui/uicontroller.c index 9e26388..b0c0bbe 100644 --- a/src/ui/uicontroller.c +++ b/src/ui/uicontroller.c @@ -23,10 +23,14 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) { // Load theme gtk_icon_theme_add_search_path( - gtk_icon_theme_get_for_display( - gdk_display_get_default() - ), "icons" - ); + gtk_icon_theme_get_for_display(gdk_display_get_default()), "icons"); + + GtkIconTheme* theme = + gtk_icon_theme_get_for_display(gdk_display_get_default()); + if (gtk_icon_theme_has_icon(theme, "vektor-circle-symbolic")) + g_print("GTK sees it!\n"); + else + g_print("Still invisible...\n"); // populate state stateOut->window = @@ -46,7 +50,6 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) { GTK_BUTTON(gtk_builder_get_object(builder, "button_rectangletool")); stateOut->workspaceButtonCircletool = GTK_BUTTON(gtk_builder_get_object(builder, "button_circletool")); - // Set window properties gtk_window_set_application(stateOut->window, app); @@ -62,6 +65,4 @@ void vektor_uictrl_map(VektorWidgetState* state) { int window_width = gtk_widget_get_width(GTK_WIDGET(state->window)); g_print("%i", window_width); gtk_paned_set_position(state->workspacePaned, 800 * .7); - - } diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index c7a4990..7ad8fac 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -2,30 +2,34 @@ #include "gtk/gtk.h" #include "../core/raster.h" +#include "src/core/primitives.h" #include "uicontroller.h" #include "vektorcanvas.h" +#include #define VKTR_CANVAS_WIDTH 400 #define VKTR_CANVAS_HEIGHT 400 #define VKTR_CANVAS_SIZE (VKTR_CANVAS_WIDTH * VKTR_CANVAS_HEIGHT * 4) +char* read_file(const char* path) { + FILE* f = fopen(path, "rb"); + if (!f) + return NULL; + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + char* buffer = malloc(size + 1); + fread(buffer, 1, size, f); + buffer[size] = '\0'; // null-terminate + fclose(f); + return buffer; +} + static GLuint shader_program; static GLuint vao; - -static const char* vertex_shader_src = - "#version 300 es\n" // <- ES version - "layout(location = 0) in vec2 aPos;\n" - "void main() {\n" - " gl_Position = vec4(aPos, 0.0, 1.0);\n" - "}\n"; - -static const char* fragment_shader_src = - "#version 300 es\n" // <- ES version - "precision mediump float;\n" // required in ES for fragment color - "out vec4 FragColor;\n" - "void main() {\n" - " FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" - "}\n"; +VertexBuffer vb; static GLuint compile_shader(GLenum type, const char* src) { GLuint shader = glCreateShader(type); @@ -43,8 +47,17 @@ static GLuint compile_shader(GLenum type, const char* src) { } static void init_shader(void) { - GLuint vertex = compile_shader(GL_VERTEX_SHADER, vertex_shader_src); - GLuint fragment = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src); + char* vert_src = read_file("./shaders/triangle.vert.glsl"); + char* frag_src = read_file("./shaders/triangle.frag.glsl"); + + if (!vert_src || !frag_src) + g_error("Failed to load shader files"); + + GLuint vertex = compile_shader(GL_VERTEX_SHADER, vert_src); + GLuint fragment = compile_shader(GL_FRAGMENT_SHADER, frag_src); + + printf("%s\n", vert_src); + printf("%s\n", frag_src); shader_program = glCreateProgram(); glAttachShader(shader_program, vertex); @@ -64,55 +77,113 @@ static void init_shader(void) { } static void init_geometry(void) { - // Vertices for a rectangle in NDC coordinates - float vertices[] = { - -0.5f, -0.5f, // bottom-left - 0.5f, -0.5f, // bottom-right - 0.5f, 0.5f, // top-right - -0.5f, 0.5f // top-left - }; - unsigned int indices[] = {0, 1, 2, 2, 3, 0}; + // V2 vs[3] = {(V2){-0.5, -0.5}, (V2){0.5, -0.5}, (V2){0.0, 0.5}}; + // VertexBuffer vb = + // (VertexBuffer){.count = 3, .capacity = 3, .vertices = &vs[0]}; - GLuint vbo, ebo; + VektorPolygon triangle = *vektor_polygon_new(); + vektor_polygon_add_point(&triangle, (V2){-0.5f, -0.5f}); // bottom-left + vektor_polygon_add_point(&triangle, (V2){0.5f, -0.5f}); // bottom-right + vektor_polygon_add_point(&triangle, (V2){0.0f, 0.5f}); // top-center + + VektorPrimitiveBuffer prims = {0}; + vektor_primitivebuffer_add_primitive( + &prims, + (VektorPrimitive){.kind = VEKTOR_POLYGON, .polygon = &triangle}); + + vb = vektor_rasterize(&prims); + + for (size_t i = 0; i < vb.count; i++) { + printf("Vertex %zu: x=%f, y=%f\n", i, vb.vertices[i].x, + vb.vertices[i].y); + } + + GLuint vbo; + + // 1. Create VAO glGenVertexArrays(1, &vao); - glGenBuffers(1, &vbo); - glGenBuffers(1, &ebo); - glBindVertexArray(vao); + // 2. Create VBO + glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, + // 3. Upload your vertices (V2 = 2 floats per vertex) + glBufferData(GL_ARRAY_BUFFER, vb.count * sizeof(V2), vb.vertices, GL_STATIC_DRAW); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), - (void*)0); + // 4. Tell GL about the vertex layout glEnableVertexAttribArray(0); - + glVertexAttribPointer(0, // layout location 0 in shader + 2, // 2 components per vertex (x, y) + GL_FLOAT, // type + GL_FALSE, // do not normalize + sizeof(V2), // stride (size of one vertex) + (void*)0 // offset + ); glBindVertexArray(0); } static gboolean render(GtkGLArea* area, GdkGLContext* context) { + glUseProgram(shader_program); + + GLuint uProjectionLoc = glGetUniformLocation(shader_program, "uProjection"); + GLuint uColorLoc = glGetUniformLocation(shader_program, "uColor"); + float projectionMatrix[16] = {1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1}; + glUniformMatrix4fv(uProjectionLoc, 1, GL_FALSE, projectionMatrix); + glUniform4f(uColorLoc, 1.0, 0.0, 1.0, 1.0); // magenta + + glBindVertexArray(vao); + glDisable(GL_CULL_FACE); + // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - glUseProgram(shader_program); - glBindVertexArray(vao); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + glDrawArrays(GL_TRIANGLES, 0, vb.count); + GLenum err = glGetError(); + printf("OpenGL error: %x\n", err); + // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); glUseProgram(0); return TRUE; } +static void dump_gl_info(GtkGLArea* area) { + gtk_gl_area_make_current(area); + + if (gtk_gl_area_get_error(area)) { + g_warning("Failed to make GL context current"); + return; + } + + const GLubyte* renderer = glGetString(GL_RENDERER); + const GLubyte* vendor = glGetString(GL_VENDOR); + const GLubyte* version = glGetString(GL_VERSION); + const GLubyte* shading = glGetString(GL_SHADING_LANGUAGE_VERSION); + + g_debug("GL Vendor : %s", vendor); + g_debug("GL Renderer : %s", renderer); + g_debug("GL Version : %s", version); + g_debug("GLSL Version : %s", shading); + + GLint n; + glGetIntegerv(GL_NUM_EXTENSIONS, &n); + g_debug("Supported extensions (%d):", n); + for (GLint i = 0; i < n; ++i) { + g_debug(" %s", glGetStringi(GL_EXTENSIONS, i)); + } +} + static void realize(GtkGLArea* area, gpointer user_data) { gtk_gl_area_make_current(area); if (gtk_gl_area_get_error(area) != NULL) return; // context creation failed + glEnable(GL_DEBUG_OUTPUT); + dump_gl_info(area); init_shader(); init_geometry(); }