A pseudo-3D sprite-scaling toolkit for Odin + raylib.
Build 2D games that look first-person: objects carry a depth (z) and scale with
distance from the camera, like arcade "Super Scaler" games (Space Harrier, Outrun).
- Getting started — from an empty window to a small game.
- Concepts — coordinate system, projection, frame order, trade-offs.
- Examples index — what each demo teaches, at a glance.
- DESIGN.md — architecture, math, API conventions, and the roadmap.
- Each module's header comment in
f3d/is its API reference.
New project? Copy examples/template to start.
- 0.1 — core projection + primitive billboards. Camera + projection math, rect and circle drawing, and a depth-sorted scene helper.
- 0.2 — parallax backgrounds. Tiled, horizontally-scrolling layers that react to camera strafe and yaw.
- 0.3 — free-turning camera + culling. Move relative to facing, turn, adjust FOV, and skip off-screen objects via a public culling API.
- 0.4 — collision & spatial helpers. World-space AABB/Sphere overlap + resolution, ground-plane (xz) tests, and nearest / within-radius / within-depth queries.
- 0.5 — mode-7 ground plane. Perspective textured floor driven by a
Ground_Samplerinterface, with built-in checker/texture samplers and easy custom ones. - 0.6 — textured sprites & animation.
Sprite_Shape(depth-sorts with primitives), spritesheets, frame animation clips, and optional entityupdate/drawhooks. - 0.7 — atmosphere & camera FX. Distance fog (depth cueing, tames horizon shimmer),
trauma-based camera shake, and steering roll via a raylib
Camera2Dtransform. - 0.8 — examples & docs. Starter template, getting-started walkthrough, a concepts guide, and an examples index.
- 0.9 — stabilize & optimize. API-freeze pass,
VERSIONconstant, a headless test suite, and a faster depth sort (yaw trig computed once per sort, not per comparison). - 1.0 — stable release. API frozen; changes follow semver from here. MIT licensed.
Current version: 1.0.0 — see CHANGELOG.md.
f3d/ the library (package f3d)
core.odin Camera, project_point, world_to_screen, screen_to_ground
camera.odin camera_move / camera_turn / camera_fov / camera_set_fov
draw.odin draw_rect, draw_circle (auto side-culling)
scene.odin Body/Shape, draw_scene (+ order-preserving variants)
cull.odin point_in_view / sphere_in_view / body_in_view
collision.odin AABB/Sphere overlap + resolve, xz ground helpers
spatial.odin nearest_index / within_radius / within_depth
parallax.odin Parallax_Layer, draw_parallax
ground.odin Ground, draw_ground, Ground_Sampler (+ checker/texture)
sprite.odin Sprite_Shape, draw_sprite, Spritesheet, Anim
entity.odin Entity update/draw hooks
fx.odin Fog + fog_apply, Shake + fx_camera
examples/
template/ copy-to-start skeleton (camera, bodies, draw loop)
01_primitives/ a field of scaling rects & circles; arrow keys to move
02_parallax/ background layers + free-roam (arrows move/turn, A/D strafe)
03_free_camera/ turning + live FOV + on-screen cull count
04_collision/ solid pillars you can't pass + collectible gems
05_ground/ mode-7 floor; TAB toggles checker vs custom road sampler
06_sprites/ animated coin spritesheet + entity hooks; collect coins
07_atmosphere/ foggy racer game: WASD drive, dodge debris, cockpit HUD
From the repo root:
odin run examples/01_primitives
odin run examples/02_parallax
odin run examples/03_free_camera
odin run examples/04_collision
odin run examples/05_ground
odin run examples/06_sprites
odin run examples/07_atmosphereThe projection, sorting, and collision math have headless unit tests:
odin test f3dimport f3d "path/to/f3d"
cam := f3d.camera_make({960, 540}, height = 40, focal_length = 420)
cam.horizon_y = 200
// low-level: draw one primitive at a world position
f3d.draw_rect(cam, {0, 0, 300}, {45, 90}, rl.RED)
// or bundle objects and let f3d depth-sort them
bodies := []f3d.Body{
{pos = {0, 0, 300}, color = rl.RED, anchor = {0.5, 1}, shape = f3d.Rect_Shape{size = {45, 90}}},
{pos = {80, 25, 500}, color = rl.LIME, anchor = {0.5, 0.5}, shape = f3d.Circle_Shape{radius = 28}},
}
f3d.draw_scene(cam, bodies) // sorts far→near in place, then drawsf3d is a plain Odin package with no dependencies beyond vendor:raylib (which ships
with Odin). There's no package manager step — you make the f3d/ folder reachable and
import it. Two common ways:
1. Vendor the folder — copy f3d/ into your project (or add this repo as a git
submodule) and import it by relative path:
import f3d "libs/f3d" // path relative to the importing .odin file2. Use an Odin collection — point a collection at wherever f3d lives and import by collection name:
odin build src -collection:shared=/path/to/parent-of-f3dimport f3d "shared:f3d"Then follow Getting started, or copy
examples/template as a starting point.
MIT — see LICENSE.