Skip to content

Add the models namespace, port basic-voxel (209 → 233 bindings) - #8

Merged
burinc merged 3 commits into
mainfrom
arc/models-namespace
Aug 23, 2026
Merged

Add the models namespace, port basic-voxel (209 → 233 bindings)#8
burinc merged 3 commits into
mainfrom
arc/models-namespace

Conversation

@burinc

@burinc burinc commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Phase 3's biggest item. Adds raylib.models24 new bindings (209 → 233)
plus the five structs they need — and ports basic-voxel as the end-to-end
proof. 106 → 107.

This is the largest single addition to the binding surface so far, and it
opens the models examples, which are the biggest unported group (21 of them).

The part that matters

The Model struct changed between raylib 5.5 and 6.0. 5.5 ended with
boneCount/bones*/bindPose*; 6.0 replaces those with a nested
ModelSkeleton, a currentPose pointer and a boneMatrices pointer.

Model is passed by value to every draw call, so a wrong layout doesn't
error — it reads garbage. Binding this before the 6.0 upgrade would have
produced code that silently broke on it. Worth noting the 6.0 upgrade turned
out to be a prerequisite for this work rather than a parallel nicety.

Layouts verified two ways. By size against hand-computed offsets (mesh
120, material 40, material-map 28, model-skeleton 24, model 136), then against
real raylib data — which is the check that actually proves the offsets:

  • a generated 2×2×2 cube reports 24 vertices, 12 triangles, nonzero VAO
  • the model it loads into reports meshCount 1, materialCount 1, and validates
  • its bounding box is exactly −1..+1
  • its transform reads as an exact identity matrix — 16 floats can only all
    land correctly if every preceding offset is right

Pointer fields are real ::mem/pointers with explicit padding rather than the
split lo/hi ints used for Shader. coffi doesn't insert alignment padding and
fails loudly at alias definition if a pointer is misaligned, so every :_pad
is load-bearing.

set-model-material-color!

raylib has no function for this — its own examples reach into
model.materials[i].maps[j].color directly, so this walks the same pointers.
It multiplies with the tint passed to draw-model! rather than replacing
it, which is why basic-voxel sets both to BEIGE: setting only the tint
renders the cubes noticeably lighter than the C does.

Struct moves

Matrix is new in raylib.structs. BoundingBox and Camera3D move there
from core/collision and core/camera3d, so raylib.models doesn't depend
on those namespaces for types it needs by value. core/camera3d keeps a local
alias so existing ::camera3d references still resolve, and all nine
existing models examples were re-run
to confirm no regression.

basic-voxel

pick-voxel takes its ray/box test as an argument rather than calling raylib,
so the nearest-hit rule is checkable without a window: nearest of four wins,
no hits gives nil rather than removing something arbitrary, an empty world
gives nil rather than erroring, a tie returns a set member.

Gate: 137 namespaces, 0 lint errors, 69 warnings.

What this unlocks next

Three examples are now fully clear, and several need only a small tier more:

next step unlocks
LoadModelAnimations / UpdateModelAnimation / UnloadModelAnimations 5 examples
LoadImage / LoadTextureFromImage / UnloadImage + GenMeshCubicmap/GenMeshHeightmap 2 examples
GetRayCollisionMesh mesh_picking

One decision for you. billboard_rendering is fully unblocked and its
asset (billboard.png) is CC0. directional_billboard is also unblocked but
needs skillbot.png, which — like patterns.png — has no licence entry
upstream
. I've held both rather than add a second unattributed asset while
the first is still an open question.

burinc added 3 commits August 23, 2026 17:34
Phase 3's biggest item: 3D model loading, generation and drawing. 24 new
bindings taking the project from 209 to 233, plus the five structs they need.

THE PART THAT MATTERS. The Model struct CHANGED between raylib 5.5 and 6.0 -
5.5 ended with boneCount/bones*/bindPose*, while 6.0 replaces those with a
nested ModelSkeleton, a currentPose pointer and a boneMatrices pointer. Model
is passed BY VALUE to every draw call, so a wrong layout does not error, it
reads garbage. Binding this before the 6.0 upgrade would have produced code
that silently broke on it.

Layouts verified two ways. By size against hand-computed offsets: mesh 120,
material 40, material-map 28, model-skeleton 24, model 136. Then against real
raylib data, which is the check that actually proves the offsets: a generated
2x2x2 cube reports 24 vertices and 12 triangles with a nonzero VAO, the model
it loads into reports meshCount 1 and materialCount 1 and validates, its
bounding box is exactly -1..+1, and its transform reads as an exact identity
matrix. That last one is the strongest evidence available - 16 floats can only
all land correctly if every preceding offset is right.

Pointer fields are real ::mem/pointers with explicit padding rather than the
split lo/hi ints used for Shader. coffi does not insert alignment padding
itself and fails loudly at alias definition if a pointer is misaligned, so
every :_pad is load-bearing.

set-model-material-color! walks into memory the model owns, because raylib
exposes no function for it and its own examples reach into
model.materials[i].maps[j].color directly. Worth knowing it MULTIPLIES with
the tint passed to draw-model! rather than replacing it.

Two structs move to raylib.structs so models does not have to depend on
core.collision and core.camera3d for types it needs by value: bounding-box
and camera-3d. camera3d keeps a local alias so existing ::camera3d references
still resolve, and all nine existing models examples were re-run to confirm
no regression.
An 8x8x8 block of cubes walked in first person, with left-click removing the
one under the crosshair. First example on raylib.models, and the end-to-end
proof of it: Model is passed by value to 512 draw calls a frame.

pick-voxel takes its ray/box test as an argument rather than calling raylib
directly, so the nearest-hit rule is checkable without a window. Verified:
the nearest of four candidates wins, no hits gives nil rather than removing
something arbitrary, an empty world gives nil rather than erroring, and a tie
returns a member of the set.

The material colour is set as well as the tint, both BEIGE, because raylib
multiplies them - setting only the tint renders the cubes noticeably lighter
than the C does.
Regenerated against main after PR #7 landed, rather than merged hunk by hunk.
Both branches were cut from the same commit and both bumped example counts in
the same thirteen files, so git had "106 -> 107" against "106 -> 109" with no
way to pick. Counts are re-derived from the registry and bb check either way,
so regenerating is both simpler and less error-prone than resolving.

110 examples (22 models now), 140 namespaces, 99 of 110 starting an embedded
nREPL. Adds raylib.models to the module layout in the architecture guide.

Validated after the rebase: catalog rows, gallery entries, GIFs and registry
all agree at 110, every registry alias is in the catalog, no broken preview
links or anchors, and each section header matches its own row count. The four
examples that collided across the two branches were re-run together.

The demo leans on mouse LOOK rather than clicks: pointer moves land reliably
where button presses do not, so first-person motion carries the preview and
the single click is opportunistic. 30 frames.
@burinc
burinc force-pushed the arc/models-namespace branch from 42fc8c1 to 5d0997f Compare August 23, 2026 07:38
@burinc
burinc merged commit 108b94b into main Aug 23, 2026
1 check passed
@burinc
burinc deleted the arc/models-namespace branch August 23, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant