Merge branch 'dev-loop' into worktree-agent-ab63ab2e0656f837e
This commit is contained in:
commit
eebd5d6d2c
79
NEXT.md
79
NEXT.md
@ -1,3 +1,82 @@
|
||||
## Queued: a pointer from C needs a length before it can be indexed
|
||||
|
||||
**Sequenced after the generics lane**, which holds `lib/check.ml`.
|
||||
|
||||
`indexed` in `lib/check.ml` accepts `Array` and `Slice` only, so a `(Ptr T)` that came back from C is readable at
|
||||
element 0 through `deref` and nowhere else. Confirmed empirically rather than inferred:
|
||||
`(at (.recs (rl/get-font-default)) 0)` answers `(Ptr rl/Rectangle) cannot be indexed`.
|
||||
|
||||
**It blocks real work now**, which is why it moved from a note to a queue entry. Three upstream examples are
|
||||
unportable — `text_rectangle_bounds` (walks `font.recs[i]` and `font.glyphs[i]`), `text_codepoints_loading`
|
||||
(`LoadCodepoints` returns `int*`) and `textures_image_processing` (`LoadImageColors` returns `Color*`) — and the
|
||||
first of those is the only *procedural* font example, so the whole hand-written `Font` surface has no example
|
||||
caller. Every other text example needs a TTF on disk.
|
||||
|
||||
The problem is that C hands back a pointer and no length. Two shapes:
|
||||
|
||||
1. **`(slice-from-ptr p n)`** — the caller states the length and owns being right about it. Honest, one form, and
|
||||
the unsafety is written at the site where somebody had to know the answer anyway. It is the same trust
|
||||
`declare-c` already extends.
|
||||
2. **A per-binding declaration naming which argument carries the count.** raylib's convention is an out-parameter
|
||||
(`LoadCodepoints(text, &count)`), so the information usually *is* there. This makes the binding carry it once
|
||||
instead of every call site repeating it, and it fits the existing `bindings` file. It does not cover
|
||||
`font.recs`, where the count is a sibling *field* (`font.glyphCount`) rather than an argument.
|
||||
|
||||
They are not exclusive, and 2 does not cover every case, so 1 is probably the floor whatever else is built.
|
||||
|
||||
## Queued, decided by the author: generic structs and generic array lengths
|
||||
|
||||
**Sequenced after the core generics lane, not beside it.** Both change `Types.t`, which that lane is inside.
|
||||
|
||||
The author's question settled it: Odin has both, and the normal way to write them is together.
|
||||
`core/math/linalg/extended.odin:488` binds three things in one pattern —
|
||||
|
||||
```odin
|
||||
is_nan_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T)
|
||||
```
|
||||
|
||||
`A` is the array type, `N` its length, `T` its element, and `[N]bool` in the *return* type uses `N`, so the result
|
||||
is an array of the same length as the argument. And `core/container/small_array/small_array.odin:23` is the generic
|
||||
struct over both, with a predicate over a **value** rather than a type:
|
||||
|
||||
```odin
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 { ... }
|
||||
```
|
||||
|
||||
### Why they are one project
|
||||
|
||||
`Types.Named` is a bare string with no parameters and `Types.Array` is `int64 * t`. Either feature means `Types.t`
|
||||
gains a parameterised case, and `Types.t` is consumed by `emit.ml`'s layout calculator, `x86.ml`, `render.ml`, the
|
||||
DWARF path and the map key-pair emitter. **Same price for one as for both**, which is the whole reason to do them
|
||||
together.
|
||||
|
||||
What the spike measured for the length half specifically: `Ast.len` is `Lint | Lname`, so `$n` parses today with no
|
||||
reader or parser change — the same free ride the type sigil got. Everything after that is the expensive part.
|
||||
`bind_ty` gains a length-binding case (`Array (n, p)` against `Array (m, a)` binds `n := m`), `mangle_ty` gains a
|
||||
number, and `array_len` has to answer "a variable" rather than failing — which means the checker's compile-time
|
||||
constant folding has to accept that a length can stay symbolic until instantiation.
|
||||
|
||||
### The motivating case, and it is not `$n` on its own
|
||||
|
||||
`$n` alone buys little. `swap!` does not need it — it takes a slice and two indices, and the length is a runtime
|
||||
field. Nor does a `pop` from a `Vec`. The case that wants both is **a fixed-capacity array with a count and no
|
||||
allocation**, which is Odin's `Small_Array` and a good fit for a game that refuses to allocate in a frame:
|
||||
|
||||
```lisp
|
||||
(defvar enemies (Small-Array 64 Enemy))
|
||||
```
|
||||
|
||||
`PORTING.md`'s recommendation for the game's state was fixed arrays with counts held in `defvar` globals, kept
|
||||
deliberately away from `Vec` so nothing is move-only. `(Small-Array $n $t)` is that pattern with a type behind it
|
||||
instead of two variables kept in step by hand. That is the argument for building this, and it is stronger than
|
||||
"Odin has it".
|
||||
|
||||
### Also worth deciding when it is built
|
||||
|
||||
Odin's `where N >= 0` is a predicate over a **value**, not a type. The `where` mechanism just decided here takes
|
||||
type predicates (`ordered?`, `copyable?`). Whether it also takes value predicates over a length parameter is a
|
||||
separate question and should be answered deliberately rather than falling out of the implementation.
|
||||
|
||||
## To discuss: two decisions parked during the ownership discussion
|
||||
|
||||
Both came out of the lane that closed the enum-layout and constant-checking gaps. Its work is sound and committed;
|
||||
|
||||
@ -267,6 +267,15 @@ the state type. `sand.flan` is already written that way. The frame rollback is t
|
||||
piece that needs to touch the state, and the game can supply `snapshot`/`restore` as two
|
||||
more callbacks.
|
||||
|
||||
> **Settled by the author, 2026-09-13: fixed arrays with counts.** The first branch below. So the
|
||||
> conditional verdict in this section is now unconditional: the state fits in `defvar` globals, the
|
||||
> engine takes only `(Fn [] ())` callbacks and never names a state type, generics stays off this
|
||||
> game's critical path, and `drop` is not needed for this version. Nothing in the language had to be
|
||||
> built for it — fixed arrays, counts and slices all already work. What is left is writing the game.
|
||||
>
|
||||
> Carry forward the one flag §3 raises: the next version, with several tilesets and several atlases,
|
||||
> grows straight into `drop` and into `(Vec T)` where `T` owns a `Vec`.
|
||||
|
||||
**This verdict is conditional, and the condition is one decision.** Both it and the
|
||||
globals workaround above hang on the same pivot, so it is worth stating once, flatly:
|
||||
*nothing in this game's state needs to be move-only.* Make the inner collections fixed
|
||||
|
||||
157
examples/core-3d-picking.flan
Normal file
157
examples/core-3d-picking.flan
Normal file
@ -0,0 +1,157 @@
|
||||
;;;; raylib [core] example - 3d picking
|
||||
;;;;
|
||||
;;;; examples/core/core_3d_picking.c. The one `core` example in this batch,
|
||||
;;;; and it is here for surface and not for category: it is the only example
|
||||
;;;; anywhere upstream that uses Ray and RayCollision without also loading a
|
||||
;;;; Model, so it is the only way those two structs get a caller at all.
|
||||
;;;;
|
||||
;;;; Picking is the round trip the corpus was missing. core-world-screen.flan
|
||||
;;;; already runs get-world-to-screen — a point in the scene to a pixel. This
|
||||
;;;; is the inverse and it is a different shape: a pixel does not name a
|
||||
;;;; point, it names a *line* through the scene, which is what a Ray is, and
|
||||
;;;; the answer to "what did I click on" is where that line first meets
|
||||
;;;; something, which is what a RayCollision is. Two structs the package could
|
||||
;;;; not describe, for one operation that cannot be written without them.
|
||||
;;;;
|
||||
;;;; What was added, all in vendor/raylib/raylib.flan:
|
||||
;;;;
|
||||
;;;; - `Ray` — two Vector3s, origin and direction.
|
||||
;;;; - `RayCollision` — a C `bool`, a float and two Vector3s. This is the
|
||||
;;;; one of the three new structs a layout check can actually fail:
|
||||
;;;; `hit` is one byte and `distance` is four, so there are three padding
|
||||
;;;; bytes between them that a permutation destroys. BoundingBox's two
|
||||
;;;; Vector3s are interchangeable and nothing would catch swapping them.
|
||||
;;;; - `BoundingBox` — shared with examples/models-box-collisions.flan.
|
||||
;;;; - get-screen-to-world-ray and draw-ray, hand-written beside
|
||||
;;;; get-world-to-screen and the cube draws for the reasons `bindings`
|
||||
;;;; gives: the first reads the same camera as its inverse, and the second
|
||||
;;;; is inside a frame.
|
||||
;;;;
|
||||
;;;; get-ray-collision-box came free out of the generated half the moment Ray
|
||||
;;;; and BoundingBox existed, along with the sphere, triangle and quad forms.
|
||||
;;;; get-ray-collision-mesh is still refused and will stay refused: Mesh owns
|
||||
;;;; seven arrays and a GPU handle, and describing it is a different job.
|
||||
;;;;
|
||||
;;;; raylib 5.5 renamed GetMouseRay to GetScreenToWorldRay and left the old
|
||||
;;;; name as a #define. A #define is not a symbol, so there is nothing to bind
|
||||
;;;; it to and nothing lost by not trying.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defvar camera rl/Camera3D)
|
||||
|
||||
(defvar cube-position rl/Vector3)
|
||||
(defvar cube-size rl/Vector3)
|
||||
|
||||
;; The picking ray, kept between frames because draw-ray draws it every frame
|
||||
;; whether or not it hit anything — that is how the example shows where the
|
||||
;; click went.
|
||||
(defvar ray rl/Ray)
|
||||
(defvar collision rl/RayCollision)
|
||||
|
||||
;; The same centre/size to min/max conversion as in
|
||||
;; examples/models-box-collisions.flan. Written out here rather than shared
|
||||
;; because an example is a single file the reader can follow end to end, and
|
||||
;; a two-file port for eight expressions would cost more than it saves.
|
||||
(defn box-around [centre rl/Vector3 size rl/Vector3] rl/BoundingBox
|
||||
(rl/BoundingBox
|
||||
{.min (rl/Vector3 {.x (- (.x centre) (/ (.x size) 2.0))
|
||||
.y (- (.y centre) (/ (.y size) 2.0))
|
||||
.z (- (.z centre) (/ (.z size) 2.0))})
|
||||
.max (rl/Vector3 {.x (+ (.x centre) (/ (.x size) 2.0))
|
||||
.y (+ (.y centre) (/ (.y size) 2.0))
|
||||
.z (+ (.z centre) (/ (.z size) 2.0))})}))
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [core] example - 3d picking")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set camera (rl/Camera3D {.position (rl/Vector3 {.x 10.0 .y 10.0 .z 10.0})
|
||||
.target (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0})
|
||||
.up (rl/Vector3 {.x 0.0 .y 1.0 .z 0.0})
|
||||
.fovy 45.0
|
||||
.projection :perspective}))
|
||||
|
||||
(set cube-position (rl/Vector3 {.x 0.0 .y 1.0 .z 0.0}))
|
||||
(set cube-size (rl/Vector3 {.x 2.0 .y 2.0 .z 2.0}))
|
||||
|
||||
;; Both start zeroed, which is the C's `= { 0 }`. A zero Ray draws as a
|
||||
;; degenerate line at the origin and a zero RayCollision has hit false, so
|
||||
;; neither needs a "not yet" flag beside it.
|
||||
(set ray (rl/Ray {.position (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0})
|
||||
.direction (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0})}))
|
||||
(set collision (rl/RayCollision {.hit false .distance 0.0
|
||||
.point (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0})
|
||||
.normal (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0})}))
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update. The first-person controls only run while the cursor is
|
||||
;; captured, which is what the right button toggles — otherwise the mouse
|
||||
;; could not be used to aim at anything.
|
||||
(when (rl/cursor-hidden?) (rl/update-camera (addr camera) :first-person))
|
||||
|
||||
(when (rl/mouse-button-pressed? :right)
|
||||
(if (rl/cursor-hidden?) (rl/enable-cursor) (rl/disable-cursor)))
|
||||
|
||||
(when (rl/mouse-button-pressed? :left)
|
||||
(if (not (.hit collision))
|
||||
;; The pixel under the pointer, as a line through the scene, tested
|
||||
;; against the cube's bounds.
|
||||
(do
|
||||
(set ray (rl/get-screen-to-world-ray (rl/get-mouse-position) camera))
|
||||
(set collision
|
||||
(rl/get-ray-collision-box ray (box-around cube-position cube-size))))
|
||||
;; A second click deselects. Only `hit` is cleared; the ray stays put
|
||||
;; and keeps being drawn, as in the C.
|
||||
(set (.hit collision) false)))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
(rl/begin-mode-3d camera)
|
||||
|
||||
(if (.hit collision)
|
||||
(do
|
||||
(rl/draw-cube cube-position (.x cube-size) (.y cube-size) (.z cube-size)
|
||||
rl/red)
|
||||
(rl/draw-cube-wires cube-position (.x cube-size) (.y cube-size)
|
||||
(.z cube-size) rl/maroon)
|
||||
;; A slightly larger wireframe around the selection, which is the
|
||||
;; whole visual feedback of the example.
|
||||
(rl/draw-cube-wires cube-position (+ (.x cube-size) 0.2)
|
||||
(+ (.y cube-size) 0.2) (+ (.z cube-size) 0.2)
|
||||
rl/green))
|
||||
(do
|
||||
(rl/draw-cube cube-position (.x cube-size) (.y cube-size) (.z cube-size)
|
||||
rl/gray)
|
||||
(rl/draw-cube-wires cube-position (.x cube-size) (.y cube-size)
|
||||
(.z cube-size) rl/darkgray)))
|
||||
|
||||
;; raylib draws the ray a thousand units long, so it reads as a line
|
||||
;; crossing the scene rather than as a segment with an end.
|
||||
(rl/draw-ray ray rl/maroon)
|
||||
(rl/draw-grid 10 1.0)
|
||||
|
||||
(rl/end-mode-3d)
|
||||
|
||||
(rl/draw-text "Try clicking on the box with your mouse!" 240 10 20
|
||||
rl/darkgray)
|
||||
|
||||
(when (.hit collision)
|
||||
(rl/draw-text "BOX SELECTED"
|
||||
(/ (- screen-width (rl/measure-text "BOX SELECTED" 30)) 2)
|
||||
(i32 (* (f32 screen-height) 0.1)) 30 rl/green))
|
||||
|
||||
(rl/draw-text "Right click mouse to toggle camera controls" 10 430 10
|
||||
rl/gray)
|
||||
|
||||
(rl/draw-fps 10 10)
|
||||
|
||||
(rl/end-drawing)))
|
||||
133
examples/models-box-collisions.flan
Normal file
133
examples/models-box-collisions.flan
Normal file
@ -0,0 +1,133 @@
|
||||
;;;; raylib [models] example - box collisions
|
||||
;;;;
|
||||
;;;; examples/models/models_box_collisions.c. The previous lane's finding was
|
||||
;;;; that the whole models category is refused by the importer for want of a
|
||||
;;;; defstruct, and that a models example is therefore mostly a binding
|
||||
;;;; exercise. That is true of the twenty that load a Model — but not of this
|
||||
;;;; one, which is why it is here: nothing in it is a Model, a Mesh or a
|
||||
;;;; Material. It is two cubes, a sphere and one aggregate the package did not
|
||||
;;;; describe.
|
||||
;;;;
|
||||
;;;; That aggregate is BoundingBox, and it is two Vector3s and nothing else —
|
||||
;;;; no owned pointer, no array, no lifetime. One `defstruct` in
|
||||
;;;; vendor/raylib/raylib.flan un-refuses four raylib functions at once
|
||||
;;;; (CheckCollisionBoxes, CheckCollisionBoxSphere, DrawBoundingBox and
|
||||
;;;; GetRayCollisionBox), and that is the cheapest widening of the 3D surface
|
||||
;;;; available anywhere in the tree. Ray and RayCollision went in beside it
|
||||
;;;; for examples/core-3d-picking.flan and for the same reason.
|
||||
;;;;
|
||||
;;;; So the answer to "is a models example just a binding exercise" is: the
|
||||
;;;; ones that need Model are, and this one is the counterexample. What
|
||||
;;;; separates them is not the category, it is whether the struct owns memory.
|
||||
;;;;
|
||||
;;;; What it also exercises, and what needed hand-writing rather than
|
||||
;;;; generating: draw-cube-v, draw-sphere and draw-sphere-wires. All three
|
||||
;;;; were in the generated half already, and all three moved to the
|
||||
;;;; hand-written one here on `bindings`'s own rule — they are inside a frame,
|
||||
;;;; in an example that ships in examples/. draw-cube-v in particular was
|
||||
;;;; named there as generated *because nothing called it*; something calls it
|
||||
;;;; now.
|
||||
;;;;
|
||||
;;;; The collision maths is written twice in the C, once per test, building
|
||||
;;;; the player's box inline both times. It is a function here for the reason
|
||||
;;;; the eyes example gives: two copies of the same eight expressions cannot
|
||||
;;;; be checked against each other by reading them.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defvar camera rl/Camera3D)
|
||||
|
||||
(defvar player-position rl/Vector3)
|
||||
(defvar player-size rl/Vector3)
|
||||
(defvar player-color rl/Color)
|
||||
|
||||
(defvar enemy-box-pos rl/Vector3)
|
||||
(defvar enemy-box-size rl/Vector3)
|
||||
(defvar enemy-sphere-pos rl/Vector3)
|
||||
|
||||
(defconst enemy-sphere-size f32 1.5)
|
||||
|
||||
;; A box centred on `centre` with the given extents. raylib's BoundingBox is
|
||||
;; min-corner/max-corner, not centre/size, and every collision call in the
|
||||
;; family takes the corner form — so this is the conversion the C writes out
|
||||
;; longhand at each of its two call sites.
|
||||
(defn box-around [centre rl/Vector3 size rl/Vector3] rl/BoundingBox
|
||||
(rl/BoundingBox
|
||||
{.min (rl/Vector3 {.x (- (.x centre) (/ (.x size) 2.0))
|
||||
.y (- (.y centre) (/ (.y size) 2.0))
|
||||
.z (- (.z centre) (/ (.z size) 2.0))})
|
||||
.max (rl/Vector3 {.x (+ (.x centre) (/ (.x size) 2.0))
|
||||
.y (+ (.y centre) (/ (.y size) 2.0))
|
||||
.z (+ (.z centre) (/ (.z size) 2.0))})}))
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [models] example - box collisions")
|
||||
(defer (rl/close-window))
|
||||
|
||||
;; The C writes this camera as a brace initialiser with a trailing 0 for the
|
||||
;; projection, which is CAMERA_PERSPECTIVE. Named here, because the defenum
|
||||
;; exists precisely so that the 0 does not have to be remembered.
|
||||
(set camera (rl/Camera3D {.position (rl/Vector3 {.x 0.0 .y 10.0 .z 10.0})
|
||||
.target (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0})
|
||||
.up (rl/Vector3 {.x 0.0 .y 1.0 .z 0.0})
|
||||
.fovy 45.0
|
||||
.projection :perspective}))
|
||||
|
||||
(set player-position (rl/Vector3 {.x 0.0 .y 1.0 .z 2.0}))
|
||||
(set player-size (rl/Vector3 {.x 1.0 .y 2.0 .z 1.0}))
|
||||
(set player-color rl/green)
|
||||
|
||||
(set enemy-box-pos (rl/Vector3 {.x -4.0 .y 1.0 .z 0.0}))
|
||||
(set enemy-box-size (rl/Vector3 {.x 2.0 .y 2.0 .z 2.0}))
|
||||
(set enemy-sphere-pos (rl/Vector3 {.x 4.0 .y 0.0 .z 0.0}))
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update. The arrow keys move on the XZ plane, and the C's else-if chain
|
||||
;; means only one direction applies per frame — diagonal movement is not
|
||||
;; possible, deliberately.
|
||||
(cond
|
||||
(rl/key-down? :right) (set (.x player-position) (+ (.x player-position) 0.2))
|
||||
(rl/key-down? :left) (set (.x player-position) (- (.x player-position) 0.2))
|
||||
(rl/key-down? :down) (set (.z player-position) (+ (.z player-position) 0.2))
|
||||
(rl/key-down? :up) (set (.z player-position) (- (.z player-position) 0.2)))
|
||||
|
||||
(let [player-box (box-around player-position player-size)
|
||||
hit (or (rl/check-collision-boxes player-box
|
||||
(box-around enemy-box-pos enemy-box-size))
|
||||
(rl/check-collision-box-sphere player-box enemy-sphere-pos
|
||||
enemy-sphere-size))]
|
||||
(set player-color (if hit rl/red rl/green)))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
(rl/begin-mode-3d camera)
|
||||
|
||||
(rl/draw-cube enemy-box-pos (.x enemy-box-size) (.y enemy-box-size)
|
||||
(.z enemy-box-size) rl/gray)
|
||||
(rl/draw-cube-wires enemy-box-pos (.x enemy-box-size) (.y enemy-box-size)
|
||||
(.z enemy-box-size) rl/darkgray)
|
||||
|
||||
;; draw-sphere is draw-sphere-ex at 16 rings and 16 slices; the wires form
|
||||
;; takes them because the tessellation is only visible in the wireframe.
|
||||
(rl/draw-sphere enemy-sphere-pos enemy-sphere-size rl/gray)
|
||||
(rl/draw-sphere-wires enemy-sphere-pos enemy-sphere-size 16 16 rl/darkgray)
|
||||
|
||||
(rl/draw-cube-v player-position player-size player-color)
|
||||
|
||||
(rl/draw-grid 10 1.0)
|
||||
|
||||
(rl/end-mode-3d)
|
||||
|
||||
(rl/draw-text "Move player with arrow keys to collide" 220 40 20 rl/gray)
|
||||
|
||||
(rl/draw-fps 10 10)
|
||||
|
||||
(rl/end-drawing)))
|
||||
102
examples/shapes-basic-shapes.flan
Normal file
102
examples/shapes-basic-shapes.flan
Normal file
@ -0,0 +1,102 @@
|
||||
;;;; raylib [shapes] example - basic shapes drawing
|
||||
;;;;
|
||||
;;;; examples/shapes/shapes_basic_shapes.c. Ported first of the shapes batch
|
||||
;;;; because it is the widest single file in the category: six draw families
|
||||
;;;; the corpus had never called — draw-circle-gradient, the two rectangle
|
||||
;;;; gradients, draw-triangle and draw-triangle-lines, and all three of the
|
||||
;;;; poly draws — in one frame, beside the four the core examples already
|
||||
;;;; exercise. Every one of them came out of the generated half of the
|
||||
;;;; bindings and none needed a new line anywhere; that is the result worth
|
||||
;;;; recording, because the whole point of a committed generated.flan is that
|
||||
;;;; a build with no FLAN_RAYLIB_H set can draw with all of it.
|
||||
;;;;
|
||||
;;;; What it is actually a test of. The gradient calls are the first thing in
|
||||
;;;; the corpus to pass *two* Colors in one call, and draw-triangle is the
|
||||
;;;; first to pass three Vector2s. A struct argument crosses the FFI by
|
||||
;;;; pointer into a generated shim (vendor/raylib/raylib.flan's header note),
|
||||
;;;; so the arity of that copying is what a call with several small structs in
|
||||
;;;; a row puts under load. Nothing here can be asserted headless — every line
|
||||
;;;; needs a GL context — so the check is the link and the screen, which is
|
||||
;;;; what the Shapes comment in raylib.flan already says about this family.
|
||||
;;;;
|
||||
;;;; The one deliberate difference from the C: the C writes `screenWidth/4*2`
|
||||
;;;; and `screenWidth/4.0f*3.0f` for the same column and gets 400 and 600 out
|
||||
;;;; of integer and float division respectively. Both are written here as the
|
||||
;;;; arithmetic they are, in the type the call wants, rather than copied with
|
||||
;;;; a cast on the end — `(/ screen-width 5)` is an i32 division for the i32
|
||||
;;;; parameters of draw-circle, and the poly centre is built from f32
|
||||
;;;; literals because a Vector2 holds f32.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
;; The three columns the C lays the shapes out in. The middle one is
|
||||
;; screenWidth/4*2 and the right one screenWidth/4*3, which is 400 and 600.
|
||||
(defconst col-left 160) ; screen-width / 5
|
||||
(defconst col-mid 400)
|
||||
(defconst col-right 600)
|
||||
|
||||
(defvar rotation f32)
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [shapes] example - basic shapes drawing")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set rotation 0.0)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update. The polygons spin; nothing else moves.
|
||||
(set rotation (+ rotation 0.2))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
(rl/draw-text "some basic shapes available on raylib" 20 20 20 rl/darkgray)
|
||||
|
||||
;; Circles. The gradient one takes an inner and an outer colour and is the
|
||||
;; first call in the corpus to pass two Colors at once.
|
||||
(rl/draw-circle col-left 120 35.0 rl/darkblue)
|
||||
(rl/draw-circle-gradient col-left 220 60.0 rl/green rl/skyblue)
|
||||
(rl/draw-circle-lines col-left 340 80.0 rl/darkblue)
|
||||
|
||||
;; Rectangles. draw-rectangle-gradient-h runs the colour left to right;
|
||||
;; the -v form runs it top to bottom and the -ex form takes all four
|
||||
;; corners. The C uses the horizontal one here.
|
||||
(rl/draw-rectangle (- col-mid 60) 100 120 60 rl/red)
|
||||
(rl/draw-rectangle-gradient-h (- col-mid 90) 170 180 130 rl/maroon rl/gold)
|
||||
;; raylib draws this with quads internally rather than with lines, which
|
||||
;; is why its thickness does not follow the line width anywhere.
|
||||
(rl/draw-rectangle-lines (- col-mid 40) 320 80 60 rl/orange)
|
||||
|
||||
;; Triangles. raylib wants the three vertices in counter-clockwise order
|
||||
;; and draws nothing at all for a clockwise one, so the order here is not
|
||||
;; cosmetic.
|
||||
(rl/draw-triangle (rl/Vector2 {.x 600.0 .y 80.0})
|
||||
(rl/Vector2 {.x 540.0 .y 150.0})
|
||||
(rl/Vector2 {.x 660.0 .y 150.0})
|
||||
rl/violet)
|
||||
|
||||
(rl/draw-triangle-lines (rl/Vector2 {.x 600.0 .y 160.0})
|
||||
(rl/Vector2 {.x 580.0 .y 230.0})
|
||||
(rl/Vector2 {.x 620.0 .y 230.0})
|
||||
rl/darkblue)
|
||||
|
||||
;; Polygons, all three at the same centre and the same rotation, at three
|
||||
;; radii, so the filled hexagon sits inside the two outlines.
|
||||
(let [centre (rl/Vector2 {.x (f32 col-right) .y 330.0})]
|
||||
(rl/draw-poly centre 6 80.0 rotation rl/brown)
|
||||
(rl/draw-poly-lines centre 6 90.0 rotation rl/brown)
|
||||
(rl/draw-poly-lines-ex centre 6 85.0 rotation 6.0 rl/beige))
|
||||
|
||||
;; The C draws every LINES-based shape together so raylib can batch them
|
||||
;; into one pass. This last line is part of that comment and not an
|
||||
;; afterthought, so it stays where the C has it.
|
||||
(rl/draw-line 18 42 (- screen-width 18) 42 rl/black)
|
||||
|
||||
(rl/end-drawing)))
|
||||
129
examples/shapes-collision-area.flan
Normal file
129
examples/shapes-collision-area.flan
Normal file
@ -0,0 +1,129 @@
|
||||
;;;; raylib [shapes] example - collision area
|
||||
;;;;
|
||||
;;;; examples/shapes/shapes_collision_area.c. Picked for one call:
|
||||
;;;; get-collision-rec, which is the only binding in the package that takes
|
||||
;;;; two Rectangles and answers a third. Everything else in the corpus that
|
||||
;;;; crosses a struct either passes one in (Camera2D, Color) or gets one back
|
||||
;;;; (get-mouse-position, fade) — this is the first per-frame call doing both
|
||||
;;;; at once, and the returned struct comes back through the shim's `out`
|
||||
;;;; pointer rather than in registers, which is a different lowering again.
|
||||
;;;;
|
||||
;;;; It is also the only headless-adjacent thing in the file: both
|
||||
;;;; collision-recs? and get-collision-rec are pure arithmetic over their
|
||||
;;;; arguments and need no window at all. The acceptance table already asserts
|
||||
;;;; the pair; what this adds is the same two calls on a frame path with one
|
||||
;;;; rectangle driven by the mouse, where a permuted Rectangle would put the
|
||||
;;;; green overlap patch somewhere the two boxes are not.
|
||||
;;;;
|
||||
;;;; Both TextFormats go through examples/digits.flan the way the core ports
|
||||
;;;; do it: draw the literal part, then the number at x plus its width.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
(import d "digits.flan")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
;; The top strip the collision message is drawn into, and the floor box B is
|
||||
;; not allowed above.
|
||||
(defconst screen-upper-limit 40)
|
||||
|
||||
(defvar box-a rl/Rectangle) ; moves by itself, bounces off the sides
|
||||
(defvar box-b rl/Rectangle) ; follows the mouse
|
||||
(defvar box-collision rl/Rectangle) ; their overlap, valid only while touching
|
||||
(defvar box-a-speed-x i32)
|
||||
(defvar paused bool)
|
||||
(defvar collision bool)
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [shapes] example - collision area")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set box-a (rl/Rectangle {.x 10.0
|
||||
.y (- (/ (f32 screen-height) 2.0) 50.0)
|
||||
.width 200.0 .height 100.0}))
|
||||
(set box-a-speed-x 4)
|
||||
|
||||
(set box-b (rl/Rectangle {.x (- (/ (f32 screen-width) 2.0) 30.0)
|
||||
.y (- (/ (f32 screen-height) 2.0) 30.0)
|
||||
.width 60.0 .height 60.0}))
|
||||
|
||||
;; A fresh Rectangle is four zeroes, which is the C's `= { 0 }`. It is never
|
||||
;; drawn before the first collision sets it.
|
||||
(set box-collision (rl/Rectangle {.x 0.0 .y 0.0 .width 0.0 .height 0.0}))
|
||||
|
||||
(set paused false)
|
||||
(set collision false)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update
|
||||
(when (not paused)
|
||||
(set (.x box-a) (+ (.x box-a) (f32 box-a-speed-x))))
|
||||
|
||||
;; Bounce at either edge. The test is on the far edge going right and on
|
||||
;; the near one going left, so the box reverses on whichever it reaches.
|
||||
(when (or (>= (+ (.x box-a) (.width box-a)) (f32 (rl/get-screen-width)))
|
||||
(<= (.x box-a) 0.0))
|
||||
(set box-a-speed-x (* box-a-speed-x -1)))
|
||||
|
||||
;; Box B is centred on the mouse and then clamped back inside the play
|
||||
;; area — which is the window minus the top strip.
|
||||
(set (.x box-b) (- (f32 (rl/get-mouse-x)) (/ (.width box-b) 2.0)))
|
||||
(set (.y box-b) (- (f32 (rl/get-mouse-y)) (/ (.height box-b) 2.0)))
|
||||
|
||||
(if (>= (+ (.x box-b) (.width box-b)) (f32 (rl/get-screen-width)))
|
||||
(set (.x box-b) (- (f32 (rl/get-screen-width)) (.width box-b)))
|
||||
(when (<= (.x box-b) 0.0) (set (.x box-b) 0.0)))
|
||||
|
||||
(if (>= (+ (.y box-b) (.height box-b)) (f32 (rl/get-screen-height)))
|
||||
(set (.y box-b) (- (f32 (rl/get-screen-height)) (.height box-b)))
|
||||
(when (<= (.y box-b) (f32 screen-upper-limit))
|
||||
(set (.y box-b) (f32 screen-upper-limit))))
|
||||
|
||||
(set collision (rl/collision-recs? box-a box-b))
|
||||
|
||||
;; Only meaningful while they touch: raylib answers a zero rectangle for
|
||||
;; two boxes that do not, and the C leaves the previous one in place
|
||||
;; rather than reading that back. This follows it.
|
||||
(when collision (set box-collision (rl/get-collision-rec box-a box-b)))
|
||||
|
||||
(when (rl/key-pressed? :space) (set paused (not paused)))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
(rl/draw-rectangle 0 0 screen-width screen-upper-limit
|
||||
(if collision rl/red rl/black))
|
||||
|
||||
(rl/draw-rectangle-rec box-a rl/gold)
|
||||
(rl/draw-rectangle-rec box-b rl/blue)
|
||||
|
||||
(when collision
|
||||
;; The overlap itself, drawn over both boxes. This patch landing
|
||||
;; anywhere other than where the gold and the blue cross is what a
|
||||
;; permuted Rectangle would look like.
|
||||
(rl/draw-rectangle-rec box-collision rl/lime)
|
||||
|
||||
(rl/draw-text "COLLISION!"
|
||||
(- (/ (rl/get-screen-width) 2)
|
||||
(/ (rl/measure-text "COLLISION!" 20) 2))
|
||||
(- (/ screen-upper-limit 2) 10) 20 rl/black)
|
||||
|
||||
;; The C's TextFormat("Collision Area: %i", ...). Both dimensions are
|
||||
;; truncated to int before multiplying, as the C's two casts do.
|
||||
(let [x (- (/ (rl/get-screen-width) 2) 100)
|
||||
y (+ screen-upper-limit 10)]
|
||||
(set x (+ x (d/draw-piece "Collision Area: " x y 20 rl/black)))
|
||||
(d/draw-int (* (i32 (.width box-collision)) (i32 (.height box-collision)))
|
||||
x y 20 rl/black)))
|
||||
|
||||
(rl/draw-text "Press SPACE to PAUSE/RESUME" 20 (- screen-height 35) 20
|
||||
rl/lightgray)
|
||||
|
||||
(rl/draw-fps 10 10)
|
||||
|
||||
(rl/end-drawing)))
|
||||
103
examples/shapes-following-eyes.flan
Normal file
103
examples/shapes-following-eyes.flan
Normal file
@ -0,0 +1,103 @@
|
||||
;;;; raylib [shapes] example - following eyes
|
||||
;;;;
|
||||
;;;; examples/shapes/shapes_following_eyes.c. Ported for the thing it needs
|
||||
;;;; that raylib does not supply: the whole update is vector arithmetic —
|
||||
;;;; subtract two points, take the angle, walk back out along it — and every
|
||||
;;;; one of those steps is raymath. raymath is `static inline` in
|
||||
;;;; raymath.h, so Vector2Subtract and Vector2Angle have no symbol in
|
||||
;;;; libraylib at all and there is nothing for declare-c to bind. NEXT.md
|
||||
;;;; names this as one of the three open gaps.
|
||||
;;;;
|
||||
;;;; What that costs here is nothing, because the C does not use raymath
|
||||
;;;; either — it writes the four subtractions out and calls atan2f, cosf and
|
||||
;;;; sinf from libm, and Flan's prelude already declares all three as
|
||||
;;;; atan2-f32, cos-f32 and sin-f32. So this file is the *measurement* of the
|
||||
;;;; gap rather than a workaround for it: an example whose every line is
|
||||
;;;; vector maths, written without a vector library, in the same shape the C
|
||||
;;;; has. It reads fine. A Vector2Add would have saved two lines in the whole
|
||||
;;;; file.
|
||||
;;;;
|
||||
;;;; The raylib call it does exercise is collision-point-circle?, which no
|
||||
;;;; ported example had called, on a frame path, with the point coming
|
||||
;;;; straight out of get-mouse-position — one struct out of raylib and back
|
||||
;;;; into it untouched.
|
||||
;;;;
|
||||
;;;; The eye geometry, since the C hides it in a subtraction: the iris is
|
||||
;;;; pinned inside a circle of (sclera - iris) radius about the sclera's
|
||||
;;;; centre, so the iris disc is tangent to the sclera's edge at the extreme
|
||||
;;;; rather than hanging over it.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defconst sclera-radius f32 80.0)
|
||||
(defconst iris-radius f32 24.0)
|
||||
|
||||
(defvar sclera-left rl/Vector2)
|
||||
(defvar sclera-right rl/Vector2)
|
||||
(defvar iris-left rl/Vector2)
|
||||
(defvar iris-right rl/Vector2)
|
||||
|
||||
;; One eye's pupil, given where the mouse is and where the eye is. Answers the
|
||||
;; mouse position unchanged while it is inside the eye, and the point on the
|
||||
;; limit circle in that direction when it is outside.
|
||||
;;
|
||||
;; The C repeats this block twice with different variables; it is one function
|
||||
;; here because the two copies are identical and a difference between them
|
||||
;; would be invisible.
|
||||
(defn track [mouse rl/Vector2 centre rl/Vector2] rl/Vector2
|
||||
(let [limit (- sclera-radius iris-radius)]
|
||||
(if (rl/collision-point-circle? mouse centre limit)
|
||||
mouse
|
||||
;; Outside: the direction from the eye to the mouse, and then `limit`
|
||||
;; units back out along it. atan2 then cos/sin rather than a normalise,
|
||||
;; because that is what the C does and because it needs no guard for a
|
||||
;; zero-length vector — atan2(0,0) is 0 and the pupil sits at the right
|
||||
;; of the eye, which is unreachable anyway since (0,0) is inside.
|
||||
(let [dx (- (.x mouse) (.x centre))
|
||||
dy (- (.y mouse) (.y centre))
|
||||
angle (atan2-f32 dy dx)]
|
||||
(rl/Vector2 {.x (+ (.x centre) (* limit (cos-f32 angle)))
|
||||
.y (+ (.y centre) (* limit (sin-f32 angle)))})))))
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [shapes] example - following eyes")
|
||||
(defer (rl/close-window))
|
||||
|
||||
;; The C reads these off get-screen-width/get-screen-height after the window
|
||||
;; is open rather than off the constants, and so does this: on a high-DPI
|
||||
;; display the two can differ.
|
||||
(set sclera-left (rl/Vector2 {.x (- (/ (f32 (rl/get-screen-width)) 2.0) 100.0)
|
||||
.y (/ (f32 (rl/get-screen-height)) 2.0)}))
|
||||
(set sclera-right (rl/Vector2 {.x (+ (/ (f32 (rl/get-screen-width)) 2.0) 100.0)
|
||||
.y (/ (f32 (rl/get-screen-height)) 2.0)}))
|
||||
(set iris-left sclera-left)
|
||||
(set iris-right sclera-right)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update
|
||||
(let [mouse (rl/get-mouse-position)]
|
||||
(set iris-left (track mouse sclera-left))
|
||||
(set iris-right (track mouse sclera-right)))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
;; White of the eye, iris, pupil — in that order, each over the last.
|
||||
(rl/draw-circle-v sclera-left sclera-radius rl/lightgray)
|
||||
(rl/draw-circle-v iris-left iris-radius rl/brown)
|
||||
(rl/draw-circle-v iris-left 10.0 rl/black)
|
||||
|
||||
(rl/draw-circle-v sclera-right sclera-radius rl/lightgray)
|
||||
(rl/draw-circle-v iris-right iris-radius rl/darkgreen)
|
||||
(rl/draw-circle-v iris-right 10.0 rl/black)
|
||||
|
||||
(rl/draw-fps 10 10)
|
||||
|
||||
(rl/end-drawing)))
|
||||
140
examples/text-input-box.flan
Normal file
140
examples/text-input-box.flan
Normal file
@ -0,0 +1,140 @@
|
||||
;;;; raylib [text] example - input box
|
||||
;;;;
|
||||
;;;; examples/text/text_input_box.c. Two things in this file exist nowhere
|
||||
;;;; else in the corpus.
|
||||
;;;;
|
||||
;;;; The first is get-char-pressed. Everything ported so far reads input as
|
||||
;;;; state — key-down?, key-pressed?, the mouse position — and this is the
|
||||
;;;; only example that drains a *queue*: raylib buffers the characters the
|
||||
;;;; platform produced since the last frame, already through the keyboard
|
||||
;;;; layout and the dead keys, and hands them back one at a time until it
|
||||
;;;; answers 0. The loop that empties it is the point of the example, and a
|
||||
;;;; program that read the queue once per frame instead would silently drop
|
||||
;;;; the second character of a fast pair. It is a plain i32 out of the
|
||||
;;;; generated bindings; nothing needed adding for it.
|
||||
;;;;
|
||||
;;;; The second is set-mouse-cursor, which needed a new defenum. raylib's
|
||||
;;;; header says `int cursor` and means one of eleven MOUSE_CURSOR_ values, so
|
||||
;;;; the Flan face is `MouseCursor` — hand-written in raylib.flan beside the
|
||||
;;;; other cursor calls, excluded from the generated half, and mapped in
|
||||
;;;; `bindings` so the eleven members are checked against the header. See the
|
||||
;;;; comment there: this call is made on every frame off a hover test, and the
|
||||
;;;; failure an i32 would allow is not a crash but a pointer in the wrong
|
||||
;;;; shape, which nothing reports.
|
||||
;;;;
|
||||
;;;; The text buffer, and what it says about strings. The C keeps a
|
||||
;;;; `char name[MAX_INPUT_CHARS + 1]` and maintains the NUL itself; here it is
|
||||
;;;; a `[9 u8]` with no terminator, and every use is `(string (slice name 0
|
||||
;;;; letter-count))`. A Flan string is a pointer and a length, and the shim
|
||||
;;;; that calls DrawText copies it and NUL-terminates the copy
|
||||
;;;; (lib/shim.ml) — so the extra byte the C needs has no counterpart here and
|
||||
;;;; the buffer is exactly as long as the characters it can hold. The two
|
||||
;;;; draws and the measure all take the same slice, so they cannot disagree
|
||||
;;;; about where the text ends.
|
||||
;;;;
|
||||
;;;; Dropped from the C: its IsAnyKeyPressed helper, which is defined at the
|
||||
;;;; bottom of the file and called from nowhere.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
(import d "digits.flan")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defconst max-input-chars 9)
|
||||
|
||||
;; No +1: see the header comment. There is no NUL to leave room for.
|
||||
(defvar name [9 u8])
|
||||
(defvar letter-count i32)
|
||||
|
||||
(defvar text-box rl/Rectangle)
|
||||
(defvar mouse-on-text bool)
|
||||
(defvar frames-counter i32)
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [text] example - input box")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set name (array max-input-chars u8))
|
||||
(set letter-count 0)
|
||||
|
||||
(set text-box (rl/Rectangle {.x (- (/ (f32 screen-width) 2.0) 100.0)
|
||||
.y 180.0 .width 225.0 .height 50.0}))
|
||||
(set mouse-on-text false)
|
||||
(set frames-counter 0)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update
|
||||
(set mouse-on-text
|
||||
(rl/collision-point-rec? (rl/get-mouse-position) text-box))
|
||||
|
||||
(if mouse-on-text
|
||||
(do
|
||||
(rl/set-mouse-cursor :ibeam)
|
||||
|
||||
;; Drain the character queue. raylib answers 0 when it is empty, and
|
||||
;; more than one character can arrive in a single frame — holding a
|
||||
;; key with the platform's repeat on is the ordinary way that happens.
|
||||
(let [key (rl/get-char-pressed)]
|
||||
(while (> key 0)
|
||||
;; 32..125 is the printable ASCII range the C accepts. Anything
|
||||
;; outside it — an accented letter, a control character — is
|
||||
;; dropped rather than stored, because the buffer is bytes and a
|
||||
;; codepoint above 127 would need more than one of them.
|
||||
(when (and (>= key 32) (<= key 125) (< letter-count max-input-chars))
|
||||
(set (at name letter-count) (u8 key))
|
||||
(set letter-count (+ letter-count 1)))
|
||||
(set key (rl/get-char-pressed))))
|
||||
|
||||
(when (rl/key-pressed? :backspace)
|
||||
(set letter-count (- letter-count 1))
|
||||
(when (< letter-count 0) (set letter-count 0))))
|
||||
(rl/set-mouse-cursor :default))
|
||||
|
||||
;; The blink phase only runs while the box has focus, so the underscore is
|
||||
;; always visible on the frame the pointer arrives.
|
||||
(if mouse-on-text
|
||||
(set frames-counter (+ frames-counter 1))
|
||||
(set frames-counter 0))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
(rl/draw-text "PLACE MOUSE OVER INPUT BOX!" 240 140 20 rl/gray)
|
||||
|
||||
(rl/draw-rectangle-rec text-box rl/lightgray)
|
||||
(rl/draw-rectangle-lines (i32 (.x text-box)) (i32 (.y text-box))
|
||||
(i32 (.width text-box)) (i32 (.height text-box))
|
||||
(if mouse-on-text rl/red rl/darkgray))
|
||||
|
||||
(let [typed (string (slice name 0 letter-count))]
|
||||
(rl/draw-text typed (+ (i32 (.x text-box)) 5) (+ (i32 (.y text-box)) 8)
|
||||
40 rl/maroon)
|
||||
|
||||
;; The C's TextFormat("INPUT CHARS: %i/%i", ...), reassembled out of
|
||||
;; examples/digits.flan the way the other ports do it. `typed` is a view
|
||||
;; of `name` and not of the runtime's shared format buffer, so it
|
||||
;; survives the two numbers being formatted — which a value from
|
||||
;; i64->bytes would not.
|
||||
(let [x 315]
|
||||
(set x (+ x (d/draw-piece "INPUT CHARS: " x 250 20 rl/darkgray)))
|
||||
(set x (+ x (d/draw-int letter-count x 250 20 rl/darkgray)))
|
||||
(set x (+ x (d/draw-piece "/" x 250 20 rl/darkgray)))
|
||||
(d/draw-int max-input-chars x 250 20 rl/darkgray))
|
||||
|
||||
(when mouse-on-text
|
||||
(if (< letter-count max-input-chars)
|
||||
;; Twenty frames on, twenty frames off, at the pen position after
|
||||
;; whatever has been typed.
|
||||
(when (= (% (/ frames-counter 20) 2) 0)
|
||||
(rl/draw-text "_"
|
||||
(+ (i32 (.x text-box)) 8 (rl/measure-text typed 40))
|
||||
(+ (i32 (.y text-box)) 12) 40 rl/maroon))
|
||||
(rl/draw-text "Press BACKSPACE to delete chars..." 230 300 20
|
||||
rl/gray))))
|
||||
|
||||
(rl/end-drawing)))
|
||||
76
examples/text-writing-anim.flan
Normal file
76
examples/text-writing-anim.flan
Normal file
@ -0,0 +1,76 @@
|
||||
;;;; raylib [text] example - text writing anim
|
||||
;;;;
|
||||
;;;; examples/text/text_writing_anim.c. Sixty lines of C whose whole substance
|
||||
;;;; is one call this package cannot bind:
|
||||
;;;;
|
||||
;;;; DrawText(TextSubtext(message, 0, framesCounter/10), ...)
|
||||
;;;;
|
||||
;;;; TextSubtext is in raylib.h and is not in the bindings, on the rule
|
||||
;;;; raylib.flan states for the whole Text* family: it answers a `char *` into
|
||||
;;;; a rotating static buffer, and declare-c refuses a returned pointer to
|
||||
;;;; memory the caller does not own. There is no missing line to add — the
|
||||
;;;; binding would be wrong at any signature.
|
||||
;;;;
|
||||
;;;; And it does not matter, because Flan has the operation as a primitive.
|
||||
;;;; `(slice a lo hi)` takes a view of an array, `(string b)` reinterprets the
|
||||
;;;; bytes as a string at no cost, and `(string (slice message 0 n))` is
|
||||
;;;; TextSubtext with the static buffer removed — no copy, no shared state, no
|
||||
;;;; rotation to run out of. Porting the example is therefore how the gap gets
|
||||
;;;; *closed* rather than reported: the C's workaround for not having slices
|
||||
;;;; is the thing Flan did not need.
|
||||
;;;;
|
||||
;;;; The one place they differ, and it is why the clamp below is here rather
|
||||
;;;; than in the C: TextSubtext clips its length to the string, so the C can
|
||||
;;;; pass a counter that runs past the end for ever and see nothing happen.
|
||||
;;;; `slice` does not clip — an out-of-range bound is an error, and a bound
|
||||
;;;; computed at run time is checked at run time — so the length has to be
|
||||
;;;; brought inside the string before the call. That is a better failure than
|
||||
;;;; the C's, and it costs one `min`.
|
||||
;;;;
|
||||
;;;; The message is a `[u8]` and not a `string` because `slice` takes an array
|
||||
;;;; or a slice; `(bytes "…")` is the bridge in the other direction from
|
||||
;;;; `(string …)` and costs nothing either. The embedded newline is written as
|
||||
;;;; an escape, and raylib's draw-text breaks the line on it.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defconst message
|
||||
"This sample illustrates a text writing\nanimation effect! Check it out! ;)")
|
||||
|
||||
(defvar frames-counter i32)
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [text] example - text writing anim")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set frames-counter 0)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update. Holding space runs the counter eight times faster; enter starts
|
||||
;; it over.
|
||||
(if (rl/key-down? :space)
|
||||
(set frames-counter (+ frames-counter 8))
|
||||
(set frames-counter (+ frames-counter 1)))
|
||||
|
||||
(when (rl/key-pressed? :enter) (set frames-counter 0))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
;; One character every ten frames. The clamp is the whole difference from
|
||||
;; the C — see the header comment.
|
||||
(let [b (bytes message)
|
||||
n (min (i32 (len b)) (/ frames-counter 10))]
|
||||
(rl/draw-text (string (slice b 0 n)) 210 160 20 rl/maroon))
|
||||
|
||||
(rl/draw-text "PRESS [ENTER] to RESTART!" 240 260 20 rl/lightgray)
|
||||
(rl/draw-text "HOLD [SPACE] to SPEED UP!" 239 300 20 rl/lightgray)
|
||||
|
||||
(rl/end-drawing)))
|
||||
197
examples/textures-fog-of-war.flan
Normal file
197
examples/textures-fog-of-war.flan
Normal file
@ -0,0 +1,197 @@
|
||||
;;;; raylib [textures] example - fog of war
|
||||
;;;;
|
||||
;;;; examples/textures/textures_fog_of_war.c. Picked because it is the only
|
||||
;;;; example in the whole upstream tree where a *texture filter* is the point
|
||||
;;;; rather than a detail. The fog is drawn into a render texture one pixel
|
||||
;;;; per tile — 25 by 15 — and then stretched to 800x450 on the way to the
|
||||
;;;; screen, and the smooth edge between seen and unseen is entirely the
|
||||
;;;; bilinear sampler doing the interpolation. With the default :point filter
|
||||
;;;; the same program draws 32-pixel squares.
|
||||
;;;;
|
||||
;;;; That is what needed adding: `TextureFilter`, a new defenum in
|
||||
;;;; vendor/raylib/raylib.flan, with set-texture-filter moved from the
|
||||
;;;; generated half to the hand-written one and mapped in `bindings` so its
|
||||
;;;; six members are checked against raylib.h. The header says `int filter`
|
||||
;;;; and there is nothing in an `int` to say that 1 is the interesting value;
|
||||
;;;; `:bilinear` says it.
|
||||
;;;;
|
||||
;;;; The three other things it exercises, none of which needed a line:
|
||||
;;;;
|
||||
;;;; - A render texture used as a *scratch surface* rather than as a
|
||||
;;;; post-processing pass. core-scissor-test clipped the real framebuffer;
|
||||
;;;; this draws a whole second frame into a 25x15 target and then samples
|
||||
;;;; it. begin-texture-mode/end-texture-mode were already bound.
|
||||
;;;; - draw-texture-pro with a NEGATIVE source height. A render texture's
|
||||
;;;; rows come out of GL bottom-up, so every program that samples one has
|
||||
;;;; to flip it, and raylib's convention for that is a source rectangle
|
||||
;;;; with a negative height rather than a separate flag. Nothing in the
|
||||
;;;; corpus had passed one. A Rectangle whose `height` field had been
|
||||
;;;; permuted with `width` would draw the fog mirrored and the wrong way
|
||||
;;;; up at once.
|
||||
;;;; - clear-background with `blank` — alpha 0 — inside a texture mode, so
|
||||
;;;; the untouched parts of the fog target are transparent and the map
|
||||
;;;; shows through.
|
||||
;;;;
|
||||
;;;; The C's Map struct with its two calloc'd `unsigned char *` is two `[375
|
||||
;;;; u8]` globals here. The dimensions are compile-time constants in the C
|
||||
;;;; too (the #defines and the two assignments right after), so nothing is
|
||||
;;;; lost by fixing them, and a global cannot hold a Vec in any case
|
||||
;;;; (PORTING.md §3). 375 is 25*15, written out because Flan's array length
|
||||
;;;; must be a literal.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
(import d "digits.flan")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defconst map-tile-size 32)
|
||||
(defconst player-size 16)
|
||||
|
||||
;; How far the player sees, in tiles. The C's loop runs from -this to +this
|
||||
;; exclusive, so the lit patch is 2*this tiles across and not 2*this+1 — an
|
||||
;; asymmetry in the original that is kept rather than tidied, because tidying
|
||||
;; it would change the picture.
|
||||
(defconst player-tile-visibility 2)
|
||||
|
||||
(defconst tiles-x 25)
|
||||
(defconst tiles-y 15)
|
||||
|
||||
;; 0 or 1, picked once: which of the two blues a tile is drawn in.
|
||||
(defvar tile-ids [375 u8])
|
||||
|
||||
;; 0 = never seen (solid black), 1 = visible now (no fog), 2 = seen before
|
||||
;; (mostly black). The three-way state is why this is a byte per tile and not
|
||||
;; a bit.
|
||||
(defvar tile-fog [375 u8])
|
||||
|
||||
(defvar player-position rl/Vector2)
|
||||
(defvar player-tile-x i32)
|
||||
(defvar player-tile-y i32)
|
||||
|
||||
(defvar fog-of-war rl/RenderTexture2D)
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [textures] example - fog of war")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set tile-ids (array 375 u8))
|
||||
(set tile-fog (array 375 u8))
|
||||
|
||||
(dotimes [i (* tiles-x tiles-y)]
|
||||
(set (at tile-ids i) (u8 (rl/get-random-value 0 1))))
|
||||
|
||||
(set player-position (rl/Vector2 {.x 180.0 .y 130.0}))
|
||||
(set player-tile-x 0)
|
||||
(set player-tile-y 0)
|
||||
|
||||
;; One texel per tile. The stretch to full size on draw is where the
|
||||
;; smoothing happens, which is why the target is this small on purpose.
|
||||
(set fog-of-war (rl/load-render-texture tiles-x tiles-y))
|
||||
(defer (rl/unload-render-texture fog-of-war))
|
||||
|
||||
(rl/set-texture-filter (.texture fog-of-war) :bilinear)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update
|
||||
(when (rl/key-down? :right) (set (.x player-position) (+ (.x player-position) 5.0)))
|
||||
(when (rl/key-down? :left) (set (.x player-position) (- (.x player-position) 5.0)))
|
||||
(when (rl/key-down? :down) (set (.y player-position) (+ (.y player-position) 5.0)))
|
||||
(when (rl/key-down? :up) (set (.y player-position) (- (.y player-position) 5.0)))
|
||||
|
||||
;; Keep the player inside the tilemap. The far edge is measured against
|
||||
;; the player's far side, which is why player-size is subtracted.
|
||||
(let [max-x (f32 (- (* tiles-x map-tile-size) player-size))
|
||||
max-y (f32 (- (* tiles-y map-tile-size) player-size))]
|
||||
(set (.x player-position) (clamp (.x player-position) 0.0 max-x))
|
||||
(set (.y player-position) (clamp (.y player-position) 0.0 max-y)))
|
||||
|
||||
;; Everything lit on the previous frame drops to "seen before". The lit
|
||||
;; tiles are then set back to 1 below, so a tile still in view never
|
||||
;; spends a frame dimmed.
|
||||
(dotimes [i (* tiles-x tiles-y)]
|
||||
(when (= (at tile-fog i) 1) (set (at tile-fog i) 2)))
|
||||
|
||||
;; Which tile the player's centre is standing on.
|
||||
(set player-tile-x
|
||||
(i32 (/ (+ (.x player-position) (f32 (/ map-tile-size 2)))
|
||||
(f32 map-tile-size))))
|
||||
(set player-tile-y
|
||||
(i32 (/ (+ (.y player-position) (f32 (/ map-tile-size 2)))
|
||||
(f32 map-tile-size))))
|
||||
|
||||
;; Light the square around the player, skipping anything off the map.
|
||||
;; Without that test this reads and writes outside the array, which in the
|
||||
;; C is undefined and here is a bounds trap — the same bug, reported.
|
||||
(let [y (- player-tile-y player-tile-visibility)]
|
||||
(while (< y (+ player-tile-y player-tile-visibility))
|
||||
(let [x (- player-tile-x player-tile-visibility)]
|
||||
(while (< x (+ player-tile-x player-tile-visibility))
|
||||
(when (and (>= x 0) (< x tiles-x) (>= y 0) (< y tiles-y))
|
||||
(set (at tile-fog (+ (* y tiles-x) x)) 1))
|
||||
(set x (+ x 1))))
|
||||
(set y (+ y 1))))
|
||||
|
||||
;; Draw the fog into its own little target first, at one pixel per tile.
|
||||
;; blank is alpha 0, so a tile that is neither unseen nor remembered
|
||||
;; leaves nothing behind and the map below shows through unmodified.
|
||||
(rl/begin-texture-mode fog-of-war)
|
||||
(rl/clear-background rl/blank)
|
||||
(dotimes [y tiles-y]
|
||||
(dotimes [x tiles-x]
|
||||
;; A tile in view (1) draws nothing at all and stays transparent.
|
||||
(let [f (at tile-fog (+ (* y tiles-x) x))]
|
||||
(when (!= f 1)
|
||||
(rl/draw-rectangle x y 1 1
|
||||
(if (= f 0) rl/black (rl/fade rl/black 0.8)))))))
|
||||
(rl/end-texture-mode)
|
||||
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
;; The map itself, in full size.
|
||||
(dotimes [y tiles-y]
|
||||
(dotimes [x tiles-x]
|
||||
(rl/draw-rectangle (* x map-tile-size) (* y map-tile-size)
|
||||
map-tile-size map-tile-size
|
||||
(if (= (at tile-ids (+ (* y tiles-x) x)) 0)
|
||||
rl/blue
|
||||
(rl/fade rl/blue 0.9)))
|
||||
(rl/draw-rectangle-lines (* x map-tile-size) (* y map-tile-size)
|
||||
map-tile-size map-tile-size
|
||||
(rl/fade rl/darkblue 0.5))))
|
||||
|
||||
(rl/draw-rectangle-v player-position
|
||||
(rl/Vector2 {.x (f32 player-size) .y (f32 player-size)})
|
||||
rl/red)
|
||||
|
||||
;; The fog, stretched over the whole map. The negative source height is
|
||||
;; the flip — see the header comment.
|
||||
(rl/draw-texture-pro
|
||||
(.texture fog-of-war)
|
||||
(rl/Rectangle {.x 0.0 .y 0.0
|
||||
.width (f32 (.width (.texture fog-of-war)))
|
||||
.height (- 0.0 (f32 (.height (.texture fog-of-war))))})
|
||||
(rl/Rectangle {.x 0.0 .y 0.0
|
||||
.width (f32 (* tiles-x map-tile-size))
|
||||
.height (f32 (* tiles-y map-tile-size))})
|
||||
(rl/Vector2 {.x 0.0 .y 0.0})
|
||||
0.0
|
||||
rl/white)
|
||||
|
||||
;; The C's TextFormat("Current tile: [%i,%i]", ...). Each number is drawn
|
||||
;; before the next is formatted, which examples/digits.flan requires:
|
||||
;; both share one static buffer in the runtime.
|
||||
(let [x 10]
|
||||
(set x (+ x (d/draw-piece "Current tile: [" x 10 20 rl/raywhite)))
|
||||
(set x (+ x (d/draw-int player-tile-x x 10 20 rl/raywhite)))
|
||||
(set x (+ x (d/draw-piece "," x 10 20 rl/raywhite)))
|
||||
(set x (+ x (d/draw-int player-tile-y x 10 20 rl/raywhite)))
|
||||
(d/draw-piece "]" x 10 20 rl/raywhite))
|
||||
|
||||
(rl/draw-text "ARROW KEYS to move" 10 (- screen-height 25) 20 rl/raywhite)
|
||||
|
||||
(rl/end-drawing)))
|
||||
130
examples/textures-image-generation.flan
Normal file
130
examples/textures-image-generation.flan
Normal file
@ -0,0 +1,130 @@
|
||||
;;;; raylib [textures] example - procedural images generation
|
||||
;;;;
|
||||
;;;; examples/textures/textures_image_generation.c. The strongest single pick
|
||||
;;;; in the textures category and the reason is arithmetic: eight of the nine
|
||||
;;;; textures come from a Gen* call that no program in this tree had ever
|
||||
;;;; made, and none of the nine needs a file on disk. Every other textures
|
||||
;;;; example loads a .png out of its resources directory; this one builds all
|
||||
;;;; of its pixels.
|
||||
;;;;
|
||||
;;;; What it puts under load that nothing else does. An Image is the one
|
||||
;;;; struct in raylib.flan that carries a pointer to memory raylib owns —
|
||||
;;;; `data (Ptr u8)` — and it is returned by value, so every one of these nine
|
||||
;;;; calls hands back a 24-byte aggregate through the shim's out-pointer with
|
||||
;;;; a live heap block inside it. The corpus had crossed an Image before
|
||||
;;;; (image-from-image has a headless acceptance case) but never nine in a row
|
||||
;;;; and never on the load-then-upload-then-free path a real program uses:
|
||||
;;;; gen, load-texture-from-image to get it onto the GPU, unload-image to give
|
||||
;;;; the CPU copy back. Getting that order wrong is a leak rather than a
|
||||
;;;; crash, which is exactly the sort of thing a ported example is for.
|
||||
;;;;
|
||||
;;;; Nothing needed adding. All nine generators and load-texture-from-image
|
||||
;;;; came out of the generated half of the bindings; the only parameter that
|
||||
;;;; looks like it wants an enum is gen-image-gradient-linear's `direction`,
|
||||
;;;; which is an angle in degrees and not a flag, so an i32 is its true face.
|
||||
;;;;
|
||||
;;;; The C's `switch (currentTexture)` is a `cond` here. Flan has no switch
|
||||
;;;; and the chain reads the same; the C's `default: break` is the `else`
|
||||
;;;; branch (`:else`), unreachable because the index is taken modulo the count.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
;; Nine and not eight: the linear gradient appears three times, at three
|
||||
;; angles, because a vertical, a horizontal and a diagonal gradient are the
|
||||
;; same generator with a different `direction`.
|
||||
(defconst num-textures 9)
|
||||
|
||||
(defvar textures [9 rl/Texture2D])
|
||||
(defvar current-texture i32)
|
||||
|
||||
;; Generate on the CPU, upload, drop the pixels. The texture holds a GL name
|
||||
;; and nothing of the Image, so the CPU copy can go as soon as the upload is
|
||||
;; done — which is why the C frees all nine in a block immediately after the
|
||||
;; nine uploads and this does it one at a time. Holding all nine 800x450 RGBA
|
||||
;; images at once would be 14 MB for no reason.
|
||||
;;
|
||||
;; A top-level defn and not a local closure: Flan's `fn` takes its types from
|
||||
;; the position it is written in, so it needs a parameter of (Fn [T ...] R) to
|
||||
;; land in, and a `let` binding is not one.
|
||||
(defn upload [img rl/Image] rl/Texture2D
|
||||
(let [t (rl/load-texture-from-image img)]
|
||||
(rl/unload-image img)
|
||||
t))
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [textures] example - procedural images generation")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set textures (array num-textures rl/Texture2D))
|
||||
|
||||
;; 0 degrees runs the gradient top to bottom, 90 left to right, 45 corner
|
||||
;; to corner.
|
||||
(set (at textures 0)
|
||||
(upload (rl/gen-image-gradient-linear screen-width screen-height 0
|
||||
rl/red rl/blue)))
|
||||
(set (at textures 1)
|
||||
(upload (rl/gen-image-gradient-linear screen-width screen-height 90
|
||||
rl/red rl/blue)))
|
||||
(set (at textures 2)
|
||||
(upload (rl/gen-image-gradient-linear screen-width screen-height 45
|
||||
rl/red rl/blue)))
|
||||
;; density 0 means the falloff reaches the edge of the image.
|
||||
(set (at textures 3)
|
||||
(upload (rl/gen-image-gradient-radial screen-width screen-height 0.0
|
||||
rl/white rl/black)))
|
||||
(set (at textures 4)
|
||||
(upload (rl/gen-image-gradient-square screen-width screen-height 0.0
|
||||
rl/white rl/black)))
|
||||
;; 32 checks each way, so each square is 25 by 14 pixels.
|
||||
(set (at textures 5)
|
||||
(upload (rl/gen-image-checked screen-width screen-height 32 32
|
||||
rl/red rl/blue)))
|
||||
;; `factor` is the fraction of pixels that come out white.
|
||||
(set (at textures 6)
|
||||
(upload (rl/gen-image-white-noise screen-width screen-height 0.5)))
|
||||
(set (at textures 7)
|
||||
(upload (rl/gen-image-perlin-noise screen-width screen-height 50 50 4.0)))
|
||||
;; `tile-size` is the cell spacing, in pixels.
|
||||
(set (at textures 8)
|
||||
(upload (rl/gen-image-cellular screen-width screen-height 32)))
|
||||
|
||||
(defer (dotimes [i num-textures] (rl/unload-texture (at textures i))))
|
||||
|
||||
(set current-texture 0)
|
||||
|
||||
(rl/set-target-fps 60)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update
|
||||
(when (or (rl/mouse-button-pressed? :left) (rl/key-pressed? :right))
|
||||
(set current-texture (% (+ current-texture 1) num-textures)))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
(rl/draw-texture (at textures current-texture) 0 0 rl/white)
|
||||
|
||||
(rl/draw-rectangle 30 400 325 30 (rl/fade rl/skyblue 0.5))
|
||||
(rl/draw-rectangle-lines 30 400 325 30 (rl/fade rl/white 0.5))
|
||||
(rl/draw-text "MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES"
|
||||
40 410 10 rl/white)
|
||||
|
||||
;; The C's switch. Each label is positioned so its right edge lands in the
|
||||
;; same place, which is why the x differs per line.
|
||||
(cond
|
||||
(= current-texture 0) (rl/draw-text "VERTICAL GRADIENT" 560 10 20 rl/raywhite)
|
||||
(= current-texture 1) (rl/draw-text "HORIZONTAL GRADIENT" 540 10 20 rl/raywhite)
|
||||
(= current-texture 2) (rl/draw-text "DIAGONAL GRADIENT" 540 10 20 rl/raywhite)
|
||||
(= current-texture 3) (rl/draw-text "RADIAL GRADIENT" 580 10 20 rl/lightgray)
|
||||
(= current-texture 4) (rl/draw-text "SQUARE GRADIENT" 580 10 20 rl/lightgray)
|
||||
(= current-texture 5) (rl/draw-text "CHECKED" 680 10 20 rl/raywhite)
|
||||
(= current-texture 6) (rl/draw-text "WHITE NOISE" 640 10 20 rl/red)
|
||||
(= current-texture 7) (rl/draw-text "PERLIN NOISE" 640 10 20 rl/red)
|
||||
:else (rl/draw-text "CELLULAR" 670 10 20 rl/raywhite))
|
||||
|
||||
(rl/end-drawing)))
|
||||
255
examples/textures-mouse-painting.flan
Normal file
255
examples/textures-mouse-painting.flan
Normal file
@ -0,0 +1,255 @@
|
||||
;;;; raylib [textures] example - mouse painting
|
||||
;;;;
|
||||
;;;; examples/textures/textures_mouse_painting.c. The one example in the
|
||||
;;;; category that takes a picture back OFF the GPU and writes it to disk, and
|
||||
;;;; that round trip is why it is here:
|
||||
;;;;
|
||||
;;;; load-image-from-texture → image-flip-vertical → export-image
|
||||
;;;;
|
||||
;;;; Nothing in the corpus had run it. image-from-image has a headless
|
||||
;;;; acceptance case and sand.flan loads images the other way, but the path
|
||||
;;;; that reads a render target's pixels back into CPU memory, corrects for
|
||||
;;;; GL's bottom-up rows and encodes a PNG had never been exercised at all.
|
||||
;;;; All three came out of the generated half of the bindings; nothing needed
|
||||
;;;; adding for this file.
|
||||
;;;;
|
||||
;;;; The flip is the same fact as the negative source height in
|
||||
;;;; examples/textures-fog-of-war.flan, met from the other side: on screen the
|
||||
;;;; canvas is drawn with a negative-height source rectangle so GL's bottom-up
|
||||
;;;; rows come out the right way up, and on save there is no source rectangle
|
||||
;;;; to negate, so the pixels are turned over in memory instead. A program
|
||||
;;;; that did one and not the other would show a correct picture and save an
|
||||
;;;; upside-down one — which is exactly the bug the C's comment warns about.
|
||||
;;;;
|
||||
;;;; The other thing this puts under load is the render texture as *persistent
|
||||
;;;; state*. The fog-of-war target is rebuilt from nothing every frame; this
|
||||
;;;; one is the document — it is cleared once before the loop and every stroke
|
||||
;;;; after that accumulates in it. Between the two the corpus has both ways a
|
||||
;;;; render texture gets used.
|
||||
;;;;
|
||||
;;;; Two deviations from the C, both in the same place and both deliberate.
|
||||
;;;; The C computes colorMouseHover with a `for` whose `else` clause resets it
|
||||
;;;; on every miss and whose `break` leaves it set on a hit, so the value that
|
||||
;;;; survives depends on the loop exiting early — it works, but only because
|
||||
;;;; of the break. Here the reset is before the loop and the loop only ever
|
||||
;;;; sets it, which is the same result said plainly. And "no colour hovered"
|
||||
;;;; is -1 in both, tested with `>= 0` before it is used as an index.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
(defconst screen-width 800)
|
||||
(defconst screen-height 450)
|
||||
|
||||
(defconst max-colors-count 23)
|
||||
|
||||
;; The palette strip along the top. colors[0] is also the canvas's clear
|
||||
;; colour and the "eraser", which is why raywhite is first and not simply
|
||||
;; another entry.
|
||||
(defvar colors [23 rl/Color])
|
||||
(defvar colors-recs [23 rl/Rectangle])
|
||||
|
||||
(defvar color-selected i32)
|
||||
(defvar color-selected-prev i32)
|
||||
(defvar color-mouse-hover i32)
|
||||
(defvar brush-size f32)
|
||||
(defvar mouse-was-pressed bool)
|
||||
|
||||
(defvar btn-save-rec rl/Rectangle)
|
||||
(defvar btn-save-mouse-hover bool)
|
||||
(defvar show-save-message bool)
|
||||
(defvar save-message-counter i32)
|
||||
|
||||
(defvar target rl/RenderTexture2D)
|
||||
|
||||
(defn main [] ()
|
||||
(rl/init-window screen-width screen-height
|
||||
"raylib [textures] example - mouse painting")
|
||||
(defer (rl/close-window))
|
||||
|
||||
(set colors (array max-colors-count rl/Color))
|
||||
(set (at colors 0) rl/raywhite)
|
||||
(set (at colors 1) rl/yellow)
|
||||
(set (at colors 2) rl/gold)
|
||||
(set (at colors 3) rl/orange)
|
||||
(set (at colors 4) rl/pink)
|
||||
(set (at colors 5) rl/red)
|
||||
(set (at colors 6) rl/maroon)
|
||||
(set (at colors 7) rl/green)
|
||||
(set (at colors 8) rl/lime)
|
||||
(set (at colors 9) rl/darkgreen)
|
||||
(set (at colors 10) rl/skyblue)
|
||||
(set (at colors 11) rl/blue)
|
||||
(set (at colors 12) rl/darkblue)
|
||||
(set (at colors 13) rl/purple)
|
||||
(set (at colors 14) rl/violet)
|
||||
(set (at colors 15) rl/darkpurple)
|
||||
(set (at colors 16) rl/beige)
|
||||
(set (at colors 17) rl/brown)
|
||||
(set (at colors 18) rl/darkbrown)
|
||||
(set (at colors 19) rl/lightgray)
|
||||
(set (at colors 20) rl/gray)
|
||||
(set (at colors 21) rl/darkgray)
|
||||
(set (at colors 22) rl/black)
|
||||
|
||||
;; 30 wide with a 2-pixel gap, starting 10 from the left.
|
||||
(set colors-recs (array max-colors-count rl/Rectangle))
|
||||
(dotimes [i max-colors-count]
|
||||
(set (at colors-recs i)
|
||||
(rl/Rectangle {.x (+ 10.0 (* 32.0 (f32 i)))
|
||||
.y 10.0 .width 30.0 .height 30.0})))
|
||||
|
||||
(set color-selected 0)
|
||||
(set color-selected-prev 0)
|
||||
(set color-mouse-hover 0)
|
||||
(set brush-size 20.0)
|
||||
(set mouse-was-pressed false)
|
||||
|
||||
(set btn-save-rec (rl/Rectangle {.x 750.0 .y 10.0 .width 40.0 .height 30.0}))
|
||||
(set btn-save-mouse-hover false)
|
||||
(set show-save-message false)
|
||||
(set save-message-counter 0)
|
||||
|
||||
;; The canvas. It is the document, not a scratch buffer: nothing clears it
|
||||
;; again until the user presses C.
|
||||
(set target (rl/load-render-texture screen-width screen-height))
|
||||
(defer (rl/unload-render-texture target))
|
||||
|
||||
(rl/begin-texture-mode target)
|
||||
(rl/clear-background (at colors 0))
|
||||
(rl/end-texture-mode)
|
||||
|
||||
;; 120 and not 60: the stroke is a circle stamped at the mouse position once
|
||||
;; per frame, so the gaps between stamps during a fast drag are a function
|
||||
;; of the frame rate. The C's comment says as much.
|
||||
(rl/set-target-fps 120)
|
||||
|
||||
(until (rl/window-should-close?)
|
||||
;; Update
|
||||
(let [mouse-pos (rl/get-mouse-position)]
|
||||
(if (rl/key-pressed? :right)
|
||||
(set color-selected (+ color-selected 1))
|
||||
(when (rl/key-pressed? :left)
|
||||
(set color-selected (- color-selected 1))))
|
||||
(set color-selected (clamp color-selected 0 (- max-colors-count 1)))
|
||||
|
||||
;; Which swatch the pointer is over, or -1. See the header comment: the
|
||||
;; reset is here rather than in an else branch inside the loop.
|
||||
(set color-mouse-hover -1)
|
||||
(dotimes [i max-colors-count]
|
||||
(when (rl/collision-point-rec? mouse-pos (at colors-recs i))
|
||||
(set color-mouse-hover i)
|
||||
(break)))
|
||||
|
||||
(when (and (>= color-mouse-hover 0) (rl/mouse-button-pressed? :left))
|
||||
(set color-selected color-mouse-hover)
|
||||
(set color-selected-prev color-selected))
|
||||
|
||||
(set brush-size (clamp (+ brush-size (* (rl/get-mouse-wheel-move) 5.0))
|
||||
2.0 50.0))
|
||||
|
||||
(when (rl/key-pressed? :c)
|
||||
(rl/begin-texture-mode target)
|
||||
(rl/clear-background (at colors 0))
|
||||
(rl/end-texture-mode))
|
||||
|
||||
;; Paint. The gesture test is what makes the example work on a
|
||||
;; touchscreen, where there is no mouse button to hold.
|
||||
(when (or (rl/mouse-button-down? :left)
|
||||
(= (rl/get-gesture-detected) :drag))
|
||||
(rl/begin-texture-mode target)
|
||||
;; Above y=50 is the palette strip, and a stroke there would paint
|
||||
;; under the toolbar where it could never be seen.
|
||||
(when (> (.y mouse-pos) 50.0)
|
||||
(rl/draw-circle (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
|
||||
brush-size (at colors color-selected)))
|
||||
(rl/end-texture-mode))
|
||||
|
||||
;; Right button erases, which is painting in the clear colour. The
|
||||
;; selected swatch is parked while the button is held so the toolbar
|
||||
;; shows what is being drawn, and restored on release.
|
||||
(if (rl/mouse-button-down? :right)
|
||||
(do
|
||||
(when (not mouse-was-pressed)
|
||||
(set color-selected-prev color-selected)
|
||||
(set color-selected 0))
|
||||
(set mouse-was-pressed true)
|
||||
(rl/begin-texture-mode target)
|
||||
(when (> (.y mouse-pos) 50.0)
|
||||
(rl/draw-circle (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
|
||||
brush-size (at colors 0)))
|
||||
(rl/end-texture-mode))
|
||||
(when (and (rl/mouse-button-released? :right) mouse-was-pressed)
|
||||
(set color-selected color-selected-prev)
|
||||
(set mouse-was-pressed false)))
|
||||
|
||||
(set btn-save-mouse-hover (rl/collision-point-rec? mouse-pos btn-save-rec))
|
||||
|
||||
;; The round trip. See the header comment for why the flip is here and
|
||||
;; not on the draw.
|
||||
(when (or (and btn-save-mouse-hover (rl/mouse-button-released? :left))
|
||||
(rl/key-pressed? :s))
|
||||
(let [image (rl/load-image-from-texture (.texture target))]
|
||||
(rl/image-flip-vertical (addr image))
|
||||
(rl/export-image image "my_amazing_texture_painting.png")
|
||||
(rl/unload-image image))
|
||||
(set show-save-message true))
|
||||
|
||||
(when show-save-message
|
||||
(set save-message-counter (+ save-message-counter 1))
|
||||
(when (> save-message-counter 240)
|
||||
(set show-save-message false)
|
||||
(set save-message-counter 0)))
|
||||
|
||||
;; Draw
|
||||
(rl/begin-drawing)
|
||||
(rl/clear-background rl/raywhite)
|
||||
|
||||
;; The canvas, flipped by the negative source height.
|
||||
(rl/draw-texture-rec (.texture target)
|
||||
(rl/Rectangle {.x 0.0 .y 0.0
|
||||
.width (f32 (.width (.texture target)))
|
||||
.height (- 0.0 (f32 (.height (.texture target))))})
|
||||
(rl/Vector2 {.x 0.0 .y 0.0})
|
||||
rl/white)
|
||||
|
||||
;; The brush preview, drawn on the screen and not into the canvas.
|
||||
(when (> (.y mouse-pos) 50.0)
|
||||
(if (rl/mouse-button-down? :right)
|
||||
(rl/draw-circle-lines (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
|
||||
brush-size rl/gray)
|
||||
(rl/draw-circle (rl/get-mouse-x) (rl/get-mouse-y)
|
||||
brush-size (at colors color-selected))))
|
||||
|
||||
;; The toolbar, over the canvas.
|
||||
(rl/draw-rectangle 0 0 (rl/get-screen-width) 50 rl/raywhite)
|
||||
(rl/draw-line 0 50 (rl/get-screen-width) 50 rl/lightgray)
|
||||
|
||||
(dotimes [i max-colors-count]
|
||||
(rl/draw-rectangle-rec (at colors-recs i) (at colors i)))
|
||||
;; The first swatch is raywhite on raywhite, so it needs an outline to
|
||||
;; be visible at all.
|
||||
(rl/draw-rectangle-lines 10 10 30 30 rl/lightgray)
|
||||
|
||||
(when (>= color-mouse-hover 0)
|
||||
(rl/draw-rectangle-rec (at colors-recs color-mouse-hover)
|
||||
(rl/fade rl/white 0.6)))
|
||||
|
||||
(let [r (at colors-recs color-selected)]
|
||||
(rl/draw-rectangle-lines-ex
|
||||
(rl/Rectangle {.x (- (.x r) 2.0) .y (- (.y r) 2.0)
|
||||
.width (+ (.width r) 4.0) .height (+ (.height r) 4.0)})
|
||||
2.0 rl/black))
|
||||
|
||||
(rl/draw-rectangle-lines-ex btn-save-rec 2.0
|
||||
(if btn-save-mouse-hover rl/red rl/black))
|
||||
(rl/draw-text "SAVE!" 755 20 10
|
||||
(if btn-save-mouse-hover rl/red rl/black))
|
||||
|
||||
(when show-save-message
|
||||
(rl/draw-rectangle 0 0 (rl/get-screen-width) (rl/get-screen-height)
|
||||
(rl/fade rl/raywhite 0.8))
|
||||
(rl/draw-rectangle 0 150 (rl/get-screen-width) 80 rl/black)
|
||||
(rl/draw-text "IMAGE SAVED: my_amazing_texture_painting.png"
|
||||
150 180 20 rl/raywhite))
|
||||
|
||||
(rl/end-drawing))))
|
||||
38
lib/load.ml
38
lib/load.ml
@ -877,17 +877,33 @@ let rec import ~seen ~open_ ~loc alias dir =
|
||||
in
|
||||
List.iter
|
||||
(fun (x : Cimport.const_diff) ->
|
||||
(* Only the findings that are the *library* contradicting the
|
||||
package stop a build. A [defenum] nobody mapped and a rule
|
||||
that reaches nothing are about the package's own
|
||||
`bindings` file, and they gate `flan generate-c`, which is
|
||||
where that file is edited — telling a lane that added an
|
||||
enum to go and fix a config, in a message shaped like "the
|
||||
layout is wrong", is the wrong thing to stop a build
|
||||
with. *)
|
||||
if not x.Cimport.cmapping then
|
||||
fail (Option.value ~default:loc (cloc x.Cimport.cname))
|
||||
"the package disagrees with %s: %s" h x.Cimport.cwhy)
|
||||
(* Both kinds stop a build, and they are worded differently
|
||||
because they are different accusations.
|
||||
|
||||
[cmapping = false] is the library contradicting the
|
||||
package: a value that is one number here and another
|
||||
there. [cmapping = true] is the package's own `bindings`
|
||||
file not covering something — a [defenum] nobody mapped,
|
||||
a rule that reaches nothing.
|
||||
|
||||
The second was gated to `flan generate-c` at first, on the
|
||||
argument that stopping a build over a config file is the
|
||||
wrong stop. The objection was really to the *wording*: it
|
||||
arrived wrapped in "the package disagrees with the
|
||||
header", which describes a layout bug and sends the reader
|
||||
to the wrong file. Gating it instead meant an unchecked
|
||||
enum member — exactly the silent wrongness the header read
|
||||
exists to catch — went unreported on every ordinary build,
|
||||
and "remember to run generate-c" is the same shape as
|
||||
"remember to check the header by eye", which is what this
|
||||
replaced. So: still fatal, and the wording names the file
|
||||
and both ways out. *)
|
||||
let at = Option.value ~default:loc (cloc x.Cimport.cname) in
|
||||
if x.Cimport.cmapping then
|
||||
fail at "%s" x.Cimport.cwhy
|
||||
else
|
||||
fail at "the package disagrees with %s: %s" h
|
||||
x.Cimport.cwhy)
|
||||
(Cimport.check_constants ~config ~enums ~consts:pconsts dump);
|
||||
r)
|
||||
(header_specs ~loc dir)
|
||||
|
||||
40
vendor/raylib/bindings
vendored
40
vendor/raylib/bindings
vendored
@ -90,13 +90,16 @@ exclude IsWindowReady
|
||||
# generated i32 would take. GetWorldToScreen goes with BeginMode3D
|
||||
# because they read the same camera.
|
||||
#
|
||||
# What this line splits, and deliberately: draw-cube is here and draw-cube-v
|
||||
# is generated, get-world-to-screen is here and get-world-to-screen-ex is
|
||||
# generated, update-camera is here and update-camera-pro is generated. Every
|
||||
# other family in raylib.flan — the texture draws, the circle draws — sits
|
||||
# together, so the split is worth naming: what is hand-written is what the
|
||||
# ported examples call, and hand-writing the variants as well would widen the
|
||||
# half that has to be maintained by hand for nothing the examples ask for.
|
||||
# What this line splits, and deliberately: get-world-to-screen is here and
|
||||
# get-world-to-screen-ex is generated, update-camera is here and
|
||||
# update-camera-pro is generated. (draw-cube-v was named here as the third
|
||||
# such pair and no longer is — see the DrawCubeV line further down, which is
|
||||
# what happens to a split of this kind when an example starts calling the
|
||||
# other half.) Every other family in raylib.flan — the texture draws, the
|
||||
# circle draws — sits together, so the split is worth naming: what is
|
||||
# hand-written is what the ported examples call, and hand-writing the variants
|
||||
# as well would widen the half that has to be maintained by hand for nothing
|
||||
# the examples ask for.
|
||||
#
|
||||
# What is deliberately NOT here: the window-state family (IsWindowState,
|
||||
# SetWindowState, ClearWindowState, ToggleFullscreen, Minimize/Maximize/
|
||||
@ -115,6 +118,27 @@ exclude SetExitKey
|
||||
exclude UpdateCamera
|
||||
exclude GetWorldToScreen
|
||||
|
||||
# The second batch of ported examples widened both halves of that split, and
|
||||
# each of these is here for one of the two reasons above and no new one.
|
||||
#
|
||||
# - SetMouseCursor and SetTextureFilter take an `int` in the header and one
|
||||
# of a closed set of names in fact, so their Flan face is a defenum and
|
||||
# not the C signature — the same trade SetExitKey makes.
|
||||
# - DrawCubeV, DrawSphere, DrawSphereWires and DrawRay are inside a frame,
|
||||
# in examples/models-box-collisions.flan and examples/core-3d-picking.flan.
|
||||
# draw-cube-v moving here is the one place this batch *narrows* the split
|
||||
# the paragraph above names: it was generated because nothing called it,
|
||||
# and something calls it now.
|
||||
# - GetScreenToWorldRay goes with GetWorldToScreen for the reason given
|
||||
# there. They are inverses over the same camera.
|
||||
exclude SetMouseCursor
|
||||
exclude SetTextureFilter
|
||||
exclude DrawCubeV
|
||||
exclude DrawSphere
|
||||
exclude DrawSphereWires
|
||||
exclude DrawRay
|
||||
exclude GetScreenToWorldRay
|
||||
|
||||
# ── What the package's constants are called in C ────────────────────
|
||||
#
|
||||
# enum <FlanEnum> <C_PREFIX> every member of that defenum
|
||||
@ -146,6 +170,8 @@ enum CameraMode CAMERA_
|
||||
enum GamepadButton GAMEPAD_BUTTON_
|
||||
enum GamepadAxis GAMEPAD_AXIS_
|
||||
enum Gesture GESTURE_
|
||||
enum MouseCursor MOUSE_CURSOR_
|
||||
enum TextureFilter TEXTURE_FILTER_
|
||||
|
||||
# raylib writes GESTURE_DOUBLETAP as one word where every other member of that
|
||||
# enum is underscored. This is the narrow exception and not a general escape
|
||||
|
||||
13
vendor/raylib/generated.flan
vendored
13
vendor/raylib/generated.flan
vendored
@ -60,6 +60,7 @@
|
||||
(declare-c begin-blend-mode [mode i32] "BeginBlendMode")
|
||||
(declare-c end-blend-mode [] "EndBlendMode")
|
||||
(declare-c end-vr-stereo-mode [] "EndVrStereoMode")
|
||||
(declare-c get-screen-to-world-ray-ex [position Vector2 camera Camera3D width i32 height i32] Ray "GetScreenToWorldRayEx")
|
||||
(declare-c get-world-to-screen-ex [position Vector3 camera Camera3D width i32 height i32] Vector2 "GetWorldToScreenEx")
|
||||
(declare-c swap-screen-buffer [] "SwapScreenBuffer")
|
||||
(declare-c poll-input-events [] "PollInputEvents")
|
||||
@ -106,7 +107,6 @@
|
||||
(declare-c set-mouse-offset [offset-x i32 offset-y i32] "SetMouseOffset")
|
||||
(declare-c set-mouse-scale [scale-x f32 scale-y f32] "SetMouseScale")
|
||||
(declare-c get-mouse-wheel-move-v [] Vector2 "GetMouseWheelMoveV")
|
||||
(declare-c set-mouse-cursor [cursor i32] "SetMouseCursor")
|
||||
(declare-c update-camera-pro [camera (Ptr Camera3D) movement Vector3 rotation Vector3 zoom f32] "UpdateCameraPro")
|
||||
(declare-c draw-line-strip [points (Ptr Vector2) point-count i32 color Color] "DrawLineStrip")
|
||||
(declare-c draw-line-bezier [start-pos Vector2 end-pos Vector2 thick f32 color Color] "DrawLineBezier")
|
||||
@ -206,7 +206,6 @@
|
||||
(declare-c update-texture [texture Texture2D pixels (Ptr u8)] "UpdateTexture")
|
||||
(declare-c update-texture-rec [texture Texture2D rec Rectangle pixels (Ptr u8)] "UpdateTextureRec")
|
||||
(declare-c gen-texture-mipmaps [texture (Ptr Texture2D)] "GenTextureMipmaps")
|
||||
(declare-c set-texture-filter [texture Texture2D filter i32] "SetTextureFilter")
|
||||
(declare-c set-texture-wrap [texture Texture2D wrap i32] "SetTextureWrap")
|
||||
(declare-c color-is-equal [col-1 Color col-2 Color] bool "ColorIsEqual")
|
||||
(declare-c color-to-int [color Color] i32 "ColorToInt")
|
||||
@ -247,11 +246,8 @@
|
||||
(declare-c draw-circle-3d [center Vector3 radius f32 rotation-axis Vector3 rotation-angle f32 color Color] "DrawCircle3D")
|
||||
(declare-c draw-triangle-3d [v-1 Vector3 v-2 Vector3 v-3 Vector3 color Color] "DrawTriangle3D")
|
||||
(declare-c draw-triangle-strip-3d [points (Ptr Vector3) point-count i32 color Color] "DrawTriangleStrip3D")
|
||||
(declare-c draw-cube-v [position Vector3 size Vector3 color Color] "DrawCubeV")
|
||||
(declare-c draw-cube-wires-v [position Vector3 size Vector3 color Color] "DrawCubeWiresV")
|
||||
(declare-c draw-sphere [center-pos Vector3 radius f32 color Color] "DrawSphere")
|
||||
(declare-c draw-sphere-ex [center-pos Vector3 radius f32 rings i32 slices i32 color Color] "DrawSphereEx")
|
||||
(declare-c draw-sphere-wires [center-pos Vector3 radius f32 rings i32 slices i32 color Color] "DrawSphereWires")
|
||||
(declare-c draw-cylinder [position Vector3 radius-top f32 radius-bottom f32 height f32 slices i32 color Color] "DrawCylinder")
|
||||
(declare-c draw-cylinder-ex [start-pos Vector3 end-pos Vector3 start-radius f32 end-radius f32 sides i32 color Color] "DrawCylinderEx")
|
||||
(declare-c draw-cylinder-wires [position Vector3 radius-top f32 radius-bottom f32 height f32 slices i32 color Color] "DrawCylinderWires")
|
||||
@ -259,10 +255,17 @@
|
||||
(declare-c draw-capsule [start-pos Vector3 end-pos Vector3 radius f32 slices i32 rings i32 color Color] "DrawCapsule")
|
||||
(declare-c draw-capsule-wires [start-pos Vector3 end-pos Vector3 radius f32 slices i32 rings i32 color Color] "DrawCapsuleWires")
|
||||
(declare-c draw-plane [center-pos Vector3 size Vector2 color Color] "DrawPlane")
|
||||
(declare-c draw-bounding-box [box BoundingBox color Color] "DrawBoundingBox")
|
||||
(declare-c draw-billboard [camera Camera3D texture Texture2D position Vector3 scale f32 tint Color] "DrawBillboard")
|
||||
(declare-c draw-billboard-rec [camera Camera3D texture Texture2D source Rectangle position Vector3 size Vector2 tint Color] "DrawBillboardRec")
|
||||
(declare-c draw-billboard-pro [camera Camera3D texture Texture2D source Rectangle position Vector3 up Vector3 size Vector2 origin Vector2 rotation f32 tint Color] "DrawBillboardPro")
|
||||
(declare-c check-collision-spheres [center-1 Vector3 radius-1 f32 center-2 Vector3 radius-2 f32] bool "CheckCollisionSpheres")
|
||||
(declare-c check-collision-boxes [box-1 BoundingBox box-2 BoundingBox] bool "CheckCollisionBoxes")
|
||||
(declare-c check-collision-box-sphere [box BoundingBox center Vector3 radius f32] bool "CheckCollisionBoxSphere")
|
||||
(declare-c get-ray-collision-sphere [ray Ray center Vector3 radius f32] RayCollision "GetRayCollisionSphere")
|
||||
(declare-c get-ray-collision-box [ray Ray box BoundingBox] RayCollision "GetRayCollisionBox")
|
||||
(declare-c get-ray-collision-triangle [ray Ray p-1 Vector3 p-2 Vector3 p-3 Vector3] RayCollision "GetRayCollisionTriangle")
|
||||
(declare-c get-ray-collision-quad [ray Ray p-1 Vector3 p-2 Vector3 p-3 Vector3 p-4 Vector3] RayCollision "GetRayCollisionQuad")
|
||||
(declare-c load-wave-from-memory [file-type string file-data (Ptr u8) data-size i32] Wave "LoadWaveFromMemory")
|
||||
(declare-c update-sound [sound Sound data (Ptr u8) sample-count i32] "UpdateSound")
|
||||
(declare-c export-wave-as-code [wave Wave file-name string] bool "ExportWaveAsCode")
|
||||
|
||||
31
vendor/raylib/headers
vendored
31
vendor/raylib/headers
vendored
@ -24,13 +24,19 @@
|
||||
# agrees with it by construction. That is also why the hand-written lines
|
||||
# were kept rather than replaced by generated ones.
|
||||
#
|
||||
# Why it is still optional. A build needs libraylib linkable and *not*
|
||||
# raylib-devel installed, which is a property worth keeping; requiring a header
|
||||
# would take it from everyone to give the check to whoever has one. Before the
|
||||
# bindings were committed this marker also decided how many bindings a build
|
||||
# got, which was the real cost of it being opt-in; it no longer decides that,
|
||||
# and all it now withholds is a check that regeneration has already run once.
|
||||
# Same shape as ${FLAN_RAYLIB_WEB} in `link`, and for the same reason.
|
||||
# Why it is no longer optional. It used to be `?${FLAN_RAYLIB_H}`, and the
|
||||
# argument was that a build needs libraylib linkable and *not* raylib-devel
|
||||
# installed, so requiring a header would take that property from everyone to
|
||||
# give the check to whoever had one. **That argument dissolved when the header
|
||||
# was committed here**: nobody needs raylib-devel to have it, because it ships
|
||||
# with the repository.
|
||||
#
|
||||
# What being optional actually cost was found the hard way. `vendor/raylib/web/`
|
||||
# is gitignored, so the header a `build-web.sh` tree happens to have never
|
||||
# appears in a git worktree — and every parallel lane that touched bindings was
|
||||
# therefore checking against nothing and not being told. One of them went
|
||||
# looking and used a copy out of `~/.local/share/Trash`, which happened to be
|
||||
# byte-identical. A check that silently does not run is worse than no check.
|
||||
#
|
||||
# The version must match the shared library `link` names — 5.5, libraylib.so.550.
|
||||
# Reading one version's header while linking another's library is exactly the
|
||||
@ -38,11 +44,10 @@
|
||||
# described: against a 5.1-dev header, regeneration reports ten differences
|
||||
# that are all real and writes nothing.
|
||||
#
|
||||
# export FLAN_RAYLIB_H=/path/to/raylib-5.5/src/raylib.h
|
||||
#
|
||||
# vendor/raylib/build-web.sh already clones that exact tag to build the browser
|
||||
# archive, so a tree that has built for web has the matching header at
|
||||
# vendor/raylib/web/raylib-5.5/src/raylib.h.
|
||||
# To check against a different header — a newer raylib, or a system one — edit
|
||||
# this line or replace the file. There is no environment variable for it any
|
||||
# more: the point of committing the header is that every tree checks against
|
||||
# the same one without anybody exporting anything.
|
||||
#
|
||||
# To see what regeneration would produce without writing anything:
|
||||
#
|
||||
@ -52,4 +57,4 @@
|
||||
# are called — is `bindings` beside this file. See its comments for why a
|
||||
# committed generated file needs a config at all.
|
||||
#
|
||||
?${FLAN_RAYLIB_H}
|
||||
raylib-5.5.h
|
||||
|
||||
1708
vendor/raylib/raylib-5.5.h
vendored
Normal file
1708
vendor/raylib/raylib-5.5.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
90
vendor/raylib/raylib.flan
vendored
90
vendor/raylib/raylib.flan
vendored
@ -153,11 +153,31 @@
|
||||
;; The cursor's visibility is window state and not input, but it is read and
|
||||
;; written by the same code that reads the mouse, so it sits here.
|
||||
;; hide-cursor only hides it; it does not lock it to the window, which is
|
||||
;; what raylib's separate DisableCursor does and which nothing here needs.
|
||||
;; what raylib's separate DisableCursor does. Those two — DisableCursor and
|
||||
;; EnableCursor — are in the generated half rather than here: they take no
|
||||
;; arguments at all, so there is no Flan face for a hand-written line to
|
||||
;; improve, which is the same rule that leaves GetMouseX there.
|
||||
;; examples/core-3d-picking.flan toggles them from the right mouse button.
|
||||
(declare-c show-cursor [] "ShowCursor")
|
||||
(declare-c hide-cursor [] "HideCursor")
|
||||
(declare-c cursor-hidden? [] bool "IsCursorHidden")
|
||||
|
||||
;; The shape the pointer takes. raylib's SetMouseCursor says `int` and means
|
||||
;; one of these eleven, so the Flan face is the enum for the same reason
|
||||
;; set-exit-key takes a Key: the call is made every frame from a hover test,
|
||||
;; and `(rl/set-mouse-cursor :ibeam)` is checked against the members where an
|
||||
;; i32 would take any number at all — including the one off-by-one that picks
|
||||
;; the arrow instead of the I-beam and looks like nothing at all went wrong.
|
||||
;;
|
||||
;; All eleven are here and not a subset, unlike Key: the enum is closed and
|
||||
;; eleven members is the whole of it.
|
||||
(defenum MouseCursor
|
||||
[default 0 arrow 1 ibeam 2 crosshair 3 pointing-hand 4
|
||||
resize-ew 5 resize-ns 6 resize-nwse 7 resize-nesw 8 resize-all 9
|
||||
not-allowed 10])
|
||||
|
||||
(declare-c set-mouse-cursor [cursor MouseCursor] "SetMouseCursor")
|
||||
|
||||
;; ── Colours ─────────────────────────────────────────────────────────
|
||||
;;
|
||||
;; A Color is four bytes in RGBA order, so it is *not* the little-endian
|
||||
@ -352,10 +372,57 @@
|
||||
[position Vector3 width f32 height f32 length f32 color Color]
|
||||
"DrawCubeWires")
|
||||
|
||||
(declare-c draw-cube-v [position Vector3 size Vector3 color Color] "DrawCubeV")
|
||||
|
||||
;; DrawSphere is DrawSphereEx with rings and slices fixed at 16; the wires
|
||||
;; form takes them because the wireframe is the only place the tessellation is
|
||||
;; visible. Both are here rather than generated for the reason above: they are
|
||||
;; inside a frame, in an example that ships in examples/.
|
||||
(declare-c draw-sphere
|
||||
[center Vector3 radius f32 color Color]
|
||||
"DrawSphere")
|
||||
(declare-c draw-sphere-wires
|
||||
[center Vector3 radius f32 rings i32 slices i32 color Color]
|
||||
"DrawSphereWires")
|
||||
|
||||
;; `slices` squares each way from the origin, `spacing` world units apart, on
|
||||
;; the XZ plane.
|
||||
(declare-c draw-grid [slices i32 spacing f32] "DrawGrid")
|
||||
|
||||
;; ── Rays and boxes ──────────────────────────────────────────────────
|
||||
;;
|
||||
;; Three small aggregates, added together because the picking call produces
|
||||
;; all three: a Ray goes in, a BoundingBox says what to test it against, and
|
||||
;; a RayCollision comes back. Every field is read off raylib.h 5.5 and every
|
||||
;; one of them is a Vector3 or a scalar — there is no owned memory anywhere
|
||||
;; here, which is exactly what separates these three from Model and Mesh, and
|
||||
;; why these could be described and those still cannot.
|
||||
;;
|
||||
;; What the layout check can and cannot say about them, on the same rule the
|
||||
;; Camera3D note states: BoundingBox's two Vector3s are the same type, so a
|
||||
;; permuted BoundingBox has the identical layout and nothing would catch it.
|
||||
;; RayCollision is the opposite and is the one worth having checked — `hit` is
|
||||
;; a C `bool`, one byte, and `distance` is a float, so the three padding bytes
|
||||
;; between them are a real claim about the struct that a permutation breaks.
|
||||
(defstruct BoundingBox [min Vector3 max Vector3])
|
||||
(defstruct Ray [position Vector3 direction Vector3])
|
||||
(defstruct RayCollision
|
||||
[hit bool distance f32 point Vector3 normal Vector3])
|
||||
|
||||
;; The inverse of get-world-to-screen above, and hand-written beside it for
|
||||
;; the reason stated there: the two read the same camera and the same window
|
||||
;; dimensions, so they belong to the same half of the file. raylib 5.5 keeps
|
||||
;; GetMouseRay as a #define onto this name; the define is not a symbol and
|
||||
;; there is nothing to bind it to.
|
||||
(declare-c get-screen-to-world-ray
|
||||
[position Vector2 camera Camera3D] Ray
|
||||
"GetScreenToWorldRay")
|
||||
|
||||
;; Per-frame and inside BeginMode3D, like the cube draws. raylib draws the
|
||||
;; ray as a line a thousand units long, so it is a debugging aid and not
|
||||
;; geometry with an end.
|
||||
(declare-c draw-ray [ray Ray color Color] "DrawRay")
|
||||
|
||||
;; ── Shapes ──────────────────────────────────────────────────────────
|
||||
;;
|
||||
;; Rectangle intersection, which raylib computes from all four fields in
|
||||
@ -459,6 +526,27 @@
|
||||
|
||||
(declare-c unload-texture [texture Texture2D] "UnloadTexture")
|
||||
|
||||
;; How a texture is sampled when it is drawn at anything other than its own
|
||||
;; size. The header says `int` on SetTextureFilter and means one of these six.
|
||||
;;
|
||||
;; The value of the enum face here is not a typo caught at the call site so
|
||||
;; much as a *readable* one: the fog-of-war example renders its fog into a
|
||||
;; 25x15 render texture and scales it to 800x450, and the entire visual point
|
||||
;; of the example is that `:bilinear` smooths the tile edges where the
|
||||
;; default `:point` would show 32-pixel squares. A bare `1` in that call says
|
||||
;; nothing; the member name says the whole thing.
|
||||
;;
|
||||
;; TEXTURE_FILTER_ANISOTROPIC_* are the mipmapped modes and need a texture
|
||||
;; with mipmaps generated, which nothing here makes — they are listed because
|
||||
;; the enum is closed, not because anything calls them.
|
||||
(defenum TextureFilter
|
||||
[point 0 bilinear 1 trilinear 2
|
||||
anisotropic-4x 3 anisotropic-8x 4 anisotropic-16x 5])
|
||||
|
||||
(declare-c set-texture-filter
|
||||
[texture Texture2D filter TextureFilter]
|
||||
"SetTextureFilter")
|
||||
|
||||
(declare-c draw-texture
|
||||
[texture Texture2D x i32 y i32 tint Color]
|
||||
"DrawTexture")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user