luv

Workshop wiki

mesher.lisp

luvcraft/mesher.lisp

system luvcraft/core · 64 definitions · on GitHub

Turning resident block data into exposed-face triangle meshes.

The result of meshing is deliberately mundane: interleaved position, UV/shade, and normal triples of single floats. Meshing always starts by gathering: a chunk and its one-cell halo are copied into an immutable block-mesh-snapshot of u16 palette indices and u8 light levels (whether for a worker thread or for owner-side meshing), and the small closed block vocabulary is projected through its protocol generics into flat per-index tables once per job. The dense loop then reads only those columns and tables; no block object, face object, or generic function is consulted per sample.

in-package#:luvcraft
defclassblock-mesher
defclassexposed-face-mesher
absent-neighbor-policy:initarg:absent-neighbor-policy:initform:air:readerexposed-face-mesher-absent-neighbor-policy
defclassblock-mesh
vertex-declaration:initarg:vertex-declaration:initform:readerblock-mesh-vertex-declaration
vertices:initarg:vertices:readerblock-mesh-vertices
vertex-count:initarg:vertex-count:readerblock-mesh-vertex-count
face-count:initarg:face-count:readerblock-mesh-face-count
defmethodinitialize-instance:after
&key
let*
declaration
block-mesh-vertex-declarationmesh
vertices
block-mesh-verticesmesh
unless
error"Block mesh vertices ~S do not satisfy ~S."
type-ofvertices
unless
error"Block mesh declaration has no repeated vertex layout: ~S."declaration
unless
=
lengthvertices
*
block-mesh-vertex-countmesh
luv.arithmetic:repeated-quantity-layout-stridelayout
error"Block mesh has ~D lanes for ~D declared vertices at stride ~D."
lengthvertices
block-mesh-vertex-countmesh
luv.arithmetic:repeated-quantity-layout-stridelayout
defunmerge-block-mesh-vertex-declaration
accumulatormesh

Preserve one exact layout identity while concatenating mesh products.

let
declaration
block-mesh-vertex-declarationmesh
when
andaccumulator
not
eqaccumulatordeclaration
error"Cannot combine block meshes carrying different vertex layouts."
declaration
defclassblock-mesh-halo-domain
chunk-domain:initarg:chunk-domain:readerblock-mesh-halo-domain-chunk-domain
:documentation

The sites in one chunk plus the one-cell sampling halo around it.

defmethoddomains:domain-cardinality
let*
chunk-domain
block-mesh-halo-domain-chunk-domaindomain
shape
voxel-space-chunk-shape
chunk-domain-spacechunk-domain
*
+2
chunk-shape-widthshape
+2
chunk-shape-heightshape
+2
chunk-shape-depthshape
records:define-columnar-materializationblock-mesh-halo-fields

CONTENT-INDEX is a projection through palette, not the logical :block-content declaration itself. The light lanes already have direct represented field declarations and bind those below.

content-index0:type
unsigned-byte16
sky-level0:type
unsigned-byte8
block-level0:type
unsigned-byte8
defclassblock-mesh-snapshot
key:initarg:key:readerblock-mesh-snapshot-key
dependency-stamp:initarg:dependency-stamp:readerblock-mesh-snapshot-dependency-stamp
domain:initarg:domain:readerblock-mesh-snapshot-domain
content-definition:initarg:content-definition:readerblock-mesh-snapshot-content-definition

A frozen copy of the world vocabulary's members at capture time: the halo's u16 indices are closed by it. Its length is the ABSENT index, one past the last member, so a halo sample outside resident terrain is a distinct member of the snapshot's own vocabulary.

palette:initarg:palette:readerblock-mesh-snapshot-palette
halo-fields:initarg:halo-fields:readerblock-mesh-snapshot-halo-fields
:documentation

An immutable dense chunk plus one-cell halo transferred to a CPU worker.

The snapshot owns compact u16 columns whose indices are the world vocabulary's own offsets, frozen by copying the member vector; the copy's length stands for an absent sample. Cell identity does not cross the thread boundary, and the worker never observes live chunk storage.

defunblock-mesh-snapshot-absent-index
snapshot
length
thesimple-vector
block-mesh-snapshot-palettesnapshot
defunblock-mesh-snapshot-halo-domain
snapshot
block-mesh-halo-fields-domain
block-mesh-snapshot-halo-fieldssnapshot
defunblock-mesh-snapshot-sample-indices
snapshot
block-mesh-halo-fields-content-index-lane
block-mesh-snapshot-halo-fieldssnapshot
defunblock-mesh-snapshot-sky-samples
snapshot
block-mesh-halo-fields-sky-level-lane
block-mesh-snapshot-halo-fieldssnapshot
defunblock-mesh-snapshot-block-light-samples
snapshot
block-mesh-halo-fields-block-level-lane
block-mesh-snapshot-halo-fieldssnapshot
defunblock-mesh-snapshot-sky-definition
snapshot
records:columnar-row-lane-declaration
block-mesh-halo-fields-row-declaration
block-mesh-snapshot-halo-fieldssnapshot
'sky-level
defunblock-mesh-snapshot-block-light-definition
snapshot
records:columnar-row-lane-declaration
block-mesh-halo-fields-row-declaration
block-mesh-snapshot-halo-fieldssnapshot
'block-level
defmethodluvcraft.world.fields:materialized-field-definition
field-name
eql:block-content
declare
ignorefield-name
block-mesh-snapshot-content-definitionsnapshot
defmethodluvcraft.world.fields:materialized-field-definition
field-name
eql:sky-light
declare
ignorefield-name
defgenericmesh-block-chunk
mesherworldchunk
defgenericmesh-block-snapshot
meshersnapshot
defunpush-block-vertex-components
verticespxpypzlocal-ulocal-vshadenxnynzsky-levelblock-levelemissiontileedge-u-lowedge-u-highedge-v-lowedge-v-high

Append one interleaved vertex without constructing tuple objects.

The fourth lane carries normalized raw light readings, not an art-directed bake: shader edits can change the response curve without remeshing the world. The final two scalars carry the atlas tile offset under the atlas mapping and the face's four ternary edge classifications. Atlas UV resolution and edge unpacking belong to the shaders, not vertex producers.

vector-push
coercepx'single-float
vertices
vector-push
coercepy'single-float
vertices
vector-push
coercepz'single-float
vertices

The half-texel inset is local to a tile, independent of which atlas lane owns it or how wide the materialization is.

vector-push
coerce
/
+0.5
*local-u15
16
'single-float
vertices
vector-push
coerce
/
+0.5
*local-v15
16
'single-float
vertices
vector-push
coerceshade'single-float
vertices
vector-push
coercenx'single-float
vertices
vector-push
coerceny'single-float
vertices
vector-push
coercenz'single-float
vertices
vector-push
coercesky-level'single-float
vertices
vector-push
coerceblock-level'single-float
vertices
vector-push
coerceemission'single-float
vertices
vector-push
coercetile'single-float
vertices
vector-push
coerce
+
*
+edge-u-low1
27
*
+edge-u-high1
9
*
+edge-v-low1
3
+edge-v-high1
'single-float
vertices
vertices
defunblock-color-variation
xyz
+0.93
*0.07
/
mod
+
*x17
*y31
*z13
7
6.0
defstruct
block-mesh-neighborhood
:constructor%make-block-mesh-neighborhood

The 3x3x3 resident chunk neighborhood needed by one chunk mesh.

domainnil:type
chunks
make-array27:initial-elementnil
:typesimple-vector
defunblock-mesh-neighborhood-index
dxdydz
+
1+dx
*3
+
1+dy
*3
1+dz
defunmake-block-mesh-neighborhood
worldchunk

Resolve once the chunks every visibility and AO sample can reach.

let*
domain
block-chunk-domainchunk
coordinate
chunk-domain-coordinatedomain
chunk-x
chunk-coordinate-xcoordinate
chunk-y
chunk-coordinate-ycoordinate
chunk-z
chunk-coordinate-zcoordinate
chunks
make-array27:initial-elementnil
loopfordzfrom-1to1do
loopfordyfrom-1to1do
loopfordxfrom-1to1do
setf
world-chunk-atworld
+chunk-xdx
+chunk-ydy
+chunk-zdz
%make-block-mesh-neighborhood:domaindomain:chunkschunks
defunblock-mesh-neighborhood-locate
neighborhoodxyz

Resolve one nearby world site to its resident chunk and dense offset.

The 3x3x3 window owns availability; voxel-space and chunk-domain own the decomposition and storage order beneath it. See #K3KZTG.

let*
domain
block-mesh-neighborhood-domainneighborhood
space
chunk-domain-spacedomain
center
chunk-domain-coordinatedomain
multiple-value-bind
chunk-xchunk-ychunk-zlocal-xlocal-ylocal-z
let
dx
-chunk-x
chunk-coordinate-xcenter
dy
-chunk-y
chunk-coordinate-ycenter
dz
-chunk-z
chunk-coordinate-zcenter
when
and
<=-1dx1
<=-1dy1
<=-1dz1
let
chunk
aref
block-mesh-neighborhood-chunksneighborhood
whenchunk
valueschunk
chunk-domain-offset-components
block-chunk-domainchunk
local-xlocal-ylocal-z
defmethodlocate-chunk-window-site
neighborhoodblock-mesh-neighborhood
xyz
multiple-value-bind
chunkoffset
ifchunk
valueschunkoffset:available
valuesnilnil:unavailable
defunblock-mesh-neighborhood-block-at
neighborhoodxyz

Read a nearby world site with no coordinate objects or hash-key consing.

multiple-value-bind
chunkoffset
ifchunk
values
block-content-at-offset
block-chunk-contentchunk
offset
:resident
valuesnil:absent
defunblock-mesh-halo-offset-components
halo-domainxyz

Return the dense one-cell-halo offset for a world site, or NIL outside.

let*
domain
block-mesh-halo-domain-chunk-domainhalo-domain
shape
voxel-space-chunk-shape
chunk-domain-spacedomain
sample-width
+2
chunk-shape-widthshape
sample-height
+2
chunk-shape-heightshape
sample-depth
+2
chunk-shape-depthshape
multiple-value-bind
origin-xorigin-yorigin-z
let
sample-x
1+
-xorigin-x
sample-y
1+
-yorigin-y
sample-z
1+
-zorigin-z
when
and
<=0sample-x
<sample-xsample-width
<=0sample-y
<sample-ysample-height
<=0sample-z
<sample-zsample-depth
+sample-x
*sample-width
+sample-y
*sample-heightsample-z
defunblock-mesh-snapshot-locate
snapshotxyz

Resolve one halo site without aggregate dispatch in the meshing loop.

let
if
valuessnapshotoffset:available
valuesnilnil:unavailable
defunblock-mesh-snapshot-block-at
snapshotxyz
multiple-value-bind
materializationoffsetavailability
declare
ignorematerialization
ecaseavailability
:available
values
aref
block-mesh-snapshot-palettesnapshot
:resident
:unavailable
valuesnil:absent
defgenericsample-block-at
samplesxyz
:documentation

Read one world site from a meshing sample source.

Return (VALUES BLOCK STATUS) where STATUS is :RESIDENT or :ABSENT. Meshing itself never chooses a representation: the live world, a preresolved chunk neighborhood, and an immutable worker snapshot each answer through their own method, and further sample sources only need to add one.

defmethodsample-block-at
xyz
world-block-atsamplesxyz
defmethodsample-block-at
samplesblock-mesh-neighborhood
xyz
defgenericsample-light-at
samplesxyz
:documentation

Read one site's published light from a meshing sample source.

Return (VALUES SKY BLOCK STATUS) with raw 0..15 levels. STATUS is :RESIDENT or :ABSENT; an absent or unlit sample answers zeros, and the corner-averaging rules decide what that means rather than any caller falling through to block-solid-p.

defmethodsample-light-at
xyz
multiple-value-bind
skyblockstate
world-light-atsamplesxyz
if
eqstate:absent
values00:absent
valuesskyblock:resident
defmethodsample-light-at
samplesblock-mesh-neighborhood
xyz
multiple-value-bind
chunkoffset
ifchunk
let
field
block-chunk-light-fieldchunk
iffield
values:resident
values00:resident
values00:absent
defmethodsample-light-at
multiple-value-bind
materializationoffsetavailability
declare
ignorematerialization
ecaseavailability
:available
values:resident
:unavailable
values00:absent

What a fragment cannot know about its own face is what lies beyond its edges. The mesher does know, so it classifies each of a face's four in-plane boundaries once and hands the answer to the surface shader.

This is the difference between a block world that reads as carved solids and one that reads as a lit grid: rounding every face edge over would draw a seam across the middle of an open plain, where the face merely continues into an identically oriented neighbour and there is no edge at all.

-1 concave: a block rises across this edge, so the surface fillets into the inner corner and gathers a little occlusion there. 0 flush: the same plane continues; leave the surface alone. 1 convex: nothing beyond the edge, so the surface rounds over it.

Gathering.

The mesher starts by collecting everything it will ask of the block vocabulary, once per job, into a few flat tables indexed by palette position: occupancy, surface emission, and one atlas tile offset per face. The loop below then reads u16 palette indices and u8 light levels straight out of the halo columns and never consults a block object, a face object, or a generic function per sample. The vocabulary is small and closed -- it changes only when someone redefines a kind at the REPL -- so the gathering is cheap and the protocol generics still decide what each kind means. See #FIGJ9R.

defstruct
block-mesh-kind-tables
:constructor%make-block-mesh-kind-tables

Per-palette-index answers the mesher gathered before its dense loop.

solid
make-array0:element-type'bit
:type
simple-arraybit
emission
make-array0:element-type'single-float
:type
simple-arraysingle-float

Six tile offsets per palette index, in *BLOCK-FACES* order.

tiles
make-array0:element-type'
unsigned-byte16
:type
simple-array
unsigned-byte16
defungather-block-mesh-kind-tables
mesherpalette

Project palette through the block protocol into flat per-index tables.

The tables have one more row than palette: the final row is the absent sample, whose occupancy comes from the mesher's absent-neighbor policy. Index zero is air (NIL); every other entry is a block kind. Faces are numbered in *BLOCK-FACES* order.

let*
solid
make-arraycount:element-type'bit:initial-element0
emission
make-arraycount:element-type'single-float:initial-element0.0
tiles
make-array
*6count
:element-type'
unsigned-byte16
:initial-element0
policy
exposed-face-mesher-absent-neighbor-policymesher
setf
sbitsolidabsent
ecasepolicy
:air0
:solid
if
block-solid-p*stone-block*
10
:error0
loopforindexfrom0belowabsentforblock=
arefpaletteindex
do
when
setf
sbitsolidindex
1
setf
arefemissionindex
coerce'single-float
whenblock
loopforfacein*block-faces*forface-indexfrom0do
setf
areftiles
+
*6index
face-index
%make-block-mesh-kind-tables:solidsolid:emissionemission:tilestiles
defstruct
block-mesh-face-table
:constructor%make-block-mesh-face-table

One face's geometry, gathered once from its block-face object.

nx0:typefixnum
ny0:typefixnum
nz0:typefixnum

Four corners as (cx cy cz) triples, then local UV per corner.

corners
make-array12:element-type'fixnum
:type
simple-arrayfixnum
12
uvs
make-array8:element-type'fixnum
:type
simple-arrayfixnum
8
defungather-block-mesh-face-tables
map'simple-vector
lambda
face
let*
normal
block-face-neighborface
corners
make-array12:element-type'fixnum
uvs
make-array8:element-type'fixnum
loopforcornerin
block-face-cornersface
forifrom0do
setf
arefcorners
*3i
firstcorner
arefcorners
+1
*3i
secondcorner
arefcorners
+2
*3i
thirdcorner
multiple-value-bind
uv
setf
arefuvs
*2i
u
arefuvs
+1
*2i
v
%make-block-mesh-face-table:nx
voxel-direction-dxnormal
:ny
voxel-direction-dynormal
:nz
voxel-direction-dznormal
:cornerscorners:uvsuvs
*block-faces*
defvar*block-mesh-face-tables*nil"Face geometry gathered from *BLOCK-FACES*; NIL until first gathered."

The dense loop.

deftypeblock-mesh-halo-index
'
integer0#.
ash124
deftypeblock-mesh-halo-extent
'
integer0256
deftypeblock-mesh-halo-step
'
integer#.
-
ash124
#.
ash124
defunblock-mesh-corner-shade-and-light
solidindicesskyblock-lightabsentbasensteps1steps2step

Return (VALUES AO sky BLOCK) for one vertex corner.

base is the halo offset of the face's own cell; nstep steps across the face normal and S1STEP/S2STEP along the two in-plane directions the corner leans toward. Occupancy and light averaging follow exactly the rules of the former per-sample accessors: a solid cell holds no light, an absent sample contributes nothing, and the diagonal is unreachable behind two sides.

declare
type
simple-arraybit
solid
type
simple-array
unsigned-byte16
indices
type
simple-array
unsigned-byte8
skyblock-light
type
unsigned-byte16
absent
typeblock-mesh-halo-stepnsteps1steps2step
optimize
speed3
safety0
let*
n
+basenstep
a
+ns1step
b
+ns2step
d
+as2step
ia
arefindicesa
ib
arefindicesb
id
arefindicesd
in
arefindicesn
solid-a
=1
sbitsolidia
solid-b
=1
sbitsolidib
solid-d
=1
sbitsolidid
sky-sum0
block-sum0
count0
declare
typefixnumsky-sumblock-sumcount
flet
consider
indexoffset
unless
=indexabsent
incfsky-sum
arefskyoffset
incfblock-sum
arefblock-lightoffset
incfcount
declare
inlineconsider
considerinn
unlesssolid-a
consideriaa
unlesssolid-b
consideribb
unless
andsolid-asolid-b
unlesssolid-d
consideridd
values
if
andsolid-asolid-b
0.56
-1.0
*0.14
+
ifsolid-a10
ifsolid-b10
ifsolid-d10
if
pluspcount
/sky-sum
*15.0count
0.0
if
pluspcount
/block-sum
*15.0count
0.0
defunemit-block-mesh-face
tablesfacesverticesfillsolidindicesskyblock-lightwidthheightdepthbasepalette-indexface-indexxyz

Append one face's six vertices at fill and return the new fill position.

base is the halo offset of the cell at world (X Y Z); WIDTH/HEIGHT/DEPTH are the halo dimensions. Vertex order and every lane value match what the per-sample accessors produced, so existing meshes and tests agree.

declare
typeblock-mesh-kind-tablestables
typesimple-vectorfaces
type
simple-arraysingle-float
vertices
typefixnumfill
type
simple-arraybit
solid
type
simple-array
unsigned-byte16
indices
type
simple-array
unsigned-byte8
skyblock-light
typefixnumxyz
type
unsigned-byte16
palette-index
type
integer05
face-index
ignoredepth
optimize
speed3
safety0
let*
face
svreffacesface-index
absent
1-
lengthsolid
nx
block-mesh-face-table-nxface
ny
block-mesh-face-table-nyface
nz
block-mesh-face-table-nzface
corners
block-mesh-face-table-cornersface
uvs
block-mesh-face-table-uvsface
xstep1
ystepwidth
nstep
+
*nxxstep
*nyystep
*nzzstep
tile
coerce
aref
block-mesh-kind-tables-tilestables
+
*6palette-index
face-index
'single-float
emission
aref
block-mesh-kind-tables-emissiontables
palette-index

In-plane axes as the fragment stage chooses them: U is X unless the normal is X (then Z); V is Y unless the normal is Y (then Z).

ustep
if
zeropnx
xstepzstep
vstep
if
zeropny
ystepzstep

The two side axes a corner leans toward, in ascending axis order.

s1step
if
zeropnx
xstepystep
s2step
if
zeropnz
zstepystep
s1axis
if
zeropnx
01
s2axis
if
zeropnz
21
declare
type
simple-arrayfixnum
12
corners
type
simple-arrayfixnum
8
uvs
type
integer-11
nxnynz
type
unsigned-byte16
absent
typeblock-mesh-halo-stepxstepystepzstepnstepustepvsteps1steps2step
typesingle-floattileemissionvariation
flet
solid-at
offset
=1
sbitsolid
arefindicesoffset
emit
value
declare
typesingle-floatvalue
setf
arefverticesfill
value
incffill
declare
inlinesolid-atemit
flet
declare
inlineedge
let*
edge-u-low
edge
-ustep
edge-u-high
edgeustep
edge-v-low
edge
-vstep
edge-v-high
edgevstep
edge-code
coerce
+
*
+edge-u-low1
27
*
+edge-u-high1
9
*
+edge-v-low1
3
+edge-v-high1
'single-float
fnx
coercenx'single-float
fny
coerceny'single-float
fnz
coercenz'single-float
declare
typesingle-floatedge-u-lowedge-u-highedge-v-lowedge-v-highedge-code
macrolet
corner-vertex
i
`
let*
cx
arefcorners
*3,i
cy
arefcorners
+1
*3,i
cz
arefcorners
+2
*3,i
c1
ecases1axis
0cx
1cy
c2
ecases2axis
1cy
2cz
sign1
if
zeropc1
-s1step
s1step
sign2
if
zeropc2
-s2step
s2step
local-u
arefuvs
*2,i
local-v
arefuvs
+1
*2,i
declare
type
integer01
cxcyczc1c2local-ulocal-v
multiple-value-bind
aosky-levelblock-level
block-mesh-corner-shade-and-lightsolidindicesskyblock-lightabsentbasenstepsign1sign2
declare
typesingle-floataosky-levelblock-level
emit
coerce
+xcx
'single-float
emit
coerce
+ycy
'single-float
emit
coerce
+zcz
'single-float
emit
coerce
/
+0.5
*local-u15
16
'single-float
emit
coerce
/
+0.5
*local-v15
16
'single-float
emit
*variationao
emitfnx
emitfny
emitfnz
emitsky-level
emitblock-level
emitemission
emittile
emitedge-code
corner-vertex0
corner-vertex1
corner-vertex2
corner-vertex0
corner-vertex2
corner-vertex3
fill
defunmesh-block-halo
mesherpaletteindicesskyblock-lightwidthheightdepthorigin-xorigin-yorigin-z

Mesh the interior of one halo'd dense chunk.

indices, sky and block-light are the halo columns whose dimensions are width x height x depth (the chunk plus one cell on every side); the origin is the world coordinate of the chunk's first interior cell.

declare
type
simple-array
unsigned-byte16
indices
type
simple-array
unsigned-byte8
skyblock-light
typefixnumorigin-xorigin-yorigin-z
optimize
speed3
safety1
when
eq:error
exposed-face-mesher-absent-neighbor-policymesher
when
findindices
error"Meshing reached absent terrain around chunk at (~D ~D ~D)."origin-xorigin-yorigin-z
let*
solid
block-mesh-kind-tables-solidtables
xstep1
ystepwidth
interior
masks
make-arrayinterior:element-type'
unsigned-byte8
:initial-element0
face-count0
declare
typefixnuminteriorface-count
flet
solid-at
offset
=1
sbitsolid
arefindicesoffset
declare
inlinesolid-at

Pass one: exposed-face masks and the exact face count.

let
declare
typefixnumsite
loopforlzof-typeblock-mesh-halo-extentfrom1below
1-depth
do
loopforlyof-typeblock-mesh-halo-extentfrom1belowdo
loopforlxof-typeblock-mesh-halo-extentfrom1belowdo
let
base
when
solid-atbase
let
mask0
declare
type
unsigned-byte8
mask

*BLOCK-FACES* order: -x +x -y +y -z +z.

unless
solid-at
-basexstep
setfmask
logiormask1
unless
solid-at
+basexstep
setfmask
logiormask2
unless
solid-at
-baseystep
setfmask
logiormask4
unless
solid-at
+baseystep
setfmask
logiormask8
unless
solid-at
-basezstep
setfmask
logiormask16
unless
solid-at
+basezstep
setfmask
logiormask32
incfface-count
logcountmask
setf
arefmaskssite
mask
incfsite

Pass two: emit every exposed face into an exactly sized array.

let
vertices
make-array:element-type'single-float
fill0
declare
typefixnumfillsite
loopforlzof-typeblock-mesh-halo-extentfrom1below
1-depth
do
loopforlyof-typeblock-mesh-halo-extentfrom1belowdo
loopforlxof-typeblock-mesh-halo-extentfrom1belowdo
let
mask
arefmaskssite
unless
zeropmask
let
base
dotimes
face-index6
when
logbitpface-indexmask
setffill
emit-block-mesh-facetablesfacesverticesfillsolidindicesskyblock-lightwidthheightdepthbase
arefindicesbase
face-index
+origin-x
1-lx
+origin-y
1-ly
+origin-z
1-lz
incfsite
assert
=fill
lengthvertices
make-instance'block-mesh:verticesvertices:vertex-count:face-countface-count
defunmesh-block-snapshot-halo
meshersnapshot
let*
domain
block-mesh-snapshot-domainsnapshot
shape
voxel-space-chunk-shape
chunk-domain-spacedomain
multiple-value-bind
origin-xorigin-yorigin-z
mesh-block-halomesher
block-mesh-snapshot-palettesnapshot
+2
chunk-shape-widthshape
+2
chunk-shape-heightshape
+2
chunk-shape-depthshape
origin-xorigin-yorigin-z
defmethodmesh-block-chunk

Mesh a live chunk by gathering it into a halo snapshot first.

Owner-side and worker-side meshing share one dense loop, so they are equal by construction rather than by parallel maintenance.

defunmake-block-mesh-snapshot
worldchunkdependency-stamp

Copy chunk and its one-cell halo into immutable worker-owned columns.

Every resident chunk's indices are already offsets under world's vocabulary, so the halo is gathered by plain slab copies from the up to 27 neighbours, with no per-cell decomposition, translation, or block object lookup. The vocabulary's members are frozen into the snapshot by copying; their count is the index written for absent halo samples.

let*
domain
block-chunk-domainchunk
shape
voxel-space-chunk-shape
chunk-domain-spacedomain
width
chunk-shape-widthshape
height
chunk-shape-heightshape
depth
chunk-shape-depthshape
sample-width
sample-height
sample-depth
+depth2
vocabulary
block-world-vocabularyworld
palette
coerce'simple-vector
content-definition
fields:materialized-field-definition
block-chunk-contentchunk
:block-content
light-field
block-chunk-light-fieldchunk
sky-definition
iflight-field
block-light-definition
iflight-field
halo-fields
make-block-mesh-halo-fieldshalo-domain:declarations`
sky-level.,sky-definition
block-level.,block-light-definition
neighborhood
declare
type
integer14096
widthheightdepthsample-widthsample-heightsample-depth
unless
<absent#x10000
error"The block vocabulary is too large to leave an absent index."
records:with-columnar-materialization-storage
borrowed-domainextentrow
sample-indicescontent-index
sky-samplessky-level
block-light-samplesblock-level
halo-fieldsblock-mesh-halo-fields
declare
ignorerow
assert
eqborrowed-domainhalo-domain
assert
=extent
*sample-widthsample-heightsample-depth
check-typesample-indices
simple-array
unsigned-byte16
check-typesky-samples
simple-array
unsigned-byte8
check-typeblock-light-samples
simple-array
unsigned-byte8
fillsample-indicesabsent
flet
slab
dxdydz
"Copy neighbour (DX DY DZ)'s cells that fall inside the halo."
let
neighbour
aref
block-mesh-neighborhood-chunksneighborhood
whenneighbour
with-block-content-storage
neighbour-domainneighbour-paletteneighbour-indices
neighbour
declare
ignoreneighbour-domain
unless
eqneighbour-palette
error"Chunk ~S is not under world ~S's vocabulary."neighbourworld
check-typeneighbour-indices
simple-array
unsigned-byte16
let*
field
block-chunk-light-fieldneighbour

Which local range of the neighbour lands where.

lx0
if
=dx-1
0
lx1
if
=dx1
0
ly0
if
=dy-1
0
ly1
if
=dy1
0
lz0
if
=dz-1
1-depth
0
lz1
if
=dz1
0
1-depth

Halo sample coordinate of local (lx0 ly0 lz0).

sx0
1+
+lx0
sy0
1+
sz0
1+
+
*dzdepth
lz0
run
1+
-lx1lx0
declare
type
ornull
simple-array
unsigned-byte8
skyblock-light
typefixnumlx0lx1ly0ly1lz0lz1sx0sy0sz0run
loopforlzof-typefixnumfromlz0tolz1forszof-typefixnumfromsz0do
loopforlyof-typefixnumfromly0toly1forsyof-typefixnumfromsy0do
let
source
target
+sx0
*sample-width
+sy
*sample-heightsz
declare
typefixnumsourcetarget
replacesample-indicesneighbour-indices:start1target:end1
+targetrun
:start2source
whensky
replacesky-samplessky:start1target:end1
+targetrun
:start2source
replaceblock-light-samplesblock-light:start1target:end1
+targetrun
:start2source
loopfordzfrom-1to1do
loopfordyfrom-1to1do
loopfordxfrom-1to1do
slabdxdydz
make-instance'block-mesh-snapshot:key:dependency-stampdependency-stamp:domaindomain:content-definitioncontent-definition:palettepalette:halo-fieldshalo-fields