1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
|
/**
* @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>
#include <stdio.h>
#include <stdlib.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;
}
}
}
}
}
}
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);
}
|