Three shapes, two text, three textures, one models and one core, picked for binding surface rather than for how they look. shapes-basic-shapes brings in six draw families nothing had called — the circle and rectangle gradients, the triangles, all three poly draws — and is the first call in the corpus to pass two Colors or three Vector2s at once. shapes-collision-area is get-collision-rec, the only binding that takes two Rectangles and answers a third, on a frame path. shapes-following-eyes is the raymath gap measured rather than worked around: every line of it is vector arithmetic written without a vector library, the way the C writes it. text-input-box drains get-char-pressed's queue, which no example had read, and needed a MouseCursor defenum for set-mouse-cursor. text-writing-anim replaces TextSubtext — unbindable, it answers a pointer into a rotating static buffer — with (string (slice b 0 n)), which is the same operation without the shared state. textures-image-generation runs nine Gen* calls and the gen/upload/unload-image path, all procedural, no file on disk. textures-fog-of-war needed a TextureFilter defenum: the smooth fog edge is entirely :bilinear on a 25x15 render texture, and it is also the first draw-texture-pro with a negative source height. textures-mouse-painting is the same render texture used as a document rather than as scratch, plus the round trip back off the GPU — load-image-from-texture, image-flip-vertical, export-image — which nothing had run. models-box-collisions is the counterexample to "a models example is a binding exercise": nothing in it is a Model, and one BoundingBox defstruct un-refuses four functions. core-3d-picking is the only caller anywhere for Ray and RayCollision, and picking is the inverse of the get-world-to-screen the corpus already had. Added to vendor/raylib: defstructs BoundingBox, Ray and RayCollision; defenums MouseCursor and TextureFilter with their mapping lines in bindings; hand-written declare-c for SetMouseCursor, SetTextureFilter, DrawCubeV, DrawSphere, DrawSphereWires, DrawRay and GetScreenToWorldRay, each excluded from the generated half on the rule bindings already states. generated.flan regenerated against raylib 5.5: 272 declarations, 117 refused, every defstruct, hand-written declare-c and mapped constant agreeing with the header.
131 lines
6.1 KiB
Plaintext
131 lines
6.1 KiB
Plaintext
;;;; 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)))
|