flan/examples/core-world-screen.flan
Joseph Ferano 52e9c92b0f The enum prefix goes uniform across all eleven
The author's ruling: "I think the prefix reads better, keep it" — so two
prefixed enums out of eleven was the inconsistency, not the prefix.
TraceLogLevel takes log-, CameraProjection projection-, CameraMode camera-,
GamepadButton button-, GamepadAxis axis-, Gesture gesture-, MouseCursor
cursor-, TextureFilter filter-, PixelFormat pixel-.

Two of those are judgement. CameraProjection and CameraMode share raylib's
CAMERA_ and deliberately do not share a Flan prefix: they are two questions
asked of the same struct, and :projection-perspective beside :camera-orbital
says which is being answered. GamepadButton and GamepadAxis take the short
stems rather than a shared gamepad-, which keeps :button-left-face-up and
:axis-left-trigger readable.

It is a reading choice and not a collision fix, and bindings, raylib.flan and
docs/BUILT.md all say so: a keyword resolves against the expected type and
nothing else, so :point at a TextureFilter site was never ambiguous. What the
prefix buys is the call site read on its own.

The three constant exception lines are keyed on the member's full Flan
spelling and moved with it. flan generate-c vendor/raylib is green against
raylib-5.5.h, and the check was confirmed non-vacuous by breaking it:
filter-trilinearr reported TEXTURE_FILTER_TRILINEARR rather than passing.
test_flan pins one member of each of the eleven to the C name the rule
reaches, read out of the real bindings file.

sand.flan line 121 is (rl/set-trace-log-level :warning) and is the author's
to respell. TraceLogLevel carries a warning alias beside log-warning, mapped
by name in bindings, so the suite stays green until he does; FIX.org has the
three-edit removal recipe.
2026-09-21 07:59:48 +07:00

112 lines
5.1 KiB
Plaintext

;;;; raylib [core] example - world to screen
;;;;
;;;; examples/core/core_world_screen.c, and the first thing in this directory
;;;; to draw in 3D at all. That is why it is here: before it, vendor/raylib
;;;; described no Vector3, no Camera3D and not one call that takes either, so
;;;; the importer refused every 3D function in raylib.h by name — "Vector3 is
;;;; a struct the package does not describe". Two defstructs later it refuses
;;;; none of them, and the generated half grew the cubes, spheres, cylinders
;;;; and billboards along with the eight lines this example needs.
;;;;
;;;; Hand-written in raylib.flan rather than generated, and why each:
;;;;
;;;; begin-mode-3d / end-mode-3d a begin/end pair inside a frame, the
;;;; class docs/PORTING.md §1 names, and taken
;;;; together the way begin-mode-2d is
;;;; draw-cube / draw-cube-wires / draw-grid drawn every frame
;;;; update-camera the Flan face differs: (Ptr Camera3D),
;;;; because it mutates, and a CameraMode
;;;; rather than the header's int
;;;; get-world-to-screen it reads the same camera as the mode
;;;;
;;;; What is NOT pinned by anything, and cannot be: Camera3D's first three
;;;; fields are all Vector3, so every permutation of position, target and up
;;;; has the identical layout and no computed test can tell them apart. A
;;;; camera looking from the wrong place is a picture, not a number. The same
;;;; is true of Vector3's own x, y and z. Only the screen catches those.
;;;;
;;;; update-camera in :camera-third-person mode also takes the mouse, which
;;;; is why the C calls DisableCursor: the cursor is locked to the window and
;;;; its movement becomes camera rotation rather than a pointer. disable-cursor
;;;; comes from the generated half — it is called once, before the loop.
;;;;
;;;; The C's two TextFormats go through examples/digits.flan, as in the other
;;;; ports, because Flan has no TextFormat and i64->bytes has no field width.
(import rl "vendor:raylib")
(import d "digits.flan")
(defconst screen-width 800)
(defconst screen-height 450)
(defonce camera rl/Camera3D)
(defonce cube rl/Vector3)
(defn main [] ()
(rl/init-window screen-width screen-height
"raylib [core] example - core world screen")
(defer (rl/close-window))
;; `projection` is a CameraProjection, so the keyword resolves against the
;; enum's members right here and a misspelt projection is a compile error
;; rather than a 0. raylib's field is `int` and the layout is unchanged —
;; an enum is four bytes either way.
(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 :projection-perspective}))
(set cube (rl/Vector3 {.x 0.0 .y 0.0 .z 0.0}))
;; The mouse becomes the camera's, not a pointer's.
(rl/disable-cursor)
(rl/set-target-fps 60)
(until (rl/window-should-close?)
;; Update. The camera is passed by address because update-camera writes
;; to it — a global is an assignable place, so it has an address to take.
(rl/update-camera (addr camera) :camera-third-person)
;; Where the point 2.5 units above the cube lands on the screen. This is
;; the whole example: the same transform raylib is about to draw with,
;; asked in advance, so a label can be put on a thing in the world.
(let [label-pos (rl/get-world-to-screen
(rl/Vector3 {.x (.x cube)
.y (+ (.y cube) 2.5)
.z (.z cube)})
camera)]
;; Draw
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/with-mode-3d camera
(rl/draw-cube cube 2.0 2.0 2.0 rl/red)
(rl/draw-cube-wires cube 2.0 2.0 2.0 rl/maroon)
(rl/draw-grid 10 1.0))
;; And the 2D half, drawn after end-mode-3d and therefore on top of the
;; cube whatever the depth buffer says — which is the second half of
;; what the example demonstrates.
(rl/draw-text "Enemy: 100 / 100"
(- (i32 (.x label-pos))
(/ (rl/measure-text "Enemy: 100/100" 20) 2))
(i32 (.y label-pos)) 20 rl/black)
(let [x 10]
(rl/draw-text "Cube position in screen space coordinates: [" x 10 20
rl/lime)
(set x (+ x (rl/measure-text
"Cube position in screen space coordinates: [" 20)))
(set x (+ x (d/draw-int (i32 (.x label-pos)) x 10 20 rl/lime)))
(rl/draw-text ", " x 10 20 rl/lime)
(set x (+ x (rl/measure-text ", " 20)))
(set x (+ x (d/draw-int (i32 (.y label-pos)) x 10 20 rl/lime)))
(rl/draw-text "]" x 10 20 rl/lime))
(rl/draw-text "Text 2d should be always on top of the cube" 10 40 20
rl/gray)))))