|
|
|
|
@ -4,6 +4,7 @@ const Layer = @import("Layer.zig");
|
|
|
|
|
const Color = @import("Color.zig");
|
|
|
|
|
const Recti = @import("geometry/Recti.zig");
|
|
|
|
|
const Vec2f = @import("geometry/Vec2f.zig");
|
|
|
|
|
const Vec2i = @import("geometry/Vec2i.zig");
|
|
|
|
|
const Entity = @import("Entity.zig");
|
|
|
|
|
const Scene = @import("Scene.zig");
|
|
|
|
|
const Camera = @import("Camera.zig");
|
|
|
|
|
@ -19,52 +20,16 @@ const CHUNK_LENGTH = CHUNK_SIZE * CHUNK_SIZE;
|
|
|
|
|
|
|
|
|
|
const tile_size: usize = 16;
|
|
|
|
|
|
|
|
|
|
pub inline fn xy_to_index(x: i32, y: i32) i32 {
|
|
|
|
|
return x + y * CHUNK_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub inline fn index_to_xy(index: i32) struct { i32, i32 } {
|
|
|
|
|
const y: i32 = @divFloor(index, CHUNK_SIZE);
|
|
|
|
|
const x: i32 = @rem(index, CHUNK_SIZE);
|
|
|
|
|
return .{ x, y };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GROWS = true;
|
|
|
|
|
const GROWTH_RATE: f32 = 0.5;
|
|
|
|
|
|
|
|
|
|
pub const Tile = struct {
|
|
|
|
|
texture_idx: usize,
|
|
|
|
|
growth: f32,
|
|
|
|
|
stone_decoration: f32,
|
|
|
|
|
grass_color: Color,
|
|
|
|
|
dirt_color: Color,
|
|
|
|
|
stone_color: Color,
|
|
|
|
|
|
|
|
|
|
pub const NULL = Tile {
|
|
|
|
|
.texture_idx = 0,
|
|
|
|
|
.growth = 0,
|
|
|
|
|
.stone_decoration = 0,
|
|
|
|
|
.grass_color = Color.WHITE,
|
|
|
|
|
.dirt_color = Color.WHITE,
|
|
|
|
|
.stone_color = Color.WHITE,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tiles: []Tile = undefined,
|
|
|
|
|
|
|
|
|
|
// the noise for deciding random textures
|
|
|
|
|
texture_noise: Noise,
|
|
|
|
|
// noise for deciding grass growth
|
|
|
|
|
growth_noise: Noise,
|
|
|
|
|
// noise for stone decoration placement
|
|
|
|
|
stone_decoration_noise: Noise.Custom,
|
|
|
|
|
// noise for colors
|
|
|
|
|
dirt_color_noise: Noise.Color,
|
|
|
|
|
grass_color_noise: Noise.Color,
|
|
|
|
|
stone_color_noise: Noise.Color,
|
|
|
|
|
|
|
|
|
|
chunks: std.ArrayList(*Chunk),
|
|
|
|
|
generator: WorldGenerator,
|
|
|
|
|
|
|
|
|
|
camera: *Camera = undefined,
|
|
|
|
|
scene: ?*Scene = null,
|
|
|
|
|
|
|
|
|
|
pub fn entity(self: *Terrain) Entity {
|
|
|
|
|
return Entity.init(self, .{
|
|
|
|
|
@ -72,9 +37,12 @@ pub fn entity(self: *Terrain) Entity {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn start(self: *Terrain, scene: *Scene) void {
|
|
|
|
|
pub fn start(self: *Terrain, scene: *Scene) !void {
|
|
|
|
|
self.camera = scene.get(Camera.TAG, Camera);
|
|
|
|
|
self.camera.set_focus(Vec2f.create(CHUNK_SIZE / 2, CHUNK_SIZE / 2));
|
|
|
|
|
// self.camera.set_focus(Vec2f.create(CHUNK_SIZE / 2, CHUNK_SIZE / 2));
|
|
|
|
|
self.scene = scene;
|
|
|
|
|
|
|
|
|
|
for (self.chunks.items) |chunk| try scene.add(chunk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn contrast(n: f32, blend_strength: f32) f32 {
|
|
|
|
|
@ -89,107 +57,72 @@ fn contrast(n: f32, blend_strength: f32) f32 {
|
|
|
|
|
return (unadjusted_total + 1.0) / 2.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_tiles(self: *Terrain) void {
|
|
|
|
|
for(0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
const x, const y = index_to_xy(@intCast(idx));
|
|
|
|
|
const fx: f32 = @floatFromInt(x);
|
|
|
|
|
const fy: f32 = @floatFromInt(y);
|
|
|
|
|
const texture_random = self.texture_noise.get(fx, fy);
|
|
|
|
|
self.tiles[idx] = Tile {
|
|
|
|
|
.texture_idx = @intFromFloat(@floor(texture_random * 4)),
|
|
|
|
|
.growth = contrast(self.growth_noise.get(fx, fy), 10),
|
|
|
|
|
.stone_decoration = self.stone_decoration_noise.get(fx, fy),
|
|
|
|
|
.dirt_color = self.dirt_color_noise.get(fx, fy),
|
|
|
|
|
.grass_color = self.grass_color_noise.get(fx, fy),
|
|
|
|
|
.stone_color = self.stone_color_noise.get(fx, fy),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
fn gen_chunk(self: *Terrain, chunk_pos: Vec2i) !void {
|
|
|
|
|
std.debug.print("[Terrain:gen_chunk] {}\n", .{ chunk_pos });
|
|
|
|
|
const chunk = try Chunk.generate(chunk_pos, &self.generator);
|
|
|
|
|
try self.chunks.append(chunk);
|
|
|
|
|
if (self.scene != null) try self.scene.?.add(chunk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(seed: i64) !*Terrain {
|
|
|
|
|
var self: *Terrain = try Scene.EntityPool.allocate(Terrain {
|
|
|
|
|
.tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL),
|
|
|
|
|
.growth_noise = Noise.create(seed, 10, 0.03),
|
|
|
|
|
.texture_noise = Noise.create(seed + 1, 1, 3),
|
|
|
|
|
.stone_decoration_noise = try Noise.Custom.create(seed + 2, 1, &.{
|
|
|
|
|
.{ .frequency = 0.03, .amplitude = 1 },
|
|
|
|
|
.{ .frequency = 1, .amplitude = 1 }
|
|
|
|
|
}),
|
|
|
|
|
.dirt_color_noise = try Noise.Color.create(seed + 3, Noise.ColorRange.DIRT),
|
|
|
|
|
.grass_color_noise = try Noise.Color.create(seed + 4, Noise.ColorRange.GRASS),
|
|
|
|
|
.stone_color_noise = try Noise.Color.create(seed + 5, Noise.ColorRange.STONE)
|
|
|
|
|
.chunks = std.ArrayList(*Chunk).init(std.heap.page_allocator),
|
|
|
|
|
.generator = try WorldGenerator.create(seed),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.generate_tiles();
|
|
|
|
|
try self.gen_chunk(Vec2i.ZERO);
|
|
|
|
|
try self.gen_chunk(Vec2i { .x = -1, .y = 0 });
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn destroy(self: *const Terrain) void {
|
|
|
|
|
self.texture_noise.destroy();
|
|
|
|
|
self.growth_noise.destroy();
|
|
|
|
|
self.stone_decoration_noise.destroy();
|
|
|
|
|
|
|
|
|
|
self.dirt_color_noise.destroy();
|
|
|
|
|
self.grass_color_noise.destroy();
|
|
|
|
|
self.stone_color_noise.destroy();
|
|
|
|
|
|
|
|
|
|
Entity.free_array(self.tiles);
|
|
|
|
|
self.generator.destroy();
|
|
|
|
|
self.chunks.deinit();
|
|
|
|
|
Scene.EntityPool.deallocate(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn chance(percent: f32) bool {
|
|
|
|
|
return prng.random().float(f32) < percent;
|
|
|
|
|
}
|
|
|
|
|
// fn chance(percent: f32) bool {
|
|
|
|
|
// return prng.random().float(f32) < percent;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
fn grow(self: *Terrain, x: i32, y: i32, amt: f32) void {
|
|
|
|
|
if (x < 0 or x >= CHUNK_SIZE) return;
|
|
|
|
|
if (y < 0 or y >= CHUNK_SIZE) return;
|
|
|
|
|
const idx: usize = @intCast(xy_to_index(x, y));
|
|
|
|
|
const tile = &self.tiles[idx];
|
|
|
|
|
// @compileLog(tile);
|
|
|
|
|
tile.*.growth = @min(@max(tile.growth + amt, 0), 1);
|
|
|
|
|
}
|
|
|
|
|
// fn grow(self: *Terrain, x: i32, y: i32, amt: f32) void {
|
|
|
|
|
// if (x < 0 or x >= CHUNK_SIZE) return;
|
|
|
|
|
// if (y < 0 or y >= CHUNK_SIZE) return;
|
|
|
|
|
// const idx: usize = @intCast(xy_to_index(x, y));
|
|
|
|
|
// const tile = &self.tiles[idx];
|
|
|
|
|
// // @compileLog(tile);
|
|
|
|
|
// tile.*.growth = @min(@max(tile.growth + amt, 0), 1);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
pub fn update(self: *Terrain, dt: f32) void {
|
|
|
|
|
if (GROWS) {
|
|
|
|
|
for (0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
const x, const y = index_to_xy(@intCast(idx));
|
|
|
|
|
const tile = self.tiles[idx];
|
|
|
|
|
const should_grow = chance(dt * GROWTH_RATE) and chance(tile.growth);
|
|
|
|
|
if (should_grow) {
|
|
|
|
|
self.grow(x, y, 0.010);
|
|
|
|
|
|
|
|
|
|
self.grow(x + 1, y, 0.008);
|
|
|
|
|
self.grow(x - 1, y, 0.008);
|
|
|
|
|
self.grow(x, y + 1, 0.008);
|
|
|
|
|
self.grow(x, y - 1, 0.008);
|
|
|
|
|
|
|
|
|
|
self.grow(x + 1, y + 1, 0.004);
|
|
|
|
|
self.grow(x + 1, y - 1, 0.004);
|
|
|
|
|
self.grow(x - 1, y + 1, 0.004);
|
|
|
|
|
self.grow(x - 1, y - 1, 0.004);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// pub fn update(self: *Terrain, dt: f32) void {
|
|
|
|
|
// if (GROWS) {
|
|
|
|
|
// for (0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
// const x, const y = index_to_xy(@intCast(idx));
|
|
|
|
|
// const tile = self.tiles[idx];
|
|
|
|
|
// const should_grow = chance(dt * GROWTH_RATE) and chance(tile.growth);
|
|
|
|
|
// if (should_grow) {
|
|
|
|
|
// self.grow(x, y, 0.010);
|
|
|
|
|
//
|
|
|
|
|
// self.grow(x + 1, y, 0.008);
|
|
|
|
|
// self.grow(x - 1, y, 0.008);
|
|
|
|
|
// self.grow(x, y + 1, 0.008);
|
|
|
|
|
// self.grow(x, y - 1, 0.008);
|
|
|
|
|
//
|
|
|
|
|
// self.grow(x + 1, y + 1, 0.004);
|
|
|
|
|
// self.grow(x + 1, y - 1, 0.004);
|
|
|
|
|
// self.grow(x - 1, y + 1, 0.004);
|
|
|
|
|
// self.grow(x - 1, y - 1, 0.004);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn draw(self: *const Terrain) void {
|
|
|
|
|
for(0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
const tile = self.tiles[idx];
|
|
|
|
|
const x, const y = index_to_xy(@intCast(idx));
|
|
|
|
|
const rect = Recti.from_xywh(x, y, 1, 1);
|
|
|
|
|
const sprite = assets.terrain[0][tile.texture_idx];
|
|
|
|
|
self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, tile.grass_color);
|
|
|
|
|
// switch(tile.texture_idx) {
|
|
|
|
|
// 0 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.WHITE),
|
|
|
|
|
// 1 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.RED),
|
|
|
|
|
// 2 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.GREEN),
|
|
|
|
|
// 3 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.BLUE),
|
|
|
|
|
// else => {}
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// pub fn draw(self: *const Terrain) void {
|
|
|
|
|
// for (self.chunks.items) |chunk| {
|
|
|
|
|
// chunk.draw... we dont need this. chunks draw themselves...
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// pub fn draw_opacity(self: *const Terrain) void {
|
|
|
|
|
// for(0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
@ -205,3 +138,149 @@ pub fn draw(self: *const Terrain) void {
|
|
|
|
|
// self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, color);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Chunk = struct {
|
|
|
|
|
pub const Tile = struct {
|
|
|
|
|
texture_idx: usize,
|
|
|
|
|
growth: f32,
|
|
|
|
|
stone_decoration: f32,
|
|
|
|
|
grass_color: Color,
|
|
|
|
|
dirt_color: Color,
|
|
|
|
|
stone_color: Color,
|
|
|
|
|
|
|
|
|
|
pub const NULL = Tile {
|
|
|
|
|
.texture_idx = 0,
|
|
|
|
|
.growth = 0,
|
|
|
|
|
.stone_decoration = 0,
|
|
|
|
|
.grass_color = Color.WHITE,
|
|
|
|
|
.dirt_color = Color.WHITE,
|
|
|
|
|
.stone_color = Color.WHITE,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tiles: []Tile = undefined,
|
|
|
|
|
chunk_pos: Vec2i,
|
|
|
|
|
chunk_pos_world: Vec2i,
|
|
|
|
|
|
|
|
|
|
camera: *Camera = undefined,
|
|
|
|
|
|
|
|
|
|
pub fn generate(chunk_pos: Vec2i, generator: *const WorldGenerator) !*Chunk {
|
|
|
|
|
var self = try Scene.EntityPool.allocate(Chunk {
|
|
|
|
|
.chunk_pos = chunk_pos,
|
|
|
|
|
.chunk_pos_world = Vec2i {
|
|
|
|
|
.x = chunk_pos.x * CHUNK_SIZE,
|
|
|
|
|
.y = chunk_pos.y * CHUNK_SIZE,
|
|
|
|
|
},
|
|
|
|
|
.tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.generate_tiles(generator);
|
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn destroy(self: *const Chunk) void {
|
|
|
|
|
Entity.free_array(self.tiles);
|
|
|
|
|
Scene.EntityPool.deallocate(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn entity(self: *Chunk) Entity {
|
|
|
|
|
return Entity.init(self, .{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn start(self: *Chunk, scene: *Scene) void {
|
|
|
|
|
self.camera = scene.get(Camera.TAG, Camera);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_tiles(self: *Chunk, generator: *const WorldGenerator) void {
|
|
|
|
|
for(0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
const x, const y = index_to_xy(@intCast(idx));
|
|
|
|
|
const abs_pos = self.chunk_pos.add(Vec2i.create(x, y));
|
|
|
|
|
const tile = generator.gen_tile(abs_pos);
|
|
|
|
|
|
|
|
|
|
self.tiles[idx] = tile;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn draw(self: *const Chunk) void {
|
|
|
|
|
for(0..CHUNK_LENGTH) |idx| {
|
|
|
|
|
const tile = self.tiles[idx];
|
|
|
|
|
const x, const y = index_to_xy(@intCast(idx));
|
|
|
|
|
const rect = Recti.from_xywh(x, y, 1, 1);
|
|
|
|
|
const sprite = assets.terrain[0][tile.texture_idx];
|
|
|
|
|
self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, tile.grass_color);
|
|
|
|
|
// switch(tile.texture_idx) {
|
|
|
|
|
// 0 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.WHITE),
|
|
|
|
|
// 1 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.RED),
|
|
|
|
|
// 2 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.GREEN),
|
|
|
|
|
// 3 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.BLUE),
|
|
|
|
|
// else => {}
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// some helper functions
|
|
|
|
|
pub inline fn xy_to_index(x: i32, y: i32) i32 {
|
|
|
|
|
return x + y * CHUNK_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub inline fn index_to_xy(index: i32) struct { i32, i32 } {
|
|
|
|
|
const y: i32 = @divFloor(index, CHUNK_SIZE);
|
|
|
|
|
const x: i32 = @rem(index, CHUNK_SIZE);
|
|
|
|
|
return .{ x, y };
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const WorldGenerator = struct {
|
|
|
|
|
|
|
|
|
|
// the noise for deciding random textures
|
|
|
|
|
texture_noise: Noise,
|
|
|
|
|
// noise for deciding grass growth
|
|
|
|
|
growth_noise: Noise,
|
|
|
|
|
// noise for stone decoration placement
|
|
|
|
|
stone_decoration_noise: Noise.Custom,
|
|
|
|
|
// noise for colors
|
|
|
|
|
dirt_color_noise: Noise.Color,
|
|
|
|
|
grass_color_noise: Noise.Color,
|
|
|
|
|
stone_color_noise: Noise.Color,
|
|
|
|
|
|
|
|
|
|
pub fn create(seed: i64) !WorldGenerator {
|
|
|
|
|
return WorldGenerator {
|
|
|
|
|
.growth_noise = Noise.create(seed, 10, 0.03),
|
|
|
|
|
.texture_noise = Noise.create(seed + 1, 1, 3),
|
|
|
|
|
.stone_decoration_noise = try Noise.Custom.create(seed + 2, 1, &.{
|
|
|
|
|
.{ .frequency = 0.03, .amplitude = 1 },
|
|
|
|
|
.{ .frequency = 1, .amplitude = 1 }
|
|
|
|
|
}),
|
|
|
|
|
.dirt_color_noise = try Noise.Color.create(seed + 3, Noise.ColorRange.DIRT),
|
|
|
|
|
.grass_color_noise = try Noise.Color.create(seed + 4, Noise.ColorRange.GRASS),
|
|
|
|
|
.stone_color_noise = try Noise.Color.create(seed + 5, Noise.ColorRange.STONE)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn destroy(self: *const WorldGenerator) void {
|
|
|
|
|
self.texture_noise.destroy();
|
|
|
|
|
self.growth_noise.destroy();
|
|
|
|
|
self.stone_decoration_noise.destroy();
|
|
|
|
|
|
|
|
|
|
self.dirt_color_noise.destroy();
|
|
|
|
|
self.grass_color_noise.destroy();
|
|
|
|
|
self.stone_color_noise.destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn gen_tile(self: *const WorldGenerator, position: Vec2i) Chunk.Tile {
|
|
|
|
|
const fx: f32 = @floatFromInt(position.x);
|
|
|
|
|
const fy: f32 = @floatFromInt(position.y);
|
|
|
|
|
const texture_random = self.texture_noise.get(fx, fy);
|
|
|
|
|
return Chunk.Tile {
|
|
|
|
|
.texture_idx = @intFromFloat(@floor(texture_random * 4)),
|
|
|
|
|
.growth = contrast(self.growth_noise.get(fx, fy), 10),
|
|
|
|
|
.stone_decoration = self.stone_decoration_noise.get(fx, fy),
|
|
|
|
|
.dirt_color = self.dirt_color_noise.get(fx, fy),
|
|
|
|
|
.grass_color = self.grass_color_noise.get(fx, fy),
|
|
|
|
|
.stone_color = self.stone_color_noise.get(fx, fy),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|