Files

201 lines
5.9 KiB
C++

/*
* engine.h
* MontaukOS 2D Game Engine - Core
* Window management, timing, pixel buffer helpers
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <montauk/syscall.h>
#include <montauk/heap.h>
#include <montauk/string.h>
#include <gui/gui.hpp>
#include <gui/truetype.hpp>
extern "C" {
#include <stdio.h>
}
namespace engine {
// ============================================================================
// Core engine
// ============================================================================
struct Engine {
int win_id = -1;
uint32_t* pixels = nullptr;
int screen_w = 0;
int screen_h = 0;
// Timing
uint64_t last_time_ms = 0;
float dt = 0.016f; // delta time in seconds
uint64_t frame_count = 0;
// Font
gui::TrueTypeFont* font = nullptr;
bool running = true;
// ---- Lifecycle ----
bool init(const char* title, int w, int h) {
screen_w = w;
screen_h = h;
// Load font
font = (gui::TrueTypeFont*)montauk::malloc(sizeof(gui::TrueTypeFont));
if (font) {
montauk::memset(font, 0, sizeof(gui::TrueTypeFont));
if (!font->init("0:/fonts/Roboto-Medium.ttf")) {
montauk::mfree(font);
font = nullptr;
}
}
// Create window
montauk::abi::WinCreateResult wres;
if (montauk::win_create(title, w, h, &wres) < 0 || wres.id < 0)
return false;
win_id = wres.id;
pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
last_time_ms = montauk::get_milliseconds();
return true;
}
void update_timing() {
uint64_t now = montauk::get_milliseconds();
uint64_t elapsed = now - last_time_ms;
if (elapsed == 0) elapsed = 1;
if (elapsed > 100) elapsed = 100; // cap to avoid spiral
dt = (float)elapsed / 1000.0f;
last_time_ms = now;
frame_count++;
}
// Poll one window event. Returns true if an event was received.
bool poll(montauk::abi::WinEvent* ev) {
int r = montauk::win_poll(win_id, ev);
if (r < 0) { running = false; return false; }
if (r == 0) return false;
if (ev->type == 3) { running = false; return false; } // close
if (ev->type == 2) { // resize
screen_w = ev->resize.w;
screen_h = ev->resize.h;
pixels = (uint32_t*)(uintptr_t)montauk::win_resize(
win_id, screen_w, screen_h);
}
return true;
}
void present() {
montauk::win_present(win_id);
}
void shutdown() {
if (win_id >= 0) montauk::win_destroy(win_id);
win_id = -1;
}
// ---- Drawing helpers ----
void clear(uint32_t color) {
int count = screen_w * screen_h;
for (int i = 0; i < count; i++)
pixels[i] = color;
}
void fill_rect(int x, int y, int w, int h, uint32_t color) {
int x0 = x < 0 ? 0 : x;
int y0 = y < 0 ? 0 : y;
int x1 = x + w > screen_w ? screen_w : x + w;
int y1 = y + h > screen_h ? screen_h : y + h;
for (int row = y0; row < y1; row++)
for (int col = x0; col < x1; col++)
pixels[row * screen_w + col] = color;
}
void fill_rect_alpha(int x, int y, int w, int h, uint32_t color) {
uint8_t sa = (color >> 24) & 0xFF;
uint8_t sr = (color >> 16) & 0xFF;
uint8_t sg = (color >> 8) & 0xFF;
uint8_t sb = color & 0xFF;
if (sa == 0) return;
int x0 = x < 0 ? 0 : x;
int y0 = y < 0 ? 0 : y;
int x1 = x + w > screen_w ? screen_w : x + w;
int y1 = y + h > screen_h ? screen_h : y + h;
for (int row = y0; row < y1; row++) {
for (int col = x0; col < x1; col++) {
uint32_t dst = pixels[row * screen_w + col];
uint8_t dr = (dst >> 16) & 0xFF;
uint8_t dg = (dst >> 8) & 0xFF;
uint8_t db = dst & 0xFF;
uint32_t inv = 255 - sa;
uint32_t rr = (sa * sr + inv * dr + 128) / 255;
uint32_t gg = (sa * sg + inv * dg + 128) / 255;
uint32_t bb = (sa * sb + inv * db + 128) / 255;
pixels[row * screen_w + col] =
0xFF000000 | (rr << 16) | (gg << 8) | bb;
}
}
}
void draw_rect_outline(int x, int y, int w, int h, uint32_t color) {
fill_rect(x, y, w, 1, color);
fill_rect(x, y + h - 1, w, 1, color);
fill_rect(x, y, 1, h, color);
fill_rect(x + w - 1, y, 1, h, color);
}
void draw_text(int x, int y, const char* text, gui::Color c, int size = 16) {
if (font)
font->draw_to_buffer(pixels, screen_w, screen_h, x, y, text, c, size);
}
int text_width(const char* text, int size = 16) {
return font ? font->measure_text(text, size) : 0;
}
int text_height(int size = 16) {
if (!font) return size;
auto* gc = font->get_cache(size);
if (!gc) return size;
return gc->ascent - gc->descent;
}
};
// ============================================================================
// File loading helper
// ============================================================================
struct FileData {
uint8_t* data = nullptr;
uint64_t size = 0;
bool load(const char* vfs_path) {
int fd = montauk::open(vfs_path);
if (fd < 0) return false;
size = montauk::getsize(fd);
if (size == 0 || size > 16 * 1024 * 1024) {
montauk::close(fd);
return false;
}
data = (uint8_t*)montauk::malloc(size);
if (!data) { montauk::close(fd); return false; }
montauk::read(fd, data, 0, size);
montauk::close(fd);
return true;
}
void free() {
if (data) { montauk::mfree(data); data = nullptr; }
size = 0;
}
};
} // namespace engine