flan/examples/core-world-screen.fln

92 lines
4.7 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.fln 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.fln, as in the other
;;;; ports, because Flan has no TextFormat and i64->bytes has no field width.
import rl "vendor:raylib"
import d "digits.fln"
const screen-width = 800
const screen-height = 450
once camera: rl/Camera3D
once cube: rl/Vector3
fn 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.
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}
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 cube.x, .y cube.y + 2.5, .z cube.z},
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(label-pos.x) - rl/measure-text("Enemy: 100/100", 20) / 2,
i32(label-pos.y), 20, rl/black)
let x = 10
rl/draw-text("Cube position in screen space coordinates: [", x, 10, 20,
rl/lime)
x += rl/measure-text("Cube position in screen space coordinates: [", 20)
x += d/draw-int(i32(label-pos.x), x, 10, 20, rl/lime)
rl/draw-text(", ", x, 10, 20, rl/lime)
x += rl/measure-text(", ", 20)
x += d/draw-int(i32(label-pos.y), 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)