;;;; 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 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 :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) (defvar camera rl/Camera3D) (defvar 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 :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) :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/begin-drawing) (rl/clear-background rl/raywhite) (rl/begin-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) (rl/end-mode-3d) ;; 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) (rl/end-drawing))))