diff options
Diffstat (limited to 'chunks.ha')
-rw-r--r-- | chunks.ha | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/chunks.ha b/chunks.ha new file mode 100644 index 0000000..af78281 --- /dev/null +++ b/chunks.ha @@ -0,0 +1,221 @@ +use comparray; + +type Chunk = struct { + sections: nullable *[*]Section, +}; + +type Section = struct { + bstates: comparray::Array, + dirty: bool, + non_air_count: i32, + + // render state + + vertexbuf: uint, + vertexcount: i32, +}; + +// XXX: the view range behaves a bit weirdly sometimes; further investigation +// needed. +def VIEW_MARGIN: i32 = 2; + +let CHUNKS: []Chunk = []; // len = VIEW_SIZE * VIEW_SIZE +let CHUNKS_POS: ChunkPos = ChunkPos { ... }; // -X,-Z corner of the rectangular view area +let CHUNKS_SIZE = 0i32; // width of the view area +let CHUNKS_MIN_Y = 0i8; // in chunks +let CHUNKS_HEIGHT = 0u8; // in chunks + +fn chunks_respawn() void = { + const dim_type = &DIM_TYPES[DIM_TYPE]; + CHUNKS_MIN_Y = dim_type.min_y; + CHUNKS_HEIGHT = dim_type.height; + setview(ChunkPos { ... }); +}; + +fn setview(center: ChunkPos) void = { + const view_pos = ChunkPos { + x = center.x - VIEW_DISTANCE - VIEW_MARGIN, + z = center.z - VIEW_DISTANCE - VIEW_MARGIN, + }; + const view_size = VIEW_DISTANCE * 2 + VIEW_MARGIN * 2 + 1; + let chunks = alloc([Chunk { ... }...], + (view_size * view_size): size); + + for (let z = 0i32; z < CHUNKS_SIZE; z += 1) + for (let x = 0i32; x < CHUNKS_SIZE; x += 1) { + const pos = ChunkPos { + x = CHUNKS_POS.x + x, + z = CHUNKS_POS.z + z, + }; + const chunk = getchunk(pos) as *Chunk; + + const pos_ = ChunkPos { + x = pos.x - view_pos.x, + z = pos.z - view_pos.z, + }; + if (pos_.x >= 0 && pos_.x < view_size + && pos_.z >= 0 && pos_.z < view_size) { + chunks[pos_.x + pos_.z * view_size] = *chunk; + } else { + chunk_destroy(pos); + }; + }; + free(CHUNKS); + + CHUNKS = chunks; + CHUNKS_POS = view_pos; + CHUNKS_SIZE = view_size; +}; + +fn setblock(pos: BlockPos, bstate: u16) void = { + const spos = block_section(pos); + const section = getsection(spos) as *Section; + + const ref = if (section.bstates.palette_size == comparray::DIRECT) { + yield bstate; + } else :ref { + const palette = comparray::get_palette(§ion.bstates); + for (let i = 0u8; i < len(palette); i += 1) { + if (palette[i] == bstate) + yield :ref, i: u16; + }; + + comparray::grow_palette(§ion.bstates, 4096, + section.bstates.palette_size + 1); + + if (section.bstates.palette_size == comparray::DIRECT) { + yield :ref, bstate; + } else { + const i = section.bstates.palette_size - 1; + comparray::get_palette(§ion.bstates)[i] = bstate; + yield :ref, i: u16; + }; + }; + + const i = (pos.x & 0xf): size + | (pos.z & 0xf): size << 4 + | (pos.y & 0xf): size << 8; + comparray::set(§ion.bstates, i, ref); + + section.dirty = true; + if (pos.x & 0xf == 0) section_make_dirty(spos.x - 1, spos.y, spos.z); + if (pos.x & 0xf == 15) section_make_dirty(spos.x + 1, spos.y, spos.z); + if (pos.y & 0xf == 0) section_make_dirty(spos.x, spos.y - 1, spos.z); + if (pos.y & 0xf == 15) section_make_dirty(spos.x, spos.y + 1, spos.z); + if (pos.z & 0xf == 0) section_make_dirty(spos.x, spos.y, spos.z - 1); + if (pos.z & 0xf == 15) section_make_dirty(spos.x, spos.y, spos.z + 1); +}; + +fn getchunk(pos: ChunkPos) nullable *Chunk = { + pos.x -= CHUNKS_POS.x; + pos.z -= CHUNKS_POS.z; + + if (pos.x < 0 || pos.x >= CHUNKS_SIZE) return null; + if (pos.z < 0 || pos.z >= CHUNKS_SIZE) return null; + + return &CHUNKS[pos.x + pos.z * CHUNKS_SIZE]; +}; + +fn getsection(pos: SectionPos) nullable *Section = { + if (pos.y < CHUNKS_MIN_Y || pos.y >= CHUNKS_MIN_Y + CHUNKS_HEIGHT: i8) + return null; + + match (getchunk(pos.chunk)) { + case let chunk: *Chunk => + match (chunk.sections) { + case let sections: *[*]Section => + return §ions[pos.y: size - CHUNKS_MIN_Y: size]; + case null => + return null; + }; + case null => + return null; + }; +}; + +fn chunk_init(pos: ChunkPos) *Chunk = { + chunk_destroy(pos); + + let sections: []Section = alloc([], CHUNKS_HEIGHT); + + for (let i = 0z; i < CHUNKS_HEIGHT; i += 1) { + let bstates = comparray::new(1, 4096); + comparray::clear(&bstates, 0); + + static append(sections, Section { + bstates = bstates, + ... + }); + }; + + const chunk = getchunk(pos) as *Chunk; + *chunk = Chunk { + sections = sections: *[*]Section, + }; + + for (let i = 0u8; i < CHUNKS_HEIGHT; i += 1) { + const spos = SectionPos { + chunk = pos, + y = i: i8 + CHUNKS_MIN_Y, + }; + section_make_dirty(spos.x - 1, spos.y, spos.z); + section_make_dirty(spos.x + 1, spos.y, spos.z); + section_make_dirty(spos.x, spos.y, spos.z - 1); + section_make_dirty(spos.x, spos.y, spos.z + 1); + }; + + return chunk; +}; + +fn section_make_dirty(x: i32, y: i8, z: i32) void = { + match (getsection(SectionPos { x = x, y = y, z = z })) { + case let section: *Section => + section.dirty = true; + case null => void; + }; +}; + +fn chunk_destroy(pos: ChunkPos) void = { + const chunk = match (getchunk(pos)) { + case let chunk: *Chunk => + yield chunk; + case null => + return; + }; + + const sections = match (chunk.sections) { + case let sections: *[*]Section => + yield sections; + case null => + return; + }; + + for (let i = 0u8; i < CHUNKS_HEIGHT; i += 1) { + section_destroy(§ions[i]); + }; + + free(sections); + chunk.sections = null; +}; + +fn section_destroy(section: *Section) void = { + render_chunks_section_destroy(section); + comparray::clear(§ion.bstates, 0); +}; + +fn chunks_despawn() void = { + for (let z = 0i32; z < CHUNKS_SIZE; z += 1) + for (let x = 0i32; x < CHUNKS_SIZE; x += 1) { + const pos = ChunkPos { + x = CHUNKS_POS.x + x, + z = CHUNKS_POS.z + z, + }; + chunk_destroy(pos); + }; + free(CHUNKS); + CHUNKS = []; + CHUNKS_POS = ChunkPos { ... }; + CHUNKS_SIZE = 0; + CHUNKS_MIN_Y = 0; + CHUNKS_HEIGHT = 0; +}; |