Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_iso_engine.c File Reference

Isometric/axonometric tile engine with height maps, terrain transitions, and A* pathfinding integration. More...

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "nilorea/n_astar.h"
#include "nilorea/n_iso_engine.h"
#include <fcntl.h>
#include <unistd.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
Include dependency graph for n_iso_engine.c:

Go to the source code of this file.

Functions

static int _cmp_draw_entry (const void *a, const void *b)
static ALLEGRO_COLOR _compute_tile_tint (int seg_top, int max_height, float intensity, float ambient_r, float ambient_g, float ambient_b, float dyn_r, float dyn_g, float dyn_b)
static void _draw_height_borders (const ISO_MAP *map, int mx, int my, int seg_idx, int seg_bottom, int seg_top, int has_underside_face, const float vx[4], const float vy[4], float lift)
 Draw black border outlines on the diamond edges of an elevated tile.
static void _draw_segment_sides (const ISO_MAP *map, float east_x, float east_y, float south_x, float south_y, float west_x, float west_y, int mx, int my, int seg_bottom, int seg_top, float lift, float height_brightness, int wall_terrain_override)
 Draw cliff side faces for a single segment {seg_bottom..seg_top}.
static void _draw_segment_underside (ALLEGRO_BITMAP *bmp, float base_dx, float base_dy, float phw, float phh, float tile_draw_w, float tile_draw_h, int tile_w, int tile_h)
 Draw the underside face of a floating segment at 65% brightness.
static void _draw_tile_warped (ALLEGRO_BITMAP *bmp, const float vx[4], const float vy[4], int tile_w, int tile_h, ALLEGRO_COLOR tint)
 Draw a tile bitmap warped to 4 pre-computed screen vertices.
static void _iso_corner_to_screen (float hw, float hh, float tl, int cx, int cy, float fh, float cam_px, float cam_py, float zoom, float *sx, float *sy)
 Project a tile corner to screen coordinates (canonical formula).
static FILE * _iso_fopen_write (const char *path, const char *mode)
static int _iso_object_check_occlusion (const ISO_MAP *map, float obj_fx, float obj_fy, float obj_fz, float obj_sx, float obj_sy, float sprite_h, float sprite_half_w, float cam_px, float cam_py, float zoom, float *out_clip_y)
 Check if an object is occluded by tiles drawn in front of it.
static float _iso_preset_angle (int preset)
 Get the angle in degrees for a projection preset.
static int _neighbor_height_at (const ISO_MAP *map, int mx, int my)
static int _neighbor_should_transition (const ISO_MAP *map, int mx1, int my1, int mx2, int my2)
static int _neighbor_terrain_at (const ISO_MAP *map, int mx, int my)
 Edge-aware terrain lookup for chunked rendering.
static float _smooth_neighbor_h (const ISO_MAP *map, float h_self, int nx, int ny)
 Helper: clamp neighbor height for smooth slope rendering.
static int _tile_covers_height (const ISO_MAP *map, int mx, int my, int z)
 Check if a tile has geometry covering height level z.
void iso_apply_line_jitter_compensation (float *out_dx, float *out_dy)
ALLEGRO_COLOR iso_apply_primitive_color_filter (ALLEGRO_COLOR c)
void iso_corner_to_screen (const ISO_MAP *map, int cx, int cy, float fh, float cam_px, float cam_py, float zoom, float *sx, float *sy)
 Project a tile corner to screen coordinates (canonical formula).
float iso_diamond_dist (int px, int py, int tile_w, int tile_h)
 Distance from pixel to the diamond edge.
void iso_generate_transition_masks (ALLEGRO_BITMAP **masks, int tile_w, int tile_h)
 Generate the 32 procedural transition alpha masks (16 edge + 16 corner).
void iso_generate_transition_tiles (ALLEGRO_BITMAP ***tiles, ALLEGRO_BITMAP **masks, ALLEGRO_BITMAP **tile_bitmaps, int num_terrains, int tile_w, int tile_h)
 Pre-composite transition tiles (terrain texture * alpha mask).
int iso_is_in_diamond (int px, int py, int tile_w, int tile_h)
 Test if pixel (px,py) is inside the isometric diamond.
int iso_map_build_draw_order (const ISO_MAP *map, ISO_DRAW_ENTRY *out, int max_entries)
 Build the draw order for segment-sorted rendering.
void iso_map_calc_transitions (const ISO_MAP *map, int mx, int my, int *edge_bits, int *corner_bits)
 Compute terrain transition bitmasks for a cell (Article 934).
void iso_map_calc_transitions_full (const ISO_MAP *map, int mx, int my, int *edge_bits, int *corner_bits)
 Compute per-terrain transition bitmasks with height filtering.
void iso_map_corner_heights (const ISO_MAP *map, int mx, int my, float *h_n, float *h_e, float *h_s, float *h_w)
 Compute average corner heights for smooth tile rendering (Article 2026).
void iso_map_draw (const ISO_MAP *map, ALLEGRO_BITMAP **tile_bitmaps, ALLEGRO_BITMAP ***transition_tiles, int num_masks, ALLEGRO_BITMAP **overlay_bitmaps, int num_overlay_tiles, float cam_px, float cam_py, float zoom, int screen_w, int screen_h, int player_mode, N_ISO_OBJECT *objects, int num_objects)
 Draw the full ISO_MAP with segment-sorted rendering.
void iso_map_free (ISO_MAP **map_ptr)
 Free an ISO_MAP and set the pointer to NULL.
int iso_map_get_ability (const ISO_MAP *map, int mx, int my)
 Get the ability at a cell.
int iso_map_get_height (const ISO_MAP *map, int mx, int my)
 Get the height at a cell.
const ISO_TILE_SEGMENTS * iso_map_get_segments (const ISO_MAP *map, int mx, int my)
 get per-tile segments.
int iso_map_get_terrain (const ISO_MAP *map, int mx, int my)
 Get the terrain type at a cell.
ISO_VISIBLE_EDGES iso_map_get_visible_edges (const ISO_MAP *map, int mx, int my)
 Compute which diamond edges of tile (mx,my) need a height border.
ISO_VISIBLE_EDGES iso_map_get_visible_edges_segment (const ISO_MAP *map, int mx, int my, int seg_idx)
 Per-segment edge visibility with adjacency + occlusion.
float iso_map_interpolate_height (const ISO_MAP *map, float fx, float fy)
 Bilinear height interpolation at fractional map coordinates.
void iso_map_lerp_projection (ISO_MAP *map, float dt)
 Smoothly interpolate the projection angle toward the target.
ISO_MAP * iso_map_load (const char *filename)
 Load ISO_MAP from a binary file.
ISO_MAP * iso_map_new (int width, int height, int num_terrains, int max_height)
 Create a new height-aware isometric map.
void iso_map_randomize (ISO_MAP *map)
 Randomize terrain and height for testing/demo purposes.
int iso_map_save (const ISO_MAP *map, const char *filename)
 Save ISO_MAP to a binary file.
void iso_map_set_ability (ISO_MAP *map, int mx, int my, int ab)
 Set the ability at a cell.
void iso_map_set_height (ISO_MAP *map, int mx, int my, int h)
 Set the height at a cell (clamped to [0, max_height]).
void iso_map_set_projection (ISO_MAP *map, int preset, float tile_width)
 Set projection parameters from a preset and tile width.
void iso_map_set_projection_custom (ISO_MAP *map, float half_w, float half_h, float tile_lift)
 Set custom projection parameters directly.
void iso_map_set_projection_target (ISO_MAP *map, int preset)
 Set the target projection for smooth interpolation.
int iso_map_set_segments (ISO_MAP *map, int mx, int my, const ISO_TILE_SEGMENT *segs, int count)
 set per-tile segments on an ISO_MAP.
void iso_map_set_terrain (ISO_MAP *map, int mx, int my, int terrain)
 Set the terrain type at a cell.
int iso_map_should_transition (const ISO_MAP *map, int mx1, int my1, int mx2, int my2)
 Check if terrain blending should occur between two adjacent cells.
int iso_map_should_transition_smooth (const ISO_MAP *map, int mx1, int my1, int mx2, int my2)
 Check if terrain transition should render between two cells (height-aware).
void iso_map_smooth_corner_heights (const ISO_MAP *map, int mx, int my, float *h_n, float *h_e, float *h_s, float *h_w)
 Compute smooth corner heights with slope clamping.
void iso_map_to_screen (const ISO_MAP *map, int mx, int my, int h, float *screen_x, float *screen_y)
 Convert map tile coordinates to screen pixel coordinates.
void iso_map_to_screen_f (const ISO_MAP *map, float fmx, float fmy, float h, float *screen_x, float *screen_y)
 Convert map coordinates to screen coordinates (float version).
void iso_mask_tile_to_diamond (ALLEGRO_BITMAP *bmp, int tile_w, int tile_h)
 Mask a tile bitmap to the isometric diamond shape.
const char * iso_projection_name (int preset)
 Get the display name for a projection preset.
void iso_screen_to_map (const ISO_MAP *map, float screen_x, float screen_y, int *mx, int *my)
 Convert screen pixel coordinates to map tile coordinates.
void iso_screen_to_map_height (const ISO_MAP *map, float screen_x, float screen_y, int tile_w, int tile_h, int *mx, int *my)
 Height-aware screen-to-map conversion with diamond hit testing.
void iso_screen_to_map_height_f (const ISO_MAP *map, float screen_x, float screen_y, int tile_w, int tile_h, int *mx, int *my, float *out_fx, float *out_fy)
 Height-aware screen-to-map conversion returning fractional tile coordinates.
void iso_set_line_jitter_compensation (IsoLineJitterCompensationFn fn)
void iso_set_primitive_color_filter (IsoPrimitiveColorFilter fn)
void n_iso_camera_center_on (N_ISO_CAMERA *cam, float world_x, float world_y, int screen_w, int screen_h)
 Center the camera so that a world point is at screen center.
void n_iso_camera_follow (N_ISO_CAMERA *cam, float target_x, float target_y, int screen_w, int screen_h, float smoothing, float dt)
 Smoothly follow a world-space target.
void n_iso_camera_free (N_ISO_CAMERA **cam)
 Free a camera and set the pointer to NULL.
N_ISO_CAMERA * n_iso_camera_new (float zoom_min, float zoom_max)
 Create a new 2D isometric camera.
void n_iso_camera_screen_to_world (const N_ISO_CAMERA *cam, float sx, float sy, float *wx, float *wy)
 Convert screen pixel coordinates to world coordinates.
void n_iso_camera_scroll (N_ISO_CAMERA *cam, float dx, float dy)
 Scroll the camera by (dx, dy) world units.
void n_iso_camera_world_to_screen (const N_ISO_CAMERA *cam, float wx, float wy, float *sx, float *sy)
 Convert world coordinates to screen pixel coordinates.
void n_iso_camera_zoom (N_ISO_CAMERA *cam, float dz, float mouse_x, float mouse_y)
 Zoom the camera toward a screen-space point.

Variables

static IsoLineJitterCompensationFn g_iso_line_jitter_fn = NULL
static IsoPrimitiveColorFilter g_iso_primitive_color_filter = NULL
double n_iso_pass1_us = 0.0
 Per-pass timing accumulators populated by iso_map_draw.
double n_iso_pass2_us = 0.0
double n_iso_pass3_us = 0.0
double n_iso_setup_us = 0.0

Detailed Description

Isometric/axonometric tile engine with height maps, terrain transitions, and A* pathfinding integration.

Author
Castagnier Mickael
Version
2.0
Date
01/03/2026

Definition in file n_iso_engine.c.

Function Documentation

◆ _cmp_draw_entry()

int _cmp_draw_entry ( const void * a,
const void * b )
static

Definition at line 1025 of file n_iso_engine.c.

References ISO_DRAW_ENTRY::bottom, ISO_DRAW_ENTRY::mx, and ISO_DRAW_ENTRY::my.

Referenced by iso_map_build_draw_order().

Here is the caller graph for this function:

◆ _compute_tile_tint()

ALLEGRO_COLOR _compute_tile_tint ( int seg_top,
int max_height,
float intensity,
float ambient_r,
float ambient_g,
float ambient_b,
float dyn_r,
float dyn_g,
float dyn_b )
inlinestatic

Definition at line 1589 of file n_iso_engine.c.

References iso_apply_primitive_color_filter().

Referenced by iso_map_draw().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _draw_height_borders()

void _draw_height_borders ( const ISO_MAP * map,
int mx,
int my,
int seg_idx,
int seg_bottom,
int seg_top,
int has_underside_face,
const float vx[4],
const float vy[4],
float lift )
static

Draw black border outlines on the diamond edges of an elevated tile.

Rules:

  • Only draws edges where height difference > 1 or neighbor missing.
  • NEVER draws the top vertex (N) where NW and NE edges meet, it is always hidden behind the tile surface itself.
  • At corners E, S, W where two drawn edges meet, they join naturally.
  • Occlusion is handled by painter's algorithm: tiles drawn later (closer to camera) paint over borders of earlier tiles.

Diamond vertices: N=vx[0] (top), E=vx[1] (right), S=vx[2] (bottom), W=vx[3] (left)

Definition at line 1956 of file n_iso_engine.c.

References ISO_VISIBLE_EDGES::draw_ne, ISO_VISIBLE_EDGES::draw_nw, ISO_VISIBLE_EDGES::draw_se, ISO_VISIBLE_EDGES::draw_sw, ISO_VISIBLE_EDGES::draw_underside, iso_apply_line_jitter_compensation(), iso_map_get_visible_edges_segment(), ISO_VISIBLE_EDGES::neighbor_top_ne, ISO_VISIBLE_EDGES::neighbor_top_nw, ISO_VISIBLE_EDGES::neighbor_top_se, and ISO_VISIBLE_EDGES::neighbor_top_sw.

Referenced by iso_map_draw().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _draw_segment_sides()

void _draw_segment_sides ( const ISO_MAP * map,
float east_x,
float east_y,
float south_x,
float south_y,
float west_x,
float west_y,
int mx,
int my,
int seg_bottom,
int seg_top,
float lift,
float height_brightness,
int wall_terrain_override )
static

Draw cliff side faces for a single segment {seg_bottom..seg_top}.

For ground segments (seg_bottom==0) uses neighbor-aware wall height. For floating segments (seg_bottom>0) draws full segment height.

Definition at line 1703 of file n_iso_engine.c.

References iso_apply_line_jitter_compensation(), iso_apply_primitive_color_filter(), iso_map_get_height(), and iso_map_get_terrain().

Referenced by iso_map_draw().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _draw_segment_underside()

void _draw_segment_underside ( ALLEGRO_BITMAP * bmp,
float base_dx,
float base_dy,
float phw,
float phh,
float tile_draw_w,
float tile_draw_h,
int tile_w,
int tile_h )
static

Draw the underside face of a floating segment at 65% brightness.

The underside is a diamond drawn at the segment's bottom height.

Definition at line 1823 of file n_iso_engine.c.

Referenced by iso_map_draw().

Here is the caller graph for this function:

◆ _draw_tile_warped()

void _draw_tile_warped ( ALLEGRO_BITMAP * bmp,
const float vx[4],
const float vy[4],
int tile_w,
int tile_h,
ALLEGRO_COLOR tint )
static

Draw a tile bitmap warped to 4 pre-computed screen vertices.

Uses two textured triangles mapped to the warped diamond shape. Vertices must come from _iso_corner_to_screen for bit-identical shared edges between adjacent tiles.

Definition at line 1628 of file n_iso_engine.c.

Referenced by iso_map_draw().

Here is the caller graph for this function:

◆ _iso_corner_to_screen()

void _iso_corner_to_screen ( float hw,
float hh,
float tl,
int cx,
int cy,
float fh,
float cam_px,
float cam_py,
float zoom,
float * sx,
float * sy )
inlinestatic

Project a tile corner to screen coordinates (canonical formula).

Every screen vertex in the renderer must come from this function so that two tiles sharing a corner get bit-identical screen positions regardless of which tile's draw call is computing the vertex.

The camera offset (cam_px, cam_py) must be integer-snapped pixel offsets computed as floor(cam_x * zoom), floor(cam_y * zoom). This ensures the fractional pixel position of each vertex comes only from (wx * zoom), which is constant per vertex, eliminating sub-pixel aliasing shimmer during camera movement.

Parameters
hwmap->proj.half_w (unzoomed)
hhmap->proj.half_h (unzoomed)
tlmap->proj.tile_lift (unzoomed)
cxcorner X in tile grid (e.g. mx+1 for the East corner)
cycorner Y in tile grid
fhheight (float, supports smooth-height interpolation)
cam_pxinteger-snapped camera pixel X: floor(cam_x * zoom)
cam_pyinteger-snapped camera pixel Y: floor(cam_y * zoom)
zoomzoom factor
sxoutput screen X
syoutput screen Y

Definition at line 1579 of file n_iso_engine.c.

Referenced by _iso_object_check_occlusion(), and iso_map_draw().

Here is the caller graph for this function:

◆ _iso_fopen_write()

FILE * _iso_fopen_write ( const char * path,
const char * mode )
static

Definition at line 46 of file n_iso_engine.c.

References mode.

Referenced by iso_map_save().

Here is the caller graph for this function:

◆ _iso_object_check_occlusion()

int _iso_object_check_occlusion ( const ISO_MAP * map,
float obj_fx,
float obj_fy,
float obj_fz,
float obj_sx,
float obj_sy,
float sprite_h,
float sprite_half_w,
float cam_px,
float cam_py,
float zoom,
float * out_clip_y )
static

Check if an object is occluded by tiles drawn in front of it.

Tests tiles at higher depth (mx+my) in a forward cone to see if any elevated tile's screen area overlaps the object's screen position.

Parameters
mapthe map
obj_fxobject fractional map X
obj_fyobject fractional map Y
obj_fzobject height
obj_sxobject screen X (camera-transformed)
obj_syobject screen Y (camera-transformed)
sprite_hsprite height used for occlusion bounding
sprite_half_whalf-width of the sprite footprint (occlusion bbox)
cam_pxcamera X offset in pixels
cam_pycamera Y offset in pixels
zoomcurrent zoom
out_clip_yoptional out-pointer: when non-NULL and a partial overlap is detected, receives the screen-Y above which the sprite should be clipped (for soft-fade rendering). May be NULL.
Returns
1 if occluded, 0 if visible

Definition at line 1862 of file n_iso_engine.c.

References _iso_corner_to_screen(), ISO_PROJECTION::half_h, ISO_PROJECTION::half_w, ISO_MAP::height, iso_map_get_height(), ISO_MAP::max_height, ISO_MAP::proj, ISO_PROJECTION::tile_lift, and ISO_MAP::width.

Referenced by iso_map_draw().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _iso_preset_angle()

float _iso_preset_angle ( int preset)
static

Get the angle in degrees for a projection preset.

Parameters
presetone of ISO_PROJ_CLASSIC, ISO_PROJ_ISOMETRIC, ISO_PROJ_STAGGERED, ISO_PROJ_MILITARY
Returns
angle in degrees

Definition at line 214 of file n_iso_engine.c.

References ISO_PROJ_CLASSIC, ISO_PROJ_ISOMETRIC, ISO_PROJ_MILITARY, and ISO_PROJ_STAGGERED.

Referenced by iso_map_set_projection(), and iso_map_set_projection_target().

Here is the caller graph for this function:

◆ _neighbor_height_at()

int _neighbor_height_at ( const ISO_MAP * map,
int mx,
int my )
static

Definition at line 1125 of file n_iso_engine.c.

References ISO_MAP::height, iso_map_get_height(), ISO_MAP::neighbor_heights_east, ISO_MAP::neighbor_heights_north, ISO_MAP::neighbor_heights_south, ISO_MAP::neighbor_heights_west, and ISO_MAP::width.

Referenced by _neighbor_should_transition().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _neighbor_should_transition()

int _neighbor_should_transition ( const ISO_MAP * map,
int mx1,
int my1,
int mx2,
int my2 )
static

Definition at line 1141 of file n_iso_engine.c.

References _neighbor_height_at(), ISO_MAP::smooth_height, and ISO_MAP::smooth_slope_max.

Referenced by iso_map_calc_transitions_full().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _neighbor_terrain_at()

int _neighbor_terrain_at ( const ISO_MAP * map,
int mx,
int my )
static

Edge-aware terrain lookup for chunked rendering.

When (mx,my) falls just outside the chunk on a cardinal edge and the caller has wired up the neighbor_terrains_* arrays, consult them so transition masks can cross chunk seams. Out-of-range without a neighbor array falls back to the standard 0-out-of-bounds semantics. Diagonal neighbors (corners of the 8-neighborhood that fall outside the chunk's own bounds) are not covered, a fully correct diagonal fix would need 4 extra corner-cell fields.

Parameters
mapthe map
mxtile X (may be just outside [0, width))
mytile Y (may be just outside [0, height))
Returns
terrain index at (mx,my), or 0 if both in-chunk and neighbor lookups fail

Definition at line 1109 of file n_iso_engine.c.

References ISO_MAP::height, iso_map_get_terrain(), ISO_MAP::neighbor_terrains_east, ISO_MAP::neighbor_terrains_north, ISO_MAP::neighbor_terrains_south, ISO_MAP::neighbor_terrains_west, and ISO_MAP::width.

Referenced by iso_map_calc_transitions_full().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _smooth_neighbor_h()

float _smooth_neighbor_h ( const ISO_MAP * map,
float h_self,
int nx,
int ny )
inlinestatic

Helper: clamp neighbor height for smooth slope rendering.

If the height difference exceeds smooth_slope_max, return self height (creating a cliff instead of a slope).

Definition at line 945 of file n_iso_engine.c.

References iso_map_get_height(), and ISO_MAP::smooth_slope_max.

Referenced by iso_map_smooth_corner_heights().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _tile_covers_height()

int _tile_covers_height ( const ISO_MAP * map,
int mx,
int my,
int z )
static

Check if a tile has geometry covering height level z.

Returns 1 if any segment of (mx,my) has bottom <= z AND top > z, meaning the tile's wall/surface extends strictly above z. Used for occlusion: a tile drawn later in painter order covers edges of earlier tiles when its wall passes above the edge height.

Requires the occluding tile to be at least 2 height units taller (top > z + 1) so that single-step stairs (+1 per tile) do not suppress the NW/NE borders, the step only partially covers the edge, and painter order handles the overlap visually.

Definition at line 1401 of file n_iso_engine.c.

References ISO_TILE_SEGMENT::bottom, ISO_TILE_SEGMENTS::count, h(), ISO_MAP::height, iso_map_get_height(), iso_map_get_segments(), ISO_TILE_SEGMENTS::segs, ISO_TILE_SEGMENT::top, and ISO_MAP::width.

Referenced by iso_map_get_visible_edges_segment().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ g_iso_line_jitter_fn

IsoLineJitterCompensationFn g_iso_line_jitter_fn = NULL
static

◆ g_iso_primitive_color_filter

IsoPrimitiveColorFilter g_iso_primitive_color_filter = NULL
static