Skip to content

Sprites

Sprites are runtime render entities built from active bonus pickup state. They are not part of the mandatory world renderer; they extend the bonus path by projecting collectible entities into the first-person view.

Bonus sprites

Pickup-backed sprites projected after wall raycasting

Sprites are rebuilt from active pickups, sorted by distance, projected through the camera basis, then clipped against the wall z-buffer produced during DDA rendering.

01
t_bonus_sprites

Structure-of-arrays storage keeps per-frame scans simple.

xs[] / ys[] positionstypes[] semantic pickup typeactive[] render flagdists[] sort key cachezbuf[] wall depth per columnpickup textures and anim frames
02
render pipeline

World walls feed depth before sprites are drawn.

background walls + zbuf sprites minimap HUD last
01
bonus_sprites_rebuild(app)

active pickups become renderable sprite entries

              sprites.xs[i] = pickup.x
sprites.ys[i] = pickup.y
sprites.types[i] = pickup.type
sprites.active[i] = 1
            
02
bonus_sprites_set_depth(app, x, d)

raycasting feeds one wall depth per screen column

              zbuf[x] = ray.perp_dist

if (sprite_depth >= zbuf[x])
  skip column;
            
03
sort_bonus_sprites(...)

far-to-near order keeps painter rendering stable

              dists[i] = (px - xs[i])^2
         + (py - ys[i])^2

sort descending by dists[]
            
04
bonus_setup_sprite_projection(...)

camera transform converts world points into screen windows

              inv_det = 1 / (plane_x*dir_y - dir_x*plane_y)
tx = inv_det * (dir_y*dx - dir_x*dy)
sx = W/2 * (1 + transform_x / tx)
sprite_h = abs(W / tx)
            
z-buffer and occlusion visualizer

Switch between 3D projection, top-down sorting, and per-column z-buffer inspection.

Sprites projected with z-buffer occlusion.

render invariants

After sprite drawing, world visibility remains coherent.

only active pickups renderzbuf hides sprites behind wallsfar-to-near order is stabletexture bounds stay clamped

Sprite-capable map symbols are defined by BONUS_SPRITE_SET and currently include:

  • * for health pickup
  • @ for ammo pickup
  • ) for armor pickup
  • / for score or animated pickup

Parsing and validation accept these symbols through bonus map rules. Pickup discovery then builds runtime entities, and the sprite subsystem rebuilds its render arrays from active pickups.

The sprite subsystem stores data in structure-of-arrays form:

  • xs[], ys[] for positions
  • types[] for semantic type
  • active[] for render eligibility
  • dists[] for sort keys
  • zbuf[] for wall depth per screen column
  • texture slots for pickup assets and animation frames

Sprite rendering is integrated after wall raycasting because wall distances are required for occlusion.

The runtime sequence is:

  1. raycasting stores ray.perp_dist into zbuf[x]
  2. active sprite arrays already reflect the current pickup state
  3. sprite positions are shifted into player-relative space
  4. sprite distances are computed and sorted far-to-near
  5. each sprite is projected into screen coordinates
  6. each stripe is skipped when sprite_depth >= zbuf[x]
  7. only opaque texels are written into the frame buffer

After sprite rendering:

  • only active pickups are renderable sprites
  • collected pickups are no longer drawn
  • sprites behind walls are hidden by the z-buffer
  • far-to-near ordering is respected
  • temporary player-relative coordinate shifts are restored before the frame ends
  • texture sampling remains clamped to valid bounds
  • include/structs_bonus.h
  • srcs_bonus/sprites/sprites_api.c
  • srcs_bonus/sprites/sprites_alloc.c
  • srcs_bonus/sprites/sprites_render.c
  • srcs_bonus/sprites/sprites_setup.c
  • srcs_bonus/sprites/sprites_sort.c
  • srcs_bonus/sprites/sprites_depth.c
  • srcs_bonus/sprites/sprites_space.c
  • srcs_bonus/sprites/sprites_shutdown.c