112 lines
5.8 KiB
Plaintext
112 lines
5.8 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"
|
|
|
|
const screen-width = 800
|
|
const 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`.
|
|
const num-textures = 9
|
|
|
|
once textures: [9 rl/Texture2D]
|
|
once 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.
|
|
fn upload(img: rl/Image) -> rl/Texture2D
|
|
let t = rl/load-texture-from-image(img)
|
|
rl/unload-image(img)
|
|
t
|
|
|
|
fn main() -> ()
|
|
rl/init-window(screen-width, screen-height,
|
|
"raylib [textures] example - procedural images generation")
|
|
defer rl/close-window()
|
|
textures = array(num-textures, rl/Texture2D)
|
|
;; 0 degrees runs the gradient top to bottom, 90 left to right, 45 corner
|
|
;; to corner.
|
|
textures[0] = upload(rl/gen-image-gradient-linear(screen-width, screen-height, 0, rl/red, rl/blue))
|
|
textures[1] = upload(rl/gen-image-gradient-linear(screen-width, screen-height, 90, rl/red, rl/blue))
|
|
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.
|
|
textures[3] = upload(rl/gen-image-gradient-radial(screen-width, screen-height, 0.0, rl/white, rl/black))
|
|
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.
|
|
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.
|
|
textures[6] = upload(rl/gen-image-white-noise(screen-width, screen-height, 0.5))
|
|
textures[7] = upload(rl/gen-image-perlin-noise(screen-width, screen-height, 50, 50, 4.0))
|
|
;; `tile-size` is the cell spacing, in pixels.
|
|
textures[8] = upload(rl/gen-image-cellular(screen-width, screen-height, 32))
|
|
defer dotimes([i num-textures], rl/unload-texture(textures[i]))
|
|
current-texture = 0
|
|
rl/set-target-fps(60)
|
|
until rl/window-should-close()
|
|
;; Update
|
|
if rl/is-mouse-button-pressed(:mouse-left) or rl/is-key-pressed(:key-right)
|
|
current-texture = (current-texture + 1) % num-textures
|
|
;; Draw
|
|
rl/with-drawing:
|
|
rl/clear-background(rl/raywhite)
|
|
rl/draw-texture(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.
|
|
if current-texture == 0
|
|
rl/draw-text("VERTICAL GRADIENT", 560, 10, 20, rl/raywhite)
|
|
elif current-texture == 1
|
|
rl/draw-text("HORIZONTAL GRADIENT", 540, 10, 20, rl/raywhite)
|
|
elif current-texture == 2
|
|
rl/draw-text("DIAGONAL GRADIENT", 540, 10, 20, rl/raywhite)
|
|
elif current-texture == 3
|
|
rl/draw-text("RADIAL GRADIENT", 580, 10, 20, rl/lightgray)
|
|
elif current-texture == 4
|
|
rl/draw-text("SQUARE GRADIENT", 580, 10, 20, rl/lightgray)
|
|
elif current-texture == 5
|
|
rl/draw-text("CHECKED", 680, 10, 20, rl/raywhite)
|
|
elif current-texture == 6
|
|
rl/draw-text("WHITE NOISE", 640, 10, 20, rl/red)
|
|
elif current-texture == 7
|
|
rl/draw-text("PERLIN NOISE", 640, 10, 20, rl/red)
|
|
else
|
|
rl/draw-text("CELLULAR", 670, 10, 20, rl/raywhite)
|