rgame 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +94 -0
- data/README.md +130 -233
- data/docs/api/README.md +116 -69
- data/docs/api/assets.md +11 -12
- data/docs/api/components.md +58 -34
- data/docs/api/drawing.md +77 -9
- data/docs/api/game.md +34 -13
- data/docs/api/input.md +232 -51
- data/docs/api/scene_graph.md +242 -15
- data/docs/api/systems.md +20 -0
- data/docs/api/toolbox.md +19 -15
- data/docs/api/ui.md +98 -0
- data/docs/api/values.md +32 -0
- data/ext/README.md +6 -5
- data/ext/rgame_core/app/app.c +182 -8
- data/ext/rgame_core/audio/audio.c +74 -0
- data/ext/rgame_core/example.rb +17 -6
- data/ext/rgame_core/extconf.rb +52 -24
- data/ext/rgame_core/graphics/canvas.c +45 -4
- data/ext/rgame_core/graphics/canvas.h +65 -10
- data/ext/rgame_core/graphics/clip.c +22 -13
- data/ext/rgame_core/include/rgame/core.h +113 -3
- data/ext/rgame_core/input/gamepad.c +57 -3
- data/ext/rgame_core/ruby/core_ext.c +16 -0
- data/ext/rgame_core/ruby/renderer_ext.c +23 -0
- data/ext/rgame_util/color_ext.c +12 -3
- data/lib/rgame/core/app.rb +2 -0
- data/lib/rgame/core/input.rb +35 -41
- data/lib/rgame/core/recording.rb +3 -1
- data/lib/rgame/core/renderer.rb +76 -28
- data/lib/rgame/core/tile_map_renderer.rb +84 -55
- data/lib/rgame/engine/camera.rb +55 -10
- data/lib/rgame/engine/component.rb +11 -1
- data/lib/rgame/engine/components/animated_sprite.rb +9 -3
- data/lib/rgame/engine/components/camera_follow.rb +44 -0
- data/lib/rgame/engine/components/character_body.rb +25 -4
- data/lib/rgame/engine/components/sprite.rb +11 -1
- data/lib/rgame/engine/components/tile_world.rb +31 -18
- data/lib/rgame/engine/culling.rb +47 -0
- data/lib/rgame/engine/debug_overlay.rb +20 -9
- data/lib/rgame/engine/input/action_mapper.rb +101 -21
- data/lib/rgame/engine/input/actions.rb +69 -12
- data/lib/rgame/engine/input/input_map.rb +178 -0
- data/lib/rgame/engine/layout.rb +82 -0
- data/lib/rgame/engine/node2d.rb +205 -36
- data/lib/rgame/engine/player.rb +69 -0
- data/lib/rgame/engine/player_layer.rb +70 -0
- data/lib/rgame/engine/players.rb +212 -0
- data/lib/rgame/engine/scene/scene_stack.rb +25 -3
- data/lib/rgame/engine/spatial_hash.rb +17 -4
- data/lib/rgame/engine/tile_map_layer.rb +84 -0
- data/lib/rgame/engine/ui/menu.rb +115 -0
- data/lib/rgame/engine/ui/menu_item.rb +84 -0
- data/lib/rgame/engine/view.rb +76 -0
- data/lib/rgame/engine/viewports.rb +174 -0
- data/lib/rgame/engine/world_view.rb +70 -0
- data/lib/rgame/engine.rb +13 -1
- data/lib/rgame/game.rb +81 -11
- data/lib/rgame/util/controls.rb +117 -41
- data/lib/rgame/util/z.rb +133 -0
- data/lib/rgame/util.rb +1 -0
- data/lib/rgame/version.rb +1 -1
- metadata +26 -11
- data/lib/rgame/engine/camera_view.rb +0 -28
data/ext/rgame_core/app/app.c
CHANGED
|
@@ -227,14 +227,168 @@ void rgame_app_gl_restore(const rgame_gl_context_save *saved) {
|
|
|
227
227
|
* the compiler checks — it must not include SDL. These assertions close that
|
|
228
228
|
* gap at compile time, so the two can never drift apart silently.
|
|
229
229
|
*/
|
|
230
|
-
_Static_assert(
|
|
231
|
-
|
|
232
|
-
_Static_assert(
|
|
233
|
-
|
|
234
|
-
_Static_assert(
|
|
235
|
-
|
|
236
|
-
_Static_assert(
|
|
237
|
-
|
|
230
|
+
_Static_assert(RGAME_KEY_A == SDL_SCANCODE_A,
|
|
231
|
+
"key id must match SDL scancode");
|
|
232
|
+
_Static_assert(RGAME_KEY_B == SDL_SCANCODE_B,
|
|
233
|
+
"key id must match SDL scancode");
|
|
234
|
+
_Static_assert(RGAME_KEY_C == SDL_SCANCODE_C,
|
|
235
|
+
"key id must match SDL scancode");
|
|
236
|
+
_Static_assert(RGAME_KEY_D == SDL_SCANCODE_D,
|
|
237
|
+
"key id must match SDL scancode");
|
|
238
|
+
_Static_assert(RGAME_KEY_E == SDL_SCANCODE_E,
|
|
239
|
+
"key id must match SDL scancode");
|
|
240
|
+
_Static_assert(RGAME_KEY_F == SDL_SCANCODE_F,
|
|
241
|
+
"key id must match SDL scancode");
|
|
242
|
+
_Static_assert(RGAME_KEY_G == SDL_SCANCODE_G,
|
|
243
|
+
"key id must match SDL scancode");
|
|
244
|
+
_Static_assert(RGAME_KEY_H == SDL_SCANCODE_H,
|
|
245
|
+
"key id must match SDL scancode");
|
|
246
|
+
_Static_assert(RGAME_KEY_I == SDL_SCANCODE_I,
|
|
247
|
+
"key id must match SDL scancode");
|
|
248
|
+
_Static_assert(RGAME_KEY_J == SDL_SCANCODE_J,
|
|
249
|
+
"key id must match SDL scancode");
|
|
250
|
+
_Static_assert(RGAME_KEY_K == SDL_SCANCODE_K,
|
|
251
|
+
"key id must match SDL scancode");
|
|
252
|
+
_Static_assert(RGAME_KEY_L == SDL_SCANCODE_L,
|
|
253
|
+
"key id must match SDL scancode");
|
|
254
|
+
_Static_assert(RGAME_KEY_M == SDL_SCANCODE_M,
|
|
255
|
+
"key id must match SDL scancode");
|
|
256
|
+
_Static_assert(RGAME_KEY_N == SDL_SCANCODE_N,
|
|
257
|
+
"key id must match SDL scancode");
|
|
258
|
+
_Static_assert(RGAME_KEY_O == SDL_SCANCODE_O,
|
|
259
|
+
"key id must match SDL scancode");
|
|
260
|
+
_Static_assert(RGAME_KEY_P == SDL_SCANCODE_P,
|
|
261
|
+
"key id must match SDL scancode");
|
|
262
|
+
_Static_assert(RGAME_KEY_Q == SDL_SCANCODE_Q,
|
|
263
|
+
"key id must match SDL scancode");
|
|
264
|
+
_Static_assert(RGAME_KEY_R == SDL_SCANCODE_R,
|
|
265
|
+
"key id must match SDL scancode");
|
|
266
|
+
_Static_assert(RGAME_KEY_S == SDL_SCANCODE_S,
|
|
267
|
+
"key id must match SDL scancode");
|
|
268
|
+
_Static_assert(RGAME_KEY_T == SDL_SCANCODE_T,
|
|
269
|
+
"key id must match SDL scancode");
|
|
270
|
+
_Static_assert(RGAME_KEY_U == SDL_SCANCODE_U,
|
|
271
|
+
"key id must match SDL scancode");
|
|
272
|
+
_Static_assert(RGAME_KEY_V == SDL_SCANCODE_V,
|
|
273
|
+
"key id must match SDL scancode");
|
|
274
|
+
_Static_assert(RGAME_KEY_W == SDL_SCANCODE_W,
|
|
275
|
+
"key id must match SDL scancode");
|
|
276
|
+
_Static_assert(RGAME_KEY_X == SDL_SCANCODE_X,
|
|
277
|
+
"key id must match SDL scancode");
|
|
278
|
+
_Static_assert(RGAME_KEY_Y == SDL_SCANCODE_Y,
|
|
279
|
+
"key id must match SDL scancode");
|
|
280
|
+
_Static_assert(RGAME_KEY_Z == SDL_SCANCODE_Z,
|
|
281
|
+
"key id must match SDL scancode");
|
|
282
|
+
_Static_assert(RGAME_KEY_1 == SDL_SCANCODE_1,
|
|
283
|
+
"key id must match SDL scancode");
|
|
284
|
+
_Static_assert(RGAME_KEY_2 == SDL_SCANCODE_2,
|
|
285
|
+
"key id must match SDL scancode");
|
|
286
|
+
_Static_assert(RGAME_KEY_3 == SDL_SCANCODE_3,
|
|
287
|
+
"key id must match SDL scancode");
|
|
288
|
+
_Static_assert(RGAME_KEY_4 == SDL_SCANCODE_4,
|
|
289
|
+
"key id must match SDL scancode");
|
|
290
|
+
_Static_assert(RGAME_KEY_5 == SDL_SCANCODE_5,
|
|
291
|
+
"key id must match SDL scancode");
|
|
292
|
+
_Static_assert(RGAME_KEY_6 == SDL_SCANCODE_6,
|
|
293
|
+
"key id must match SDL scancode");
|
|
294
|
+
_Static_assert(RGAME_KEY_7 == SDL_SCANCODE_7,
|
|
295
|
+
"key id must match SDL scancode");
|
|
296
|
+
_Static_assert(RGAME_KEY_8 == SDL_SCANCODE_8,
|
|
297
|
+
"key id must match SDL scancode");
|
|
298
|
+
_Static_assert(RGAME_KEY_9 == SDL_SCANCODE_9,
|
|
299
|
+
"key id must match SDL scancode");
|
|
300
|
+
_Static_assert(RGAME_KEY_0 == SDL_SCANCODE_0,
|
|
301
|
+
"key id must match SDL scancode");
|
|
302
|
+
_Static_assert(RGAME_KEY_RETURN == SDL_SCANCODE_RETURN,
|
|
303
|
+
"key id must match SDL scancode");
|
|
304
|
+
_Static_assert(RGAME_KEY_ESCAPE == SDL_SCANCODE_ESCAPE,
|
|
305
|
+
"key id must match SDL scancode");
|
|
306
|
+
_Static_assert(RGAME_KEY_BACKSPACE == SDL_SCANCODE_BACKSPACE,
|
|
307
|
+
"key id must match SDL scancode");
|
|
308
|
+
_Static_assert(RGAME_KEY_TAB == SDL_SCANCODE_TAB,
|
|
309
|
+
"key id must match SDL scancode");
|
|
310
|
+
_Static_assert(RGAME_KEY_SPACE == SDL_SCANCODE_SPACE,
|
|
311
|
+
"key id must match SDL scancode");
|
|
312
|
+
_Static_assert(RGAME_KEY_MINUS == SDL_SCANCODE_MINUS,
|
|
313
|
+
"key id must match SDL scancode");
|
|
314
|
+
_Static_assert(RGAME_KEY_EQUALS == SDL_SCANCODE_EQUALS,
|
|
315
|
+
"key id must match SDL scancode");
|
|
316
|
+
_Static_assert(RGAME_KEY_LEFTBRACKET == SDL_SCANCODE_LEFTBRACKET,
|
|
317
|
+
"key id must match SDL scancode");
|
|
318
|
+
_Static_assert(RGAME_KEY_RIGHTBRACKET == SDL_SCANCODE_RIGHTBRACKET,
|
|
319
|
+
"key id must match SDL scancode");
|
|
320
|
+
_Static_assert(RGAME_KEY_BACKSLASH == SDL_SCANCODE_BACKSLASH,
|
|
321
|
+
"key id must match SDL scancode");
|
|
322
|
+
_Static_assert(RGAME_KEY_SEMICOLON == SDL_SCANCODE_SEMICOLON,
|
|
323
|
+
"key id must match SDL scancode");
|
|
324
|
+
_Static_assert(RGAME_KEY_APOSTROPHE == SDL_SCANCODE_APOSTROPHE,
|
|
325
|
+
"key id must match SDL scancode");
|
|
326
|
+
_Static_assert(RGAME_KEY_GRAVE == SDL_SCANCODE_GRAVE,
|
|
327
|
+
"key id must match SDL scancode");
|
|
328
|
+
_Static_assert(RGAME_KEY_COMMA == SDL_SCANCODE_COMMA,
|
|
329
|
+
"key id must match SDL scancode");
|
|
330
|
+
_Static_assert(RGAME_KEY_PERIOD == SDL_SCANCODE_PERIOD,
|
|
331
|
+
"key id must match SDL scancode");
|
|
332
|
+
_Static_assert(RGAME_KEY_SLASH == SDL_SCANCODE_SLASH,
|
|
333
|
+
"key id must match SDL scancode");
|
|
334
|
+
_Static_assert(RGAME_KEY_CAPSLOCK == SDL_SCANCODE_CAPSLOCK,
|
|
335
|
+
"key id must match SDL scancode");
|
|
336
|
+
_Static_assert(RGAME_KEY_F1 == SDL_SCANCODE_F1,
|
|
337
|
+
"key id must match SDL scancode");
|
|
338
|
+
_Static_assert(RGAME_KEY_F2 == SDL_SCANCODE_F2,
|
|
339
|
+
"key id must match SDL scancode");
|
|
340
|
+
_Static_assert(RGAME_KEY_F3 == SDL_SCANCODE_F3,
|
|
341
|
+
"key id must match SDL scancode");
|
|
342
|
+
_Static_assert(RGAME_KEY_F4 == SDL_SCANCODE_F4,
|
|
343
|
+
"key id must match SDL scancode");
|
|
344
|
+
_Static_assert(RGAME_KEY_F5 == SDL_SCANCODE_F5,
|
|
345
|
+
"key id must match SDL scancode");
|
|
346
|
+
_Static_assert(RGAME_KEY_F6 == SDL_SCANCODE_F6,
|
|
347
|
+
"key id must match SDL scancode");
|
|
348
|
+
_Static_assert(RGAME_KEY_F7 == SDL_SCANCODE_F7,
|
|
349
|
+
"key id must match SDL scancode");
|
|
350
|
+
_Static_assert(RGAME_KEY_F8 == SDL_SCANCODE_F8,
|
|
351
|
+
"key id must match SDL scancode");
|
|
352
|
+
_Static_assert(RGAME_KEY_F9 == SDL_SCANCODE_F9,
|
|
353
|
+
"key id must match SDL scancode");
|
|
354
|
+
_Static_assert(RGAME_KEY_F10 == SDL_SCANCODE_F10,
|
|
355
|
+
"key id must match SDL scancode");
|
|
356
|
+
_Static_assert(RGAME_KEY_F11 == SDL_SCANCODE_F11,
|
|
357
|
+
"key id must match SDL scancode");
|
|
358
|
+
_Static_assert(RGAME_KEY_F12 == SDL_SCANCODE_F12,
|
|
359
|
+
"key id must match SDL scancode");
|
|
360
|
+
_Static_assert(RGAME_KEY_INSERT == SDL_SCANCODE_INSERT,
|
|
361
|
+
"key id must match SDL scancode");
|
|
362
|
+
_Static_assert(RGAME_KEY_HOME == SDL_SCANCODE_HOME,
|
|
363
|
+
"key id must match SDL scancode");
|
|
364
|
+
_Static_assert(RGAME_KEY_PAGEUP == SDL_SCANCODE_PAGEUP,
|
|
365
|
+
"key id must match SDL scancode");
|
|
366
|
+
_Static_assert(RGAME_KEY_DELETE == SDL_SCANCODE_DELETE,
|
|
367
|
+
"key id must match SDL scancode");
|
|
368
|
+
_Static_assert(RGAME_KEY_END == SDL_SCANCODE_END,
|
|
369
|
+
"key id must match SDL scancode");
|
|
370
|
+
_Static_assert(RGAME_KEY_PAGEDOWN == SDL_SCANCODE_PAGEDOWN,
|
|
371
|
+
"key id must match SDL scancode");
|
|
372
|
+
_Static_assert(RGAME_KEY_RIGHT == SDL_SCANCODE_RIGHT,
|
|
373
|
+
"key id must match SDL scancode");
|
|
374
|
+
_Static_assert(RGAME_KEY_LEFT == SDL_SCANCODE_LEFT,
|
|
375
|
+
"key id must match SDL scancode");
|
|
376
|
+
_Static_assert(RGAME_KEY_DOWN == SDL_SCANCODE_DOWN,
|
|
377
|
+
"key id must match SDL scancode");
|
|
378
|
+
_Static_assert(RGAME_KEY_UP == SDL_SCANCODE_UP,
|
|
379
|
+
"key id must match SDL scancode");
|
|
380
|
+
_Static_assert(RGAME_KEY_LCTRL == SDL_SCANCODE_LCTRL,
|
|
381
|
+
"key id must match SDL scancode");
|
|
382
|
+
_Static_assert(RGAME_KEY_LSHIFT == SDL_SCANCODE_LSHIFT,
|
|
383
|
+
"key id must match SDL scancode");
|
|
384
|
+
_Static_assert(RGAME_KEY_LALT == SDL_SCANCODE_LALT,
|
|
385
|
+
"key id must match SDL scancode");
|
|
386
|
+
_Static_assert(RGAME_KEY_RCTRL == SDL_SCANCODE_RCTRL,
|
|
387
|
+
"key id must match SDL scancode");
|
|
388
|
+
_Static_assert(RGAME_KEY_RSHIFT == SDL_SCANCODE_RSHIFT,
|
|
389
|
+
"key id must match SDL scancode");
|
|
390
|
+
_Static_assert(RGAME_KEY_RALT == SDL_SCANCODE_RALT,
|
|
391
|
+
"key id must match SDL scancode");
|
|
238
392
|
|
|
239
393
|
/* The snapshot array must be exactly as long as SDL's keyboard state array,
|
|
240
394
|
* since filling it is a straight copy. */
|
|
@@ -400,6 +554,9 @@ void rgame_app_run(rgame_app *app, const rgame_app_callbacks *cb) {
|
|
|
400
554
|
|
|
401
555
|
rgame_draw_backend backend = rgame_gl_backend_table(&app->gl);
|
|
402
556
|
rgame_canvas_submit(&app->canvas, &backend);
|
|
557
|
+
if (cb->frame_end) {
|
|
558
|
+
cb->frame_end(cb->userdata);
|
|
559
|
+
}
|
|
403
560
|
SDL_GL_SwapWindow(app->window);
|
|
404
561
|
}
|
|
405
562
|
}
|
|
@@ -599,6 +756,23 @@ int rgame_app_push_clip(rgame_app *app, int x, int y, int width, int height) {
|
|
|
599
756
|
return 1;
|
|
600
757
|
}
|
|
601
758
|
|
|
759
|
+
void rgame_app_push_layer(rgame_app *app, double base) {
|
|
760
|
+
rgame_canvas *canvas = drawing_canvas(app);
|
|
761
|
+
if (canvas) {
|
|
762
|
+
rgame_canvas_push_layer(canvas, base);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
double rgame_app_layer(rgame_app *app) {
|
|
767
|
+
rgame_canvas *canvas = drawing_canvas(app);
|
|
768
|
+
return canvas ? rgame_canvas_layer(canvas) : 0.0;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
unsigned int rgame_app_next_layer_slot(rgame_app *app, int band) {
|
|
772
|
+
rgame_canvas *canvas = drawing_canvas(app);
|
|
773
|
+
return canvas ? rgame_canvas_next_slot(canvas, band) : 0;
|
|
774
|
+
}
|
|
775
|
+
|
|
602
776
|
void rgame_app_pop(rgame_app *app) {
|
|
603
777
|
rgame_canvas *canvas = drawing_canvas(app);
|
|
604
778
|
if (canvas) {
|
|
@@ -44,6 +44,15 @@
|
|
|
44
44
|
struct rgame_audio {
|
|
45
45
|
ma_resource_manager resources;
|
|
46
46
|
ma_engine engine;
|
|
47
|
+
/*
|
|
48
|
+
* Set only on Windows, only when construction fell back to `noDevice`
|
|
49
|
+
* because there was nothing to open — see create_audio. Exists so
|
|
50
|
+
* rgame_audio_backend can still answer "Null" for this engine, matching
|
|
51
|
+
* what every other no-hardware case on every platform already reports,
|
|
52
|
+
* even though this engine has no `ma_device` at all for the normal
|
|
53
|
+
* lookup to find.
|
|
54
|
+
*/
|
|
55
|
+
int forced_no_device;
|
|
47
56
|
};
|
|
48
57
|
|
|
49
58
|
struct rgame_sample {
|
|
@@ -131,6 +140,43 @@ static rgame_audio *create_audio(int offline, unsigned int sample_rate, char *er
|
|
|
131
140
|
* no sound card — a build server, a container — gets a working engine that
|
|
132
141
|
* plays silence rather than an error a game has to handle. Verified: with
|
|
133
142
|
* ALSA and PulseAudio unavailable, the chosen backend is "Null".
|
|
143
|
+
*
|
|
144
|
+
* That fallback is *inside* the real backend's own device-open attempt —
|
|
145
|
+
* it only helps if opening fails cleanly. Verified on Linux, which is the
|
|
146
|
+
* one platform where it demonstrably does, and where an earlier attempt to
|
|
147
|
+
* generalise the workaround below broke a leg that had been working.
|
|
148
|
+
*
|
|
149
|
+
* On Windows, a machine with zero playback devices at all (every GitHub
|
|
150
|
+
* Actions Windows runner, among others: confirmed via
|
|
151
|
+
* `Get-CimInstance Win32_SoundDevice`) crashes instead of failing inside
|
|
152
|
+
* WASAPI's default-device lookup, so the fallback above never gets a
|
|
153
|
+
* chance to run.
|
|
154
|
+
*
|
|
155
|
+
* Windows alone gets an extra step first: enumerate devices (read-only,
|
|
156
|
+
* does not attempt to open anything, so it does not hit whatever WASAPI's
|
|
157
|
+
* own open path gets wrong with no endpoint to find) and, when there are
|
|
158
|
+
* none, fall back to `noDevice` — the same mode the offline constructor
|
|
159
|
+
* below uses — rather than the auto-detect list's own real-backend open
|
|
160
|
+
* attempt.
|
|
161
|
+
*
|
|
162
|
+
* macOS looks like the same bug and is not, which is worth stating because
|
|
163
|
+
* extending this branch to it does nothing. A device-less Mac still
|
|
164
|
+
* enumerates a playback device, so a zero-device test never fires there;
|
|
165
|
+
* its equivalent crash is CoreAudio being initialised in a **forked
|
|
166
|
+
* child**, which macOS forbids outright. That belongs to the test harness
|
|
167
|
+
* rather than here — see test/test_main.c.
|
|
168
|
+
*
|
|
169
|
+
* An *explicit* null-backend device was tried first and rejected: it still
|
|
170
|
+
* spins up a real background device thread, and that thread crashed inside
|
|
171
|
+
* a Ruby process in a way the plain-C Check build of the same code never
|
|
172
|
+
* did. `noDevice` sidesteps the question entirely: with no device and no
|
|
173
|
+
* background thread, there is nothing left that could crash the way a
|
|
174
|
+
* thread can.
|
|
175
|
+
*
|
|
176
|
+
* Linux is deliberately excluded rather than merely untested. Generalising
|
|
177
|
+
* this to every platform is what broke its leg once already, and its own
|
|
178
|
+
* fallback is proven — so the rule here is that a platform opts in on the
|
|
179
|
+
* strength of a measured crash, not on the logic looking harmless.
|
|
134
180
|
*/
|
|
135
181
|
ma_engine_config engine = ma_engine_config_init();
|
|
136
182
|
engine.pResourceManager = &audio->resources;
|
|
@@ -141,6 +187,24 @@ static rgame_audio *create_audio(int offline, unsigned int sample_rate, char *er
|
|
|
141
187
|
engine.channels = 2;
|
|
142
188
|
engine.sampleRate = sample_rate;
|
|
143
189
|
}
|
|
190
|
+
#ifdef _WIN32
|
|
191
|
+
else {
|
|
192
|
+
ma_context probe;
|
|
193
|
+
if (ma_context_init(NULL, 0, NULL, &probe) == MA_SUCCESS) {
|
|
194
|
+
ma_uint32 playback_count = 0;
|
|
195
|
+
ma_result enum_result =
|
|
196
|
+
ma_context_get_devices(&probe, NULL, &playback_count, NULL, NULL);
|
|
197
|
+
ma_context_uninit(&probe);
|
|
198
|
+
|
|
199
|
+
if (enum_result == MA_SUCCESS && playback_count == 0) {
|
|
200
|
+
engine.noDevice = MA_TRUE;
|
|
201
|
+
engine.channels = 2;
|
|
202
|
+
engine.sampleRate = 44100;
|
|
203
|
+
audio->forced_no_device = 1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
#endif
|
|
144
208
|
|
|
145
209
|
if (ma_engine_init(&engine, &audio->engine) != MA_SUCCESS) {
|
|
146
210
|
ma_resource_manager_uninit(&audio->resources);
|
|
@@ -199,6 +263,16 @@ const char *rgame_audio_backend(const rgame_audio *audio) {
|
|
|
199
263
|
return "none";
|
|
200
264
|
}
|
|
201
265
|
|
|
266
|
+
/* The forced-noDevice fallback (see create_audio) has no ma_device for
|
|
267
|
+
* the normal lookup below to find, but it is standing in for exactly what
|
|
268
|
+
* the Null backend already means elsewhere — no hardware, silence — so it
|
|
269
|
+
* answers the same way rather than the more literal but less useful
|
|
270
|
+
* "none", which every other caller of this function reserves for "there
|
|
271
|
+
* is no audio object at all". */
|
|
272
|
+
if (audio->forced_no_device) {
|
|
273
|
+
return "Null";
|
|
274
|
+
}
|
|
275
|
+
|
|
202
276
|
ma_device *device = ma_engine_get_device((ma_engine *)&audio->engine);
|
|
203
277
|
return device ? ma_get_backend_name(device->pContext->backend) : "none";
|
|
204
278
|
}
|
data/ext/rgame_core/example.rb
CHANGED
|
@@ -88,16 +88,27 @@ class Example < RGame::Core::App
|
|
|
88
88
|
@ticks += 1
|
|
89
89
|
@spin += dt * 45.0
|
|
90
90
|
speed = 200.0 * dt
|
|
91
|
-
@cursor_x -= speed if
|
|
92
|
-
@cursor_x += speed if
|
|
93
|
-
@cursor_y -= speed if
|
|
94
|
-
@cursor_y += speed if
|
|
91
|
+
@cursor_x -= speed if held?(Controls::KEY_LEFT, Controls::PAD_DPAD_LEFT)
|
|
92
|
+
@cursor_x += speed if held?(Controls::KEY_RIGHT, Controls::PAD_DPAD_RIGHT)
|
|
93
|
+
@cursor_y -= speed if held?(Controls::KEY_UP, Controls::PAD_DPAD_UP)
|
|
94
|
+
@cursor_y += speed if held?(Controls::KEY_DOWN, Controls::PAD_DPAD_DOWN)
|
|
95
95
|
|
|
96
96
|
# Analog sticks are additive on top of the dpad; the keyboard reads 0.0.
|
|
97
|
-
@cursor_x += @input.axis(
|
|
98
|
-
@cursor_y += @input.axis(
|
|
97
|
+
@cursor_x += @input.axis(Controls::AXIS_LEFT_X, device: @device) * speed
|
|
98
|
+
@cursor_y += @input.axis(Controls::AXIS_LEFT_Y, device: @device) * speed
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# Core is the raw layer: Input answers "is this scancode down on this device",
|
|
102
|
+
# and naming the key *and* the pad button for one intent is the caller's job.
|
|
103
|
+
# Asking both costs nothing, because a device only answers for its own kind of
|
|
104
|
+
# input — a gamepad reads false for a scancode.
|
|
105
|
+
#
|
|
106
|
+
# A game does not write this. RGame::Engine::InputMap is a table of exactly
|
|
107
|
+
# these pairs, one entry per action, and the scene graph reads named actions
|
|
108
|
+
# instead. This example is here to exercise Core on its own, so it does
|
|
109
|
+
# without that and shows what the layer above is for.
|
|
110
|
+
def held?(key, pad) = @input.down?(key, device: @device) || @input.down?(pad, device: @device)
|
|
111
|
+
|
|
101
112
|
# One of everything, so a look at the window checks the whole drawing path.
|
|
102
113
|
# Colours are RGame::Util::Color values rather than arrays: a Color is frozen
|
|
103
114
|
# and allocates nothing when it is drawn, which is what per-frame code wants.
|
data/ext/rgame_core/extconf.rb
CHANGED
|
@@ -68,38 +68,66 @@ $INCFLAGS << ' -I$(srcdir)/../rgame_util'
|
|
|
68
68
|
# later compile error.
|
|
69
69
|
abort 'SDL2 not found (pkg-config --exists sdl2 failed). Install libsdl2-dev.' unless pkg_config('sdl2')
|
|
70
70
|
|
|
71
|
-
#
|
|
72
|
-
#
|
|
71
|
+
# The GL header, probed as the sources actually spell it: SDL's own
|
|
72
|
+
# <SDL2/SDL_opengl.h>, which resolves to GL/gl.h, OpenGL/gl.h or the Windows SDK's
|
|
73
|
+
# per platform. Probing GL/gl.h directly would ask the wrong question off Linux.
|
|
73
74
|
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
# surfaces as a linker error about an undefined `glClear` at the end of a long
|
|
78
|
-
# build; with one, the install stops on a sentence naming the package to
|
|
79
|
-
# install. Same reasoning as the SDL2 abort above.
|
|
80
|
-
#
|
|
81
|
-
# The header probed is the one the sources actually include — SDL's own
|
|
82
|
-
# <SDL2/SDL_opengl.h>, which resolves to GL/gl.h or OpenGL/gl.h per platform, so
|
|
83
|
-
# probing GL/gl.h directly would ask the wrong question off Linux. It needs
|
|
84
|
-
# SDL's cflags, which pkg_config folded into $CFLAGS just above.
|
|
75
|
+
# It needs SDL's cflags, which pkg_config folded into $CFLAGS just above — this
|
|
76
|
+
# is also what makes the probe a real check of the include path rather than of
|
|
77
|
+
# the filesystem.
|
|
85
78
|
abort 'SDL2 OpenGL header not found (SDL2/SDL_opengl.h). Install libsdl2-dev.' unless have_header('SDL2/SDL_opengl.h')
|
|
86
79
|
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
# Three platforms, three different ways to link the same OpenGL — the one place
|
|
81
|
+
# in this file that has to branch, and the mirror of the root Makefile's own
|
|
82
|
+
# branch (see its GL_LIBS). The engine calls only GL 1.1 entry points, which all
|
|
83
|
+
# three expose directly, so no extension loader (GLAD/GLEW) is involved anywhere;
|
|
84
|
+
# see CLAUDE.md's Conventions on why the legacy profile is deliberate.
|
|
85
|
+
#
|
|
86
|
+
# Probed rather than assumed, because this script is what runs on someone else's
|
|
87
|
+
# machine during `gem install` and a missing OpenGL is a routine thing to hit
|
|
88
|
+
# there. Without a probe it surfaces as a linker error about an undefined
|
|
89
|
+
# `glClear` at the end of a long build; with one, the install stops on a
|
|
90
|
+
# sentence naming what to install. Same reasoning as the SDL2 abort above — and
|
|
91
|
+
# the message is per-platform, since "install libgl1-mesa-dev" is useless advice
|
|
92
|
+
# on a Mac.
|
|
93
|
+
#
|
|
94
|
+
# `have_library` appends `-lGL`/`-lopengl32` to $libs on success; `have_framework`
|
|
95
|
+
# appends `-framework OpenGL` to $LDFLAGS. Both end up on the link line.
|
|
96
|
+
case RbConfig::CONFIG['host_os']
|
|
97
|
+
when /darwin/
|
|
98
|
+
# macOS ships OpenGL as a framework rather than a library, so there is no
|
|
99
|
+
# `-lGL` to find. It is part of the OS — a failure here means the toolchain
|
|
100
|
+
# is missing, not the graphics stack.
|
|
101
|
+
unless have_framework('OpenGL')
|
|
102
|
+
abort 'OpenGL framework not found. It ships with macOS, so this usually means ' \
|
|
103
|
+
'the command line tools are missing: xcode-select --install'
|
|
104
|
+
end
|
|
105
|
+
when /mingw|mswin|cygwin/
|
|
106
|
+
# Windows' OpenGL is an OS component: opengl32.dll is in system32 on every
|
|
107
|
+
# installation, and the import library comes with the toolchain. Nothing to
|
|
108
|
+
# install from a graphics vendor.
|
|
109
|
+
unless have_library('opengl32', 'glClear')
|
|
110
|
+
abort 'opengl32 not found. It is part of Windows itself, so this means an ' \
|
|
111
|
+
'incomplete MSYS2 toolchain: pacman -S mingw-w64-ucrt-x86_64-gcc'
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
# Linux and the BSDs.
|
|
115
|
+
unless have_library('GL', 'glClear')
|
|
116
|
+
abort 'OpenGL library not found (-lGL). Install the OpenGL development package: ' \
|
|
117
|
+
'libgl1-mesa-dev on Debian/Ubuntu, mesa-libGL-devel on Fedora.'
|
|
118
|
+
end
|
|
95
119
|
end
|
|
96
120
|
|
|
97
121
|
$libs = append_library($libs, 'm')
|
|
98
122
|
|
|
99
123
|
# miniaudio's threading, and the dlopen it uses to find ALSA or PulseAudio at
|
|
100
|
-
# runtime — the property that makes audio cost no new system dependency.
|
|
101
|
-
#
|
|
102
|
-
#
|
|
124
|
+
# runtime — the property that makes audio cost no new system dependency.
|
|
125
|
+
#
|
|
126
|
+
# Probed rather than branched, because unlike OpenGL above the *answer* differs
|
|
127
|
+
# per platform but the question does not: link them where they exist. On
|
|
128
|
+
# glibc both are system libraries nobody installs; macOS resolves `-lpthread`
|
|
129
|
+
# into libSystem and needs no `-ldl`; MSYS2 supplies winpthreads. Appending
|
|
130
|
+
# either unconditionally would break the link wherever it is absent.
|
|
103
131
|
$libs = append_library($libs, 'pthread') if have_library('pthread')
|
|
104
132
|
$libs = append_library($libs, 'dl') if have_library('dl')
|
|
105
133
|
|
|
@@ -6,15 +6,26 @@
|
|
|
6
6
|
enum {
|
|
7
7
|
RGAME_PUSH_TRANSFORM,
|
|
8
8
|
RGAME_PUSH_CLIP,
|
|
9
|
+
RGAME_PUSH_LAYER,
|
|
9
10
|
/* The underlying stack was full. Nothing to undo, but it still occupies a
|
|
10
11
|
* slot so that the caller's matching pop stays paired with this push. */
|
|
11
12
|
RGAME_PUSH_NOTHING
|
|
12
13
|
};
|
|
13
14
|
|
|
15
|
+
/* The layer stack and the per-band counters, both back to their frame start. */
|
|
16
|
+
static void reset_layers(rgame_canvas *canvas) {
|
|
17
|
+
canvas->layers[0] = 0.0;
|
|
18
|
+
canvas->layer_depth = 0;
|
|
19
|
+
for (int i = 0; i < RGAME_LAYER_BANDS; i++) {
|
|
20
|
+
canvas->slots[i] = 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
void rgame_canvas_init(rgame_canvas *canvas) {
|
|
15
25
|
rgame_draw_queue_init(&canvas->queue);
|
|
16
26
|
rgame_transform_stack_init(&canvas->transforms);
|
|
17
27
|
rgame_clip_stack_init(&canvas->clips, 0, 0);
|
|
28
|
+
reset_layers(canvas);
|
|
18
29
|
canvas->push_depth = 0;
|
|
19
30
|
canvas->unrecorded_pushes = 0;
|
|
20
31
|
canvas->width = 0;
|
|
@@ -29,6 +40,7 @@ void rgame_canvas_begin_frame(rgame_canvas *canvas, int width, int height) {
|
|
|
29
40
|
rgame_draw_queue_reset(&canvas->queue);
|
|
30
41
|
rgame_transform_stack_init(&canvas->transforms);
|
|
31
42
|
rgame_clip_stack_init(&canvas->clips, width, height);
|
|
43
|
+
reset_layers(canvas);
|
|
32
44
|
canvas->push_depth = 0;
|
|
33
45
|
canvas->unrecorded_pushes = 0;
|
|
34
46
|
canvas->width = width;
|
|
@@ -126,6 +138,26 @@ void rgame_canvas_push_clip(rgame_canvas *canvas, rgame_rect rect) {
|
|
|
126
138
|
account_push(canvas, ok, RGAME_PUSH_CLIP);
|
|
127
139
|
}
|
|
128
140
|
|
|
141
|
+
void rgame_canvas_push_layer(rgame_canvas *canvas, double base) {
|
|
142
|
+
int ok = canvas->layer_depth + 1 < RGAME_LAYER_STACK_DEPTH;
|
|
143
|
+
if (ok) {
|
|
144
|
+
/* Replaces rather than accumulates: see canvas.h. */
|
|
145
|
+
canvas->layers[++canvas->layer_depth] = base;
|
|
146
|
+
}
|
|
147
|
+
account_push(canvas, ok, RGAME_PUSH_LAYER);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
double rgame_canvas_layer(const rgame_canvas *canvas) {
|
|
151
|
+
return canvas->layers[canvas->layer_depth];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
unsigned int rgame_canvas_next_slot(rgame_canvas *canvas, int band) {
|
|
155
|
+
if (band < 0 || band >= RGAME_LAYER_BANDS) {
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
return canvas->slots[band]++;
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
void rgame_canvas_pop(rgame_canvas *canvas) {
|
|
130
162
|
/* Unrecorded pushes unwind first: they are the most recent ones, since a
|
|
131
163
|
* push is only left unrecorded once the record stack is already full. */
|
|
@@ -144,11 +176,20 @@ void rgame_canvas_pop(rgame_canvas *canvas) {
|
|
|
144
176
|
case RGAME_PUSH_CLIP:
|
|
145
177
|
rgame_clip_pop(&canvas->clips);
|
|
146
178
|
break;
|
|
179
|
+
case RGAME_PUSH_LAYER:
|
|
180
|
+
canvas->layer_depth--;
|
|
181
|
+
break;
|
|
147
182
|
default:
|
|
148
183
|
break; /* RGAME_PUSH_NOTHING: the push never took effect */
|
|
149
184
|
}
|
|
150
185
|
}
|
|
151
186
|
|
|
187
|
+
/* Every z that reaches the queue is an offset from the current layer base. One
|
|
188
|
+
* addition, in the one place all four queueing paths pass through. */
|
|
189
|
+
static double layer_z(const rgame_canvas *canvas, double z) {
|
|
190
|
+
return canvas->layers[canvas->layer_depth] + z;
|
|
191
|
+
}
|
|
192
|
+
|
|
152
193
|
/* Fills one vertex: map the point into screen space, copy through the texture
|
|
153
194
|
* coordinate, and write the colour as the four bytes GL reads. */
|
|
154
195
|
static void write_vertex(const rgame_canvas *canvas, rgame_vertex *vertex, float x, float y,
|
|
@@ -161,7 +202,7 @@ static void write_vertex(const rgame_canvas *canvas, rgame_vertex *vertex, float
|
|
|
161
202
|
|
|
162
203
|
void rgame_canvas_triangle(rgame_canvas *canvas, const float *xy6, rgame_color color,
|
|
163
204
|
double z) {
|
|
164
|
-
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, 3, z, 0,
|
|
205
|
+
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, 3, layer_z(canvas, z), 0,
|
|
165
206
|
rgame_clip_current(&canvas->clips));
|
|
166
207
|
for (int i = 0; i < 3; i++) {
|
|
167
208
|
write_vertex(canvas, &out[i], xy6[i * 2], xy6[(i * 2) + 1], 0.0f, 0.0f, color);
|
|
@@ -172,7 +213,7 @@ void rgame_canvas_triangle(rgame_canvas *canvas, const float *xy6, rgame_color c
|
|
|
172
213
|
static const int RGAME_QUAD_TRIANGLES[6] = { 0, 1, 2, 0, 2, 3 };
|
|
173
214
|
|
|
174
215
|
void rgame_canvas_quad(rgame_canvas *canvas, const float *xy8, rgame_color color, double z) {
|
|
175
|
-
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, 6, z, 0,
|
|
216
|
+
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, 6, layer_z(canvas, z), 0,
|
|
176
217
|
rgame_clip_current(&canvas->clips));
|
|
177
218
|
for (int i = 0; i < 6; i++) {
|
|
178
219
|
int corner = RGAME_QUAD_TRIANGLES[i];
|
|
@@ -183,7 +224,7 @@ void rgame_canvas_quad(rgame_canvas *canvas, const float *xy8, rgame_color color
|
|
|
183
224
|
|
|
184
225
|
void rgame_canvas_textured_quad(rgame_canvas *canvas, unsigned int texture, const float *xy8,
|
|
185
226
|
const float *uv8, rgame_color color, double z) {
|
|
186
|
-
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, 6, z, texture,
|
|
227
|
+
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, 6, layer_z(canvas, z), texture,
|
|
187
228
|
rgame_clip_current(&canvas->clips));
|
|
188
229
|
for (int i = 0; i < 6; i++) {
|
|
189
230
|
int corner = RGAME_QUAD_TRIANGLES[i];
|
|
@@ -213,7 +254,7 @@ void rgame_canvas_replay(rgame_canvas *canvas, const rgame_recording *recording,
|
|
|
213
254
|
/* One command per baked batch, rather than one per original draw call:
|
|
214
255
|
* the whole point of a recording is that the per-tile work happened
|
|
215
256
|
* once, at bake time. */
|
|
216
|
-
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, batch->vertex_count, z,
|
|
257
|
+
rgame_vertex *out = rgame_draw_queue_alloc(&canvas->queue, batch->vertex_count, layer_z(canvas, z),
|
|
217
258
|
batch->texture,
|
|
218
259
|
rgame_clip_current(&canvas->clips));
|
|
219
260
|
|
|
@@ -46,25 +46,67 @@
|
|
|
46
46
|
* rotates a clip today.
|
|
47
47
|
*
|
|
48
48
|
* ---------------------------------------------------------------------------
|
|
49
|
+
* The layer stack: where a caller's z is measured from
|
|
50
|
+
* ---------------------------------------------------------------------------
|
|
51
|
+
*
|
|
52
|
+
* A `z` handed to a primitive is an *offset*, added to whatever base is on top
|
|
53
|
+
* of the layer stack. The stack starts at 0, so a caller that never pushes one
|
|
54
|
+
* gets exactly the z it passed.
|
|
55
|
+
*
|
|
56
|
+
* What it is for: the scene graph gives every node its own narrow window of z
|
|
57
|
+
* values, so a node's drawing can only ever be ordered against its own other
|
|
58
|
+
* drawing — never against another node's, and never out of the band it is in.
|
|
59
|
+
* Composing that is one addition here, and the alternative is every caller
|
|
60
|
+
* remembering to add the same number to every z it passes.
|
|
61
|
+
*
|
|
62
|
+
* `rgame_canvas_next_slot` is the other half: a small set of counters, one per
|
|
63
|
+
* band, reset at the start of each frame and handing out increasing indices. It
|
|
64
|
+
* is deliberately ignorant of what a band *is* — that vocabulary lives in
|
|
65
|
+
* RGame::Util::Z, a layer up — and does nothing but count, because "which node
|
|
66
|
+
* comes next" is already the order the draw traversal visits nodes in.
|
|
67
|
+
*
|
|
68
|
+
* A push *replaces* rather than adds. Nesting bases would re-introduce the
|
|
69
|
+
* additive relative z this design exists to remove: the point is that a node's
|
|
70
|
+
* window is decided by where the traversal reached it, not by summing what its
|
|
71
|
+
* ancestors happened to pick.
|
|
72
|
+
*
|
|
73
|
+
* ---------------------------------------------------------------------------
|
|
49
74
|
* One pop for any push
|
|
50
75
|
* ---------------------------------------------------------------------------
|
|
51
76
|
*
|
|
52
|
-
* `push_translate`, `push_rotate`, `push_scale` and `
|
|
53
|
-
* by the same `pop`. The canvas remembers which stack each push
|
|
54
|
-
* caller never has to — and cannot pop the wrong one. Pushes that
|
|
55
|
-
* honoured (a full stack) are still counted, so pop stays balanced
|
|
56
|
-
* drawing comes out untransformed rather than desynchronised.
|
|
77
|
+
* `push_translate`, `push_rotate`, `push_scale`, `push_clip` and `push_layer`
|
|
78
|
+
* are all undone by the same `pop`. The canvas remembers which stack each push
|
|
79
|
+
* went to, so a caller never has to — and cannot pop the wrong one. Pushes that
|
|
80
|
+
* could not be honoured (a full stack) are still counted, so pop stays balanced
|
|
81
|
+
* and the drawing comes out untransformed rather than desynchronised.
|
|
57
82
|
*/
|
|
58
83
|
|
|
59
|
-
/* Deep enough
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
/* Deep enough for any sane scene graph, like the two stacks it sits beside. */
|
|
85
|
+
#define RGAME_LAYER_STACK_DEPTH 32
|
|
86
|
+
|
|
87
|
+
/* How many independent slot counters a frame has. RGame::Util::Z uses four;
|
|
88
|
+
* the spare room costs four unsigned ints. */
|
|
89
|
+
#define RGAME_LAYER_BANDS 8
|
|
90
|
+
|
|
91
|
+
/* Deep enough that it cannot fill before all three underlying stacks have;
|
|
92
|
+
* pushes beyond that are counted rather than recorded, so balance always
|
|
93
|
+
* holds. */
|
|
94
|
+
#define RGAME_CANVAS_STACK_DEPTH \
|
|
95
|
+
(RGAME_TRANSFORM_STACK_DEPTH + RGAME_CLIP_STACK_DEPTH + RGAME_LAYER_STACK_DEPTH)
|
|
62
96
|
|
|
63
97
|
typedef struct {
|
|
64
98
|
rgame_transform_stack transforms;
|
|
65
99
|
rgame_clip_stack clips;
|
|
66
100
|
rgame_draw_queue queue;
|
|
67
101
|
|
|
102
|
+
/* entries[0] is 0 — the base an un-layered draw sits on — so `layer_depth`
|
|
103
|
+
* is the number of pushes outstanding and entries[layer_depth] is current. */
|
|
104
|
+
double layers[RGAME_LAYER_STACK_DEPTH];
|
|
105
|
+
int layer_depth;
|
|
106
|
+
|
|
107
|
+
/* Slots handed out per band this frame. Reset by begin_frame. */
|
|
108
|
+
unsigned int slots[RGAME_LAYER_BANDS];
|
|
109
|
+
|
|
68
110
|
/* Which stack each outstanding push went to, so pop can undo the right one. */
|
|
69
111
|
unsigned char pushes[RGAME_CANVAS_STACK_DEPTH];
|
|
70
112
|
int push_depth;
|
|
@@ -79,8 +121,8 @@ void rgame_canvas_init(rgame_canvas *canvas);
|
|
|
79
121
|
void rgame_canvas_destroy(rgame_canvas *canvas);
|
|
80
122
|
|
|
81
123
|
/*
|
|
82
|
-
* Starts a frame: empties the queue (keeping its buffers), resets
|
|
83
|
-
* and sets the clip base to the window. Re-stating the size every frame is also
|
|
124
|
+
* Starts a frame: empties the queue (keeping its buffers), resets all three
|
|
125
|
+
* stacks and the slot counters, and sets the clip base to the window. Re-stating the size every frame is also
|
|
84
126
|
* how a resize takes effect, so there is no separate path to forget.
|
|
85
127
|
*/
|
|
86
128
|
void rgame_canvas_begin_frame(rgame_canvas *canvas, int width, int height);
|
|
@@ -97,6 +139,19 @@ void rgame_canvas_push_scale(rgame_canvas *canvas, float sx, float sy);
|
|
|
97
139
|
* transform before narrowing the clip. */
|
|
98
140
|
void rgame_canvas_push_clip(rgame_canvas *canvas, rgame_rect rect);
|
|
99
141
|
|
|
142
|
+
/* Sets the base every subsequent z is measured from, until the matching pop. */
|
|
143
|
+
void rgame_canvas_push_layer(rgame_canvas *canvas, double base);
|
|
144
|
+
|
|
145
|
+
/* The base currently in effect. */
|
|
146
|
+
double rgame_canvas_layer(const rgame_canvas *canvas);
|
|
147
|
+
|
|
148
|
+
/*
|
|
149
|
+
* The next slot index in `band`, counting from 0 each frame. Out-of-range bands
|
|
150
|
+
* answer 0 rather than reading past the array — a caller with a bad band gets
|
|
151
|
+
* everything piled in one slot, which is wrong but bounded.
|
|
152
|
+
*/
|
|
153
|
+
unsigned int rgame_canvas_next_slot(rgame_canvas *canvas, int band);
|
|
154
|
+
|
|
100
155
|
void rgame_canvas_pop(rgame_canvas *canvas);
|
|
101
156
|
|
|
102
157
|
/*
|