summaryrefslogtreecommitdiff
path: root/examples/game.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/game.c')
-rw-r--r--examples/game.c403
1 files changed, 48 insertions, 355 deletions
diff --git a/examples/game.c b/examples/game.c
index 33c2f6e..79beb7c 100644
--- a/examples/game.c
+++ b/examples/game.c
@@ -1,360 +1,53 @@
-/**
- * @file game.c
- * A continuous 60/144+ FPS game loop demonstration built on YASL Approach A.
- *
- * Demonstrates:
- * - Continuous display-synchronized ticks (YASL_PACING_CONTINUOUS)
- * - 3D software rendering (rotating shaded cube + starfield)
- * - WASD continuous player movement & key state tracking
- * - Relative pointer mode (YASL_POINTER_RELATIVE) for smooth first-person camera orbit
- * - Spacebar toggles pointer lock (Relative vs Normal cursor)
- * - Real-time FPS counter and frame latency display
- * - Dynamic window resizing with automatic aspect ratio adjustment
- */
-
-#define _DEFAULT_SOURCE
-#include <yasl/app.h>
-
-#include <math.h>
-#include <stdbool.h>
-#include <stdint.h>
+/* GPU example: the application owns context binding, drawing and swap pacing. */
+#include <yasl/yasl.h>
+#include <yasl/gl.h>
#include <stdio.h>
-#include <stdlib.h>
+#include <math.h>
#include <string.h>
-#define NUM_STARS 256
-#define FOV_SCALE 320.0f
-
-struct star {
- float x, y, z;
-};
-
-struct vec3 {
- float x, y, z;
-};
-
-struct game_state {
- struct yasl_display *display;
- struct yasl_surface *surface;
-
- /* Camera & controls */
- float cam_x, cam_y, cam_z;
- float cam_yaw, cam_pitch;
- bool key_w, key_a, key_s, key_d;
- bool key_q, key_e;
- bool relative_mouse;
-
- /* Simulation objects */
- float cube_rot_x, cube_rot_y;
- struct star stars[NUM_STARS];
-
- /* FPS & timing stats */
- uint64_t last_stat_time_ns;
- int frame_count;
- float current_fps;
- float avg_frame_time_ms;
-};
-
-/* 8x8 bitmap font for overlay HUD */
-static const uint8_t font8x8_digits[10][8] = {
- {0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00}, /* 0 */
- {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
- {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
- {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
- {0x0C,0x1C,0x34,0x64,0x7E,0x04,0x04,0x00}, /* 4 */
- {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
- {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
- {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
- {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
- {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
-};
-
-static void draw_digit(uint32_t *pix, int w, int h, int px, int py, int digit, uint32_t col, int s)
-{
- if (digit < 0 || digit > 9) return;
- const uint8_t *glyph = font8x8_digits[digit];
- for (int y = 0; y < 8; y++) {
- uint8_t row = glyph[y];
- for (int x = 0; x < 8; x++) {
- if (row & (0x80 >> x)) {
- for (int dy = 0; dy < s; dy++) {
- int yy = py + y * s + dy;
- if (yy < 0 || yy >= h) continue;
- for (int dx = 0; dx < s; dx++) {
- int xx = px + x * s + dx;
- if (xx >= 0 && xx < w) pix[(size_t)yy * w + xx] = col;
- }
- }
- }
+typedef void (*clear_color_fn)(float, float, float, float);
+typedef void (*clear_fn)(unsigned);
+typedef void (*viewport_fn)(int, int, int, int);
+
+int main(int argc, char **argv)
+{
+ bool smoke = argc == 2 && !strcmp(argv[1], "--smoke-test");
+ yasl_surface *s = yasl_gl_surface_create("YASL OpenGL", 800, 600, 0, NULL, NULL);
+ if (!s || !yasl_gl_make_current(s)) {
+ const char *error;
+ yasl_get_error(&error);
+ fprintf(stderr, "%s\n", error);
+ yasl_shutdown();
+ return 1;
+ }
+ clear_color_fn clear_color = (clear_color_fn)yasl_gl_get_proc_address("glClearColor");
+ clear_fn clear = (clear_fn)yasl_gl_get_proc_address("glClear");
+ viewport_fn viewport = (viewport_fn)yasl_gl_get_proc_address("glViewport");
+ if (!clear_color || !clear || !viewport) {
+ yasl_shutdown();
+ return 1;
+ }
+ yasl_gl_swap_interval(s, 1);
+ int result = 0, frames = 0;
+ for (bool running = true; running;) {
+ yasl_event e;
+ int n;
+ while ((n = yasl_wait_event(&e, 0)) > 0) {
+ if (e.type == YASL_EVENT_CLOSE || e.type == YASL_EVENT_QUIT ||
+ (e.type == YASL_EVENT_KEY && e.key.key == YASL_KEY_ESCAPE)) running = false;
+ if (e.type == YASL_EVENT_DISPLAY_LOST) { result = 1; running = false; }
}
- }
-}
-
-static void draw_number(uint32_t *pix, int w, int h, int px, int py, int num, uint32_t col, int scale)
-{
- char buf[16];
- snprintf(buf, sizeof(buf), "%d", num);
- for (int i = 0; buf[i]; i++) {
- draw_digit(pix, w, h, px + i * 8 * scale, py, buf[i] - '0', col, scale);
- }
-}
-
-static void draw_line(uint32_t *pix, int w, int h, int x0, int y0, int x1, int y1, uint32_t col)
-{
- int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
- int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
- int err = dx + dy, e2;
-
- while (1) {
- if (x0 >= 0 && x0 < w && y0 >= 0 && y0 < h) {
- pix[(size_t)y0 * w + x0] = col;
- }
- if (x0 == x1 && y0 == y1) break;
- e2 = 2 * err;
- if (e2 >= dy) { err += dy; x0 += sx; }
- if (e2 <= dx) { err += dx; y0 += sy; }
- }
-}
-
-static struct vec3 rotate_vertex(struct vec3 v, float rx, float ry)
-{
- /* Rotate around Y */
- float cy = cosf(ry), sy = sinf(ry);
- float x1 = v.x * cy + v.z * sy;
- float z1 = -v.x * sy + v.z * cy;
-
- /* Rotate around X */
- float cx = cosf(rx), sx = sinf(rx);
- float y2 = v.y * cx - z1 * sx;
- float z2 = v.y * sx + z1 * cx;
-
- return (struct vec3){x1, y2, z2};
-}
-
-static bool project_point(struct vec3 p, int w, int h, int *out_x, int *out_y)
-{
- if (p.z <= 0.2f) return false;
- float inv_z = FOV_SCALE / p.z;
- *out_x = (int)(w * 0.5f + p.x * inv_z);
- *out_y = (int)(h * 0.5f - p.y * inv_z);
- return true;
-}
-
-static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
-{
- struct game_state *game = ud;
- game->display = d;
-
- struct yasl_window_spec spec = {
- .title = "YASL 3D Game Loop (Approach A)",
- .size_logical = {800, 600},
- };
- game->surface = yasl_surface_create_toplevel(d, &spec);
- if (!game->surface) return YASL_APP_ERROR;
-
- /* Drive continuous 60/144Hz ticks */
- if (!yasl_surface_set_pacing(game->surface, YASL_PACING_CONTINUOUS))
- return YASL_APP_ERROR;
-
- game->cam_z = 3.5f;
- game->relative_mouse = false;
-
- /* Initialize stars */
- for (int i = 0; i < NUM_STARS; i++) {
- game->stars[i].x = ((float)(rand() % 2000) - 1000.0f) / 100.0f;
- game->stars[i].y = ((float)(rand() % 2000) - 1000.0f) / 100.0f;
- game->stars[i].z = ((float)(rand() % 1000) + 10.0f) / 50.0f;
- }
-
- game->last_stat_time_ns = yasl_loop_now_ns();
- return YASL_APP_CONTINUE;
-}
-
-static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
-{
- struct game_state *game = ud;
-
- switch (ev->type) {
- case YASL_EVENT_KEY: {
- bool down = ev->key.action != YASL_KEY_RELEASE;
-
- switch (ev->key.key) {
- case YASL_KEY_W: game->key_w = down; break;
- case YASL_KEY_S: game->key_s = down; break;
- case YASL_KEY_A: game->key_a = down; break;
- case YASL_KEY_D: game->key_d = down; break;
- case YASL_KEY_Q: game->key_q = down; break;
- case YASL_KEY_E: game->key_e = down; break;
- case YASL_KEY_SPACE:
- if (ev->key.action == YASL_KEY_PRESS) {
- /* Toggle relative pointer mode; unsupported backends stay normal. */
- game->relative_mouse = !game->relative_mouse &&
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_RELATIVE);
- if (!game->relative_mouse)
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_NORMAL);
- }
- break;
- case YASL_KEY_ESCAPE:
- if (ev->key.action == YASL_KEY_PRESS) {
- if (game->relative_mouse) {
- game->relative_mouse = false;
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_NORMAL);
- } else {
- return YASL_APP_QUIT;
- }
- }
- break;
- default:
- break;
- }
- break;
- }
-
- case YASL_EVENT_POINTER_MOTION:
- if (game->relative_mouse) {
- /* Smooth first-person camera orbit */
- game->cam_yaw += (float)ev->pointer.delta.x * 0.005f;
- game->cam_pitch += (float)ev->pointer.delta.y * 0.005f;
- if (game->cam_pitch > 1.4f) game->cam_pitch = 1.4f;
- if (game->cam_pitch < -1.4f) game->cam_pitch = -1.4f;
- }
- break;
-
- case YASL_EVENT_POINTER_DOWN:
- if (!game->relative_mouse) {
- /* Click window to capture cursor */
- game->relative_mouse =
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_RELATIVE);
- }
- break;
-
- case YASL_EVENT_WINDOW_CLOSE:
- return YASL_APP_QUIT;
-
- default:
- break;
- }
-
- return YASL_APP_CONTINUE;
-}
-
-static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
-{
- (void)s;
- struct game_state *game = ud;
- int w = ctx->target.size.x;
- int h = ctx->target.size.y;
- uint32_t *pix = ctx->target.pixels;
- if (!pix || w <= 0 || h <= 0) return;
-
- /* Update game simulation */
- float speed = 0.04f;
- float forward_x = sinf(game->cam_yaw);
- float forward_z = cosf(game->cam_yaw);
- float right_x = cosf(game->cam_yaw);
- float right_z = -sinf(game->cam_yaw);
-
- if (game->key_w) { game->cam_x -= forward_x * speed; game->cam_z -= forward_z * speed; }
- if (game->key_s) { game->cam_x += forward_x * speed; game->cam_z += forward_z * speed; }
- if (game->key_a) { game->cam_x -= right_x * speed; game->cam_z -= right_z * speed; }
- if (game->key_d) { game->cam_x += right_x * speed; game->cam_z += right_z * speed; }
- if (game->key_q) { game->cam_y -= speed; }
- if (game->key_e) { game->cam_y += speed; }
-
- game->cube_rot_x += 0.015f;
- game->cube_rot_y += 0.025f;
-
- /* Clear screen to deep space dark blue */
- memset(pix, 0x08, (size_t)w * h * sizeof(uint32_t));
-
- /* 1. Render 3D Starfield */
- for (int i = 0; i < NUM_STARS; i++) {
- game->stars[i].z -= 0.05f;
- if (game->stars[i].z <= 0.2f) game->stars[i].z += 20.0f;
-
- struct vec3 sp = {game->stars[i].x - game->cam_x,
- game->stars[i].y - game->cam_y,
- game->stars[i].z};
- int sx, sy;
- if (project_point(sp, w, h, &sx, &sy) && sx >= 0 && sx < w && sy >= 0 && sy < h) {
- uint8_t brightness = (uint8_t)(255.0f * (1.0f - sp.z / 20.0f));
- uint32_t star_col = 0xff000000 | (brightness << 16) | (brightness << 8) | brightness;
- pix[(size_t)sy * w + sx] = star_col;
- }
- }
-
- /* 2. Render 3D Cube */
- static const struct vec3 cube_vertices[8] = {
- {-1.0f, -1.0f, -1.0f}, { 1.0f, -1.0f, -1.0f},
- { 1.0f, 1.0f, -1.0f}, {-1.0f, 1.0f, -1.0f},
- {-1.0f, -1.0f, 1.0f}, { 1.0f, -1.0f, 1.0f},
- { 1.0f, 1.0f, 1.0f}, {-1.0f, 1.0f, 1.0f},
- };
-
- static const int cube_edges[12][2] = {
- {0, 1}, {1, 2}, {2, 3}, {3, 0},
- {4, 5}, {5, 6}, {6, 7}, {7, 4},
- {0, 4}, {1, 5}, {2, 6}, {3, 7},
- };
-
- int proj_x[8], proj_y[8];
- bool valid[8];
-
- for (int i = 0; i < 8; i++) {
- struct vec3 rot = rotate_vertex(cube_vertices[i], game->cube_rot_x, game->cube_rot_y);
- rot.x -= game->cam_x;
- rot.y -= game->cam_y;
- rot.z += game->cam_z;
- valid[i] = project_point(rot, w, h, &proj_x[i], &proj_y[i]);
- }
-
- uint32_t cyan = 0xff00ffff;
- for (int i = 0; i < 12; i++) {
- int u = cube_edges[i][0];
- int v = cube_edges[i][1];
- if (valid[u] && valid[v]) {
- draw_line(pix, w, h, proj_x[u], proj_y[u], proj_x[v], proj_y[v], cyan);
- }
- }
-
- /* 3. Center crosshair in relative mouse mode */
- if (game->relative_mouse) {
- int cx = w / 2, cy = h / 2;
- draw_line(pix, w, h, cx - 6, cy, cx + 6, cy, 0xffffffff);
- draw_line(pix, w, h, cx, cy - 6, cx, cy + 6, 0xffffffff);
- }
-
- /* 4. FPS counter calculation */
- game->frame_count++;
- uint64_t now = yasl_loop_now_ns();
- if (now - game->last_stat_time_ns >= 1000000000ull) {
- uint64_t elapsed = now - game->last_stat_time_ns;
- game->current_fps = (float)game->frame_count * 1000000000.0f / (float)elapsed;
- game->avg_frame_time_ms = 1000.0f / (game->current_fps > 0 ? game->current_fps : 1.0f);
- game->frame_count = 0;
- game->last_stat_time_ns = now;
- }
-
- /* Draw FPS overlay digits in top-left */
- int scale = ctx->scale_120 >= 240 ? 2 : 1;
- draw_number(pix, w, h, 16 * scale, 16 * scale, (int)game->current_fps, 0xff00ff00, scale);
-
- /* Submit frame */
- if (!yasl_frame_present(ctx, NULL, 0))
- yasl_app_exit(yasl_surface_display(s), YASL_APP_ERROR);
-}
-
-int main(void)
-{
- static struct game_state game;
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.game",
- .backend = YASL_BACKEND_AUTO,
- .userdata = &game,
- .init = on_init,
- .event = on_event,
- .frame = on_frame,
- };
-
- return yasl_run(&desc);
+ if (n < 0) { result = 1; break; }
+ int w, h;
+ yasl_surface_get_framebuffer_size(s, &w, &h);
+ viewport(0, 0, w, h);
+ double t = yasl_time_ns() / 1e9;
+ clear_color((float)(0.3 + 0.2 * sin(t)), 0.2f, 0.4f, 1);
+ clear(0x00004000u);
+ if (!yasl_gl_swap_buffers(s)) { result = 1; break; }
+ if (smoke && ++frames == 3) break;
+ }
+ yasl_gl_make_current(NULL);
+ yasl_shutdown();
+ return result;
}