/*- * Copyright (C) 2025 Alessandro Iezzi * * This file is part of Tris Game. * * Tris Game is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Tris Game is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tris Game. If not, see . */ #include #include #include #include #include #include #include "engine.h" #include "ui/ui.h" #include "../util/list.h" #include "shape/circle.h" #ifdef X11 #include #endif #define RANGE_GL 2.0f static Log *_log = NULL; static void init_log(void) { if (_log != NULL) return; _log = log_create("Engine"); } typedef struct Line { float x1, y1, x2, y2; } Line; static Engine *engine; /* FPS */ static time_t start; static int frames; static time_t end; void (*dispatch_ui_event)(int); typedef struct MouseButtonEvent { void (*on_mouse_button_pressed)(float x, float y, void *data); void *data; } MouseButtonEvent; MouseButtonEvent *mouse_button_event; Engine * engine_new(int w, int h) { init_log(); engine = malloc(sizeof(Engine)); if (engine == NULL) { log_error(_log, "Error allocating memory for engine"); exit(EXIT_FAILURE); } engine->lines = list_create(); engine->ui = ui_new(w, h); engine_set_rendering_background(engine, 0.0f, 0.0f, 0.2f, 1.0f); return engine; } void engine_set_window_title(Engine *engine, const char *title) { ui_set_title(engine->ui, title); } static Line * engine_new_line(float x1, float y1, float x2, float y2) { Line *line = malloc(sizeof(Line)); line->x1 = x1; line->y1 = y1; line->x2 = x2; line->y2 = y2; return line; } void engine_draw_line(float x1, float y1, float x2, float y2) { if (engine->lines != NULL) { Line *line = engine_new_line(x1, y1, x2, y2); list_add(engine->lines, line); } } static void engine_calculate_fps() { frames ++; end = time(NULL); if (end - start >= 1.0) { #ifdef FPS printf("FPS: %d\n", frames); #endif frames = 0; start = time(NULL); } } static void render_line(Line *line) { glVertex2f(line->x1, line->y1); glVertex2f(line->x2, line->y2); } static void draw_lines() { if (engine->lines == NULL || engine->lines->size <= 0) return; glLineWidth(5.0f); glBegin(GL_LINES); glColor3f(1.0f, 1.0f, 0.0f); /* Yellow */ list_node_t *current = engine->lines->head; do { render_line(current->data); current = current->next; } while (current != NULL); glEnd(); } static void draw_frames(void *data) { if (data == NULL) return; Engine *engine = data; Color *bk = engine->rendering_background; glClearColor(bk->r, bk->g, bk->b, bk->a); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); draw_lines(); engine->draw_frame_listener->draw_frames(engine->draw_frame_listener->data); engine_calculate_fps(); } static void engine_set_ortho(float left, float right, float bottom, float top) { engine->ortho_left = left; engine->ortho_right = right; engine->ortho_bottom = bottom; engine->ortho_top = top; glOrtho(left, right, bottom, top, -1, 1); } static void engine_on_ui_expose(UIEventResize *er) { /* Set the viewport to the window size */ glViewport(0, 0, er->width, er->height); /* Objects in the rendering area must maintain the proportions. So, let's enable Ortho */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); float aspect; if (er->width >= er->height) { aspect = (float)er->width / (float)er->height; engine_set_ortho(-aspect, aspect, -1, 1); } else { aspect = (float)er->height / (float)er->width; engine_set_ortho(-1, 1, -aspect, aspect); } /* Returns to the model view */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void engine_loop(void) { ui_set_resize_listener(engine_on_ui_expose); ui_set_loop_listener(engine->ui, draw_frames, engine); /* FPS calculation */ start = time(NULL); frames = 0; ui_loop(engine->ui); } void mouse_button_press_event(UIMouseButtonPressed *mbp) { float oglX = engine->ortho_left + ((float) mbp->x / engine->ui->width) * (engine->ortho_right - engine->ortho_left); float oglY = engine->ortho_top - ((float) mbp->y / engine->ui->height) * (engine->ortho_top - engine->ortho_bottom); mouse_button_event->on_mouse_button_pressed(oglX, oglY, mouse_button_event->data); } void engine_set_mouse_button_listener(void (*event)(float x, float y, void *data), void *data) { init_log(); mouse_button_event = malloc(sizeof(MouseButtonEvent)); if (mouse_button_event == NULL) { log_error(_log, "Error allocating mouse button event"); exit(EXIT_FAILURE); } mouse_button_event->on_mouse_button_pressed = event; mouse_button_event->data = data; ui_set_mouse_press_listener(mouse_button_press_event, mouse_button_event); } static void engine_dispatch_ui_events(int type) { int event_type = 0; switch (type) { #ifdef X11 case ButtonPress: event_type = ENGINE_MOUSE_PRESSED; break; #endif default: event_type = 0; break; } dispatch_ui_event(event_type); } void engine_input(void (*f_input)(int engine_input)) { ui_set_generic_listener(engine_dispatch_ui_events); dispatch_ui_event = f_input; } void engine_set_rendering_background(Engine *engine, float r, float g, float b, float a) { if (engine->rendering_background == NULL) { engine->rendering_background = color_new(r, g, b, a); } else { color_set_rgba(engine->rendering_background, r, g, b, a); } } void engine_set_rendering_background_c(Engine *engine, Color *color) { engine_set_rendering_background(engine, color->r, color->g, color->b, color->a); } void engine_set_rendering_listener(Engine *engine, void (*draw_frames)(void *data), void *data) { engine->draw_frame_listener = malloc(sizeof(DrawFrameListener)); engine->draw_frame_listener->draw_frames = draw_frames; engine->draw_frame_listener->data = data; }