sfengine

Shaofeng numerical core: fluid, vertex batch and tracer kernels compiled to WASM for the game

wasm
fluid
simulation
physics
game
webgl
moon add HK-SHAO/sfengine@0.1.2
Download zip
Author
Version
0.1.2
License
Apache-2.0
Last updated
5 hours ago
Downloads
3
README

#sfengine

Numerical core for the Shaofeng (烧风) game: an Eulerian fluid simulation with vertex batching and tracer particles, compiled to a single WebAssembly module for in-game use.

#Kernels

  • Fluid — Eulerian grid solver (temperature-driven buoyancy, MacCormack advection, red-black GS pressure projection with f64x2 SIMD).
  • Vertex batch — GPU vertex assembly: SDF terrain (marching squares), rectangles/rings/arcs/strokes and dashed rings.
  • Tracers — passive wind-field tracers with short trail history.

#Build target

The package is wasm-only (supported_targets = "wasm", foreign_library). It exposes a C-style FFI surface via #export_name — there is no _start; the host JS calls exported functions directly and reads buffers through the linear memory pointer returned by each *_buf() export.

#Memory contract

The linear memory is pinned to a fixed size at link time (memory-limits min=max 512 pages = 32 MB) to cover the worst-case grid (256×160) plus GC heap. Growing memory at runtime would detach host-side Float32Array views, so growth is forbidden at the config level.

The canary_* exports verify the memory/FFI layout between the host and the kernel — a handshake contract guarded by tests.

#Testing

moon test

White-box tests (*_wbtest.mbt) pin bit-exact invariants (SIMD/scalar equivalence, FFI addressing, kernel limits); black-box tests exercise the exported surface.

#License

#
add_force

fn add_force(wx : Double, wy : Double, fx : Double, fy : Double, amount : Double, radius : Double) -> Unit

Inject momentum at (wx, wy) in direction (fx, fy) within a radius, with falloff.

#
add_heat

fn add_heat(wx : Double, wy : Double, amount : Double) -> Unit

Inject heat at (wx, wy) within the source radius (negative cools), clamped to ±t_max.

#
b_arc

fn b_arc(cx : Double, cy : Double, radius : Double, a0 : Double, a1 : Double, seg : Int, w : Double, r : Double, g : Double, bl : Double, a : Double) -> Unit

Append a stroked arc segment (angle a0..a1) with a flat color.

#
b_capacity

fn b_capacity() -> Int

Maximum vertex-batch capacity (in vertices).

#
b_count

fn b_count() -> Int

Number of vertices currently in the batch.

#
b_dash_ring

fn b_dash_ring(cx : Double, cy : Double, radius : Double, on : Double, off : Double, w : Double, r : Double, g : Double, bl : Double, a : Double) -> Unit

Append a dashed stroked ring (dash on/off lengths) with a flat color.

#
b_data

fn b_data() -> Int

Linear-memory address of the vertex data buffer (Float32Array, stride bVertexStride).

#
b_disc

fn b_disc(cx : Double, cy : Double, rx : Double, ry : Double, rot : Double, seg : Int, r : Double, g : Double, bl : Double, a : Double) -> Unit

Append a filled ellipse/disk (rotated, segment count) with a flat color.

#
b_disc_grad

fn b_disc_grad(cx : Double, cy : Double, radius : Double, seg : Int, cr : Double, cg : Double, cb : Double, ca : Double, er : Double, eg : Double, eb : Double, ea : Double) -> Unit

Append a filled disk with a radial color gradient (center to edge).

#
b_rect

fn b_rect(x0 : Double, y0 : Double, x1 : Double, y1 : Double, r : Double, g : Double, bl : Double, a : Double) -> Unit

Append a filled rectangle (2 triangles, 6 vertices) with a flat color.

#
b_rect_vgrad

fn b_rect_vgrad(x0 : Double, y0 : Double, x1 : Double, y1 : Double, r0 : Double, g0 : Double, b0 : Double, a0 : Double, r1 : Double, g1 : Double, b1 : Double, a1 : Double) -> Unit

Append a filled rectangle with a vertical color gradient (top to bottom).

#
b_reset

fn b_reset() -> Unit

Reset the vertex batch (clear all queued vertices).

#
b_ring

fn b_ring(cx : Double, cy : Double, rx : Double, ry : Double, rot : Double, seg : Int, w : Double, r : Double, g : Double, bl : Double, a : Double) -> Unit

Append a stroked ellipse ring (width w, miter-joined) with a flat color.

#
b_stroke

fn b_stroke(x0 : Double, y0 : Double, x1 : Double, y1 : Double, w : Double, r : Double, g : Double, bl : Double, a : Double, round : Bool) -> Unit

Append a stroked segment (miter join, optional round caps) with a flat color.

#
b_terrain_cap

fn b_terrain_cap() -> Int

Baked terrain vertex data capacity (in vertices).

#
b_terrain_data

fn b_terrain_data() -> Int

Linear-memory address of the baked terrain vertex data (Float32Array).

#
b_terrain_draw

fn b_terrain_draw(i0 : Int, j0 : Int, i1 : Int, j1 : Int) -> Int

Tessellate the terrain d=0 contour within a cell range (i0..i1, j0..j1) into the batch. Returns the number of vertices emitted.

#
b_terrain_field

fn b_terrain_field(nx : Int, ny : Int, x0 : Double, y0 : Double, cell : Double, sr : Double, sg : Double, sb : Double, dr : Double, dg : Double, db : Double, depth_len : Double) -> Int

Configure the terrain field (grid, world origin, cell size, shading colors, depth length). Returns 0 on success, 1 on invalid parameters.

#
b_terrain_field_buf

fn b_terrain_field_buf() -> Int

Linear-memory address of the terrain SDF field buffer (Float32Array, nx*ny).

#
b_terrain_field_cap

fn b_terrain_field_cap() -> Int

Terrain SDF field capacity (in cells, equals the fluid grid upper bound).

#
b_tri

fn b_tri(x0 : Double, y0 : Double, x1 : Double, y1 : Double, x2 : Double, y2 : Double, r : Double, g : Double, bl : Double, a : Double) -> Unit

Append a filled triangle (3 vertices) with a flat color.

#
b_vertex_stride

fn b_vertex_stride() -> Int

Vertex stride (interleaved x, y, r, g, b, a = 6 floats).

#
canary_buf

fn canary_buf() -> Int

Linear-memory address of the canary buffer (4 i32), for host-side FFI handshake.

#
canary_get

fn canary_get(i : Int) -> Int

Read one canary slot (host verifies address round-trip).

#
canary_len

fn canary_len() -> Int

Canary buffer length (always 4).

#
canary_set

fn canary_set(i : Int, v : Int) -> Unit

Write one canary slot (host verifies address round-trip).

#
clear

fn clear() -> Unit

Zero the velocity, temperature and pressure fields.

#
f_max_nx

fn f_max_nx() -> Int

Maximum grid width (nx) supported by the fixed-capacity kernel.

#
f_max_ny

fn f_max_ny() -> Int

Maximum grid height (ny) supported by the fixed-capacity kernel.

#
field_fx_u

fn field_fx_u() -> Int

Linear-memory address of the ambient basis velocity X field (Float32Array).

#
field_fx_v

fn field_fx_v() -> Int

Linear-memory address of the ambient basis velocity Y field (Float32Array).

#
field_t

fn field_t() -> Int

Linear-memory address of the temperature field (Float32Array, nx*ny).

#
field_u

fn field_u() -> Int

Linear-memory address of the velocity X field (Float32Array, nx*ny).

#
field_v

fn field_v() -> Int

Linear-memory address of the velocity Y field (Float32Array, nx*ny).

#
fluid_init

fn fluid_init(nx_ : Int, ny_ : Int, cell_ : Double, buoyancy_ : Double, t_max_ : Double, source_radius_ : Double, vel_damping_ : Double, t_damping_ : Double, iterations_ : Int, margin_cells_ : Int) -> Int

Initialize the fluid grid (dimensions, cell size, tuning, margin) and reset all state. Returns 0 on success, 1 on invalid parameters.

#
out_x

fn out_x() -> Double

Sampled velocity X component (after sample_velocity).

#
out_y

fn out_y() -> Double

Sampled velocity Y component (after sample_velocity).

#
rebuild_solid

fn rebuild_solid() -> Unit

Rebuild the solid index from the terrain mask and rebake the ambient basis.

#
sample_temp

fn sample_temp(wx : Double, wy : Double) -> Double

Sample the total temperature at (wx, wy) = field temperature + ambient bias.

#
sample_velocity

fn sample_velocity(wx : Double, wy : Double) -> Unit

Sample the flow velocity at (wx, wy) (incl. ambient); result via out_x/out_y.

#
set_ambient

fn set_ambient(x : Double, y : Double, temp : Double) -> Unit

Set the ambient wind velocity (x, y) and temperature bias.

#
solid_buf

fn solid_buf() -> Int

Linear-memory address of the solid mask (Uint8Array, nx*ny; nonzero = solid).

#
step

fn step(dt : Double) -> Unit

Advance the fluid simulation by dt (seconds): buoyancy, advection, sponge, projection.

#
t_life_buf

fn t_life_buf() -> Int

Linear-memory address of the remaining lifetime per tracer (Float32Array, count).

#
t_max_life_buf

fn t_max_life_buf() -> Int

Linear-memory address of the max lifetime per tracer (Float32Array, count).

#
t_sdf_buf

fn t_sdf_buf() -> Int

Linear-memory address of the tracer SDF field (Float32Array, snx*sny).

#
t_sdf_cap

fn t_sdf_cap() -> Int

Tracer SDF field capacity (in cells, equals the fluid grid upper bound).

#
t_src_buf

fn t_src_buf() -> Int

Linear-memory address of the hot/cold source table (Float32Array, src_cap*6).

#
t_src_cap

fn t_src_cap() -> Int

Source table capacity (in source slots).

#
t_time

fn t_time() -> Double

Current simulation time (seconds).

#
t_trail_n_buf

fn t_trail_n_buf() -> Int

Linear-memory address of the trail length per tracer (Uint8Array, count).

#
t_trail_t_buf

fn t_trail_t_buf() -> Int

Linear-memory address of the trail write times (Float32Array, count*trailLen).

#
t_trail_x_buf

fn t_trail_x_buf() -> Int

Linear-memory address of the trail X control points (Float32Array, count*trailLen).

#
t_trail_y_buf

fn t_trail_y_buf() -> Int

Linear-memory address of the trail Y control points (Float32Array, count*trailLen).

#
t_x_buf

fn t_x_buf() -> Int

Linear-memory address of the tracer X positions (Float32Array, count).

#
t_y_buf

fn t_y_buf() -> Int

Linear-memory address of the tracer Y positions (Float32Array, count).

#
tracers_init

fn tracers_init(count : Int, trail_len : Int, world_w : Double, world_h : Double, margin : Double, snx : Int, sny : Int, scell : Double, sox : Double, soy : Double, seed : UInt) -> Int

Initialize the tracer particle system (count, trail length, world bounds, SDF grid, PRNG seed). Returns 0 on success; 1 = capacity mismatch, 2 = invalid grid.

#
tracers_step

fn tracers_step(dt : Double, src_count : Int) -> Unit

Advance all tracer particles by dt (seconds), advected by the flow and respawned on death.