From d2bc6e6428748525f7a3566fe169a504b63ce0f0 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Tue, 2 Jul 2024 10:23:31 +0200 Subject: [PATCH] Allow configuring leaf radius and tree height independently. fixes #519 --- assets/cubyz/biomes/bush_lands.json | 8 +- assets/cubyz/biomes/forest.json | 6 +- assets/cubyz/biomes/glass_forest.json | 120 ++++++++++++------ assets/cubyz/biomes/island.json | 4 +- assets/cubyz/biomes/rainbow_forest.json | 48 ++++--- .../terrain/structures/SimpleTreeModel.zig | 58 +++------ 6 files changed, 142 insertions(+), 102 deletions(-) diff --git a/assets/cubyz/biomes/bush_lands.json b/assets/cubyz/biomes/bush_lands.json index b6abbc24..89b9e2c7 100644 --- a/assets/cubyz/biomes/bush_lands.json +++ b/assets/cubyz/biomes/bush_lands.json @@ -22,9 +22,11 @@ "log" : "cubyz:oak_log", "top" : "cubyz:oak_top", "chance" : 0.01, - "type" : "bush", - "height" : 4, - "height_variation" : 6 + "type" : "round", + "height" : 1, + "height_variation" : 1, + "leafRadius" : 2, + "leafRadius_variation" : 1, }, { "id" : "cubyz:ground_patch", diff --git a/assets/cubyz/biomes/forest.json b/assets/cubyz/biomes/forest.json index 51573c4e..4a88f391 100644 --- a/assets/cubyz/biomes/forest.json +++ b/assets/cubyz/biomes/forest.json @@ -34,8 +34,10 @@ "top" : "cubyz:birch_top", "chance" : 0.01, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3.5, + "leafRadius_variation" : 1.5 }, { "id" : "cubyz:fallen_tree", diff --git a/assets/cubyz/biomes/glass_forest.json b/assets/cubyz/biomes/glass_forest.json index 4f437846..8af1a322 100644 --- a/assets/cubyz/biomes/glass_forest.json +++ b/assets/cubyz/biomes/glass_forest.json @@ -22,8 +22,10 @@ "top" : "cubyz:chalk/aqua", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -32,8 +34,10 @@ "top" : "cubyz:chalk/black", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -42,8 +46,10 @@ "top" : "cubyz:chalk/blue", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -52,8 +58,10 @@ "top" : "cubyz:chalk/brown", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -62,8 +70,10 @@ "top" : "cubyz:chalk/crimson", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -72,8 +82,10 @@ "top" : "cubyz:chalk/cyan", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -82,8 +94,10 @@ "top" : "cubyz:chalk/dark_grey", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -92,8 +106,10 @@ "top" : "cubyz:chalk/green", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -102,8 +118,10 @@ "top" : "cubyz:chalk/grey", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -112,8 +130,10 @@ "top" : "cubyz:chalk/indigo", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -122,8 +142,10 @@ "top" : "cubyz:chalk/lime", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -132,8 +154,10 @@ "top" : "cubyz:chalk/magenta", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -142,8 +166,10 @@ "top" : "cubyz:chalk/orange", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -152,8 +178,10 @@ "top" : "cubyz:chalk/pink", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -162,8 +190,10 @@ "top" : "cubyz:chalk/purple", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -172,8 +202,10 @@ "top" : "cubyz:chalk/red", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -182,8 +214,10 @@ "top" : "cubyz:chalk/violet", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -192,8 +226,10 @@ "top" : "cubyz:chalk/viridian", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -202,8 +238,10 @@ "top" : "cubyz:chalk/white", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -212,8 +250,10 @@ "top" : "cubyz:chalk/yellow", "chance" : 0.0005, "type" : "round", - "height" : 6, - "height_variation" : 3 + "height" : 10, + "height_variation" : 5, + "leafRadius" : 3, + "leafRadius_variation" : 3 }, ] } diff --git a/assets/cubyz/biomes/island.json b/assets/cubyz/biomes/island.json index c9c49aa2..4be80f6c 100644 --- a/assets/cubyz/biomes/island.json +++ b/assets/cubyz/biomes/island.json @@ -23,7 +23,9 @@ "chance" : 0.001, "type" : "round", "height" : 12, - "height_variation" : 10 + "height_variation" : 10, + "leafRadius" : 5, + "leafRadius_variation" : 6 } ], diff --git a/assets/cubyz/biomes/rainbow_forest.json b/assets/cubyz/biomes/rainbow_forest.json index b9b84758..de48308e 100644 --- a/assets/cubyz/biomes/rainbow_forest.json +++ b/assets/cubyz/biomes/rainbow_forest.json @@ -35,9 +35,11 @@ "log" : "cubyz:fog/red", "top" : "cubyz:fog/red", "chance" : 0.01, - "type" : "bush", - "height" : 32, - "height_variation" : 3 + "type" : "round", + "height" : 0, + "height_variation" : 0, + "leafRadius" : 16, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -45,9 +47,11 @@ "log" : "cubyz:fog/green", "top" : "cubyz:fog/green", "chance" : 0.01, - "type" : "bush", - "height" : 32, - "height_variation" : 3 + "type" : "round", + "height" : 0, + "height_variation" : 0, + "leafRadius" : 16, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -55,9 +59,11 @@ "log" : "cubyz:fog/blue", "top" : "cubyz:fog/blue", "chance" : 0.01, - "type" : "bush", - "height" : 32, - "height_variation" : 3 + "type" : "round", + "height" : 0, + "height_variation" : 0, + "leafRadius" : 16, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -65,9 +71,11 @@ "log" : "cubyz:fog/yellow", "top" : "cubyz:fog/yellow", "chance" : 0.01, - "type" : "bush", - "height" : 32, - "height_variation" : 3 + "type" : "round", + "height" : 0, + "height_variation" : 0, + "leafRadius" : 16, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -75,9 +83,11 @@ "log" : "cubyz:fog/cyan", "top" : "cubyz:fog/cyan", "chance" : 0.01, - "type" : "bush", - "height" : 32, - "height_variation" : 3 + "type" : "round", + "height" : 0, + "height_variation" : 0, + "leafRadius" : 16, + "leafRadius_variation" : 3 }, { "id" : "cubyz:simple_tree", @@ -85,9 +95,11 @@ "log" : "cubyz:fog/magenta", "top" : "cubyz:fog/magenta", "chance" : 0.01, - "type" : "bush", - "height" : 32, - "height_variation" : 3 + "type" : "round", + "height" : 0, + "height_variation" : 0, + "leafRadius" : 16, + "leafRadius_variation" : 3 }, ] } diff --git a/src/server/terrain/structures/SimpleTreeModel.zig b/src/server/terrain/structures/SimpleTreeModel.zig index e05a40ee..f1f3d7a2 100644 --- a/src/server/terrain/structures/SimpleTreeModel.zig +++ b/src/server/terrain/structures/SimpleTreeModel.zig @@ -18,7 +18,6 @@ const SimpleTreeModel = @This(); const Type = enum { pyramid, round, - bush, }; typ: Type, @@ -27,17 +26,24 @@ woodBlock: u16, topWoodBlock: u16, height0: i32, deltaHeight: u31, +leafRadius: f32, +deltaLeafRadius: f32, branched: bool, pub fn loadModel(arenaAllocator: NeverFailingAllocator, parameters: JsonElement) *SimpleTreeModel { const self = arenaAllocator.create(SimpleTreeModel); self.* = .{ - .typ = std.meta.stringToEnum(Type, parameters.get([]const u8, "type", "")) orelse .round, + .typ = std.meta.stringToEnum(Type, parameters.get([]const u8, "type", "")) orelse blk: { + if(parameters.get(?[]const u8, "type", null)) |typ| std.log.err("Unknown tree type \"{s}\"", .{typ}); + break :blk .round; + }, .leavesBlock = main.blocks.getByID(parameters.get([]const u8, "leaves", "cubyz:oak_leaves")), .woodBlock = main.blocks.getByID(parameters.get([]const u8, "log", "cubyz:oak_log")), .topWoodBlock = main.blocks.getByID(parameters.get([]const u8, "top", "cubyz:oak_top")), .height0 = parameters.get(i32, "height", 6), .deltaHeight = parameters.get(u31, "height_variation", 3), + .leafRadius = parameters.get(f32, "leafRadius", (1 + parameters.get(f32, "height", 6))/2), + .deltaLeafRadius = parameters.get(f32, "leafRadius_variation", parameters.get(f32, "height_variation", 3)/2), .branched = parameters.get(bool, "branched", true), }; return self; @@ -78,7 +84,9 @@ pub fn generateBranch(self: *SimpleTreeModel, x: i32, y: i32, z: i32, d: u32, ch } pub fn generate(self: *SimpleTreeModel, x: i32, y: i32, z: i32, chunk: *main.chunk.ServerChunk, caveMap: terrain.CaveMap.CaveMapView, seed: *u64) void { - var height = self.height0 + random.nextIntBounded(u31, seed, self.deltaHeight); + const factor = random.nextFloat(seed); + var height = self.height0 + @as(i32, @intFromFloat(factor*@as(f32, @floatFromInt(self.deltaHeight)))); + const leafRadius = self.leafRadius + factor*self.deltaLeafRadius; if(z + height >= caveMap.findTerrainChangeAbove(x, y, z)) // Space is too small.Allocator return; @@ -116,17 +124,16 @@ pub fn generate(self: *SimpleTreeModel, x: i32, y: i32, z: i32, chunk: *main.chu .round => { self.generateStem(x, y, z, height, chunk, seed); - const leafRadius = 1 + @divFloor(height, 2); - const floatLeafRadius = @as(f32, @floatFromInt(leafRadius)) - random.nextFloat(seed); - const radiusSqr: i32 = @intFromFloat(floatLeafRadius*floatLeafRadius); - const randomRadiusSqr: i32 = @intFromFloat((floatLeafRadius - 0.25)*(floatLeafRadius - 0.25)); + const ceilRadius: i32 = @intFromFloat(@ceil(leafRadius)); + const radiusSqr: i32 = @intFromFloat(leafRadius*leafRadius); + const randomRadiusSqr: i32 = @intFromFloat((leafRadius - 0.25)*(leafRadius - 0.25)); const center = z + height; - var pz = chunk.startIndex(center - leafRadius); - while(pz < center + leafRadius) : (pz += chunk.super.pos.voxelSize) { - var px = chunk.startIndex(x - leafRadius); - while(px < x + leafRadius) : (px += chunk.super.pos.voxelSize) { - var py = chunk.startIndex(y - leafRadius); - while(py < y + leafRadius) : (py += chunk.super.pos.voxelSize) { + var pz = chunk.startIndex(center - ceilRadius); + while(pz < center + ceilRadius) : (pz += chunk.super.pos.voxelSize) { + var px = chunk.startIndex(x - ceilRadius); + while(px < x + ceilRadius) : (px += chunk.super.pos.voxelSize) { + var py = chunk.startIndex(y - ceilRadius); + while(py < y + ceilRadius) : (py += chunk.super.pos.voxelSize) { const distSqr = (pz - center)*(pz - center) + (px - x)*(px - x) + (py - y)*(py - y); if(chunk.liesInChunk(px, py, pz) and distSqr < radiusSqr and (distSqr < randomRadiusSqr or random.nextInt(u1, seed) != 0)) { // TODO: Use another seed to make this more reliable! chunk.updateBlockIfDegradable(px, py, pz, .{.typ = self.leavesBlock, .data = 0}); // TODO: Natural standard. @@ -135,30 +142,5 @@ pub fn generate(self: *SimpleTreeModel, x: i32, y: i32, z: i32, chunk: *main.chu } } }, - .bush => { - const oldHeight = height; - height = @min(2, height); // Make sure the stem of the bush stays small. - - self.generateStem(x, y, z, height, chunk, seed); - - const leafRadius = 1 + @divFloor(oldHeight, 2); - const floatLeafRadius = @as(f32, @floatFromInt(leafRadius)) - random.nextFloat(seed); - const radiusSqr: i32 = @intFromFloat(floatLeafRadius*floatLeafRadius); - const randomRadiusSqr: i32 = @intFromFloat((floatLeafRadius - 0.25)*(floatLeafRadius - 0.25)); - const center = z + height; - var pz = chunk.startIndex(center - leafRadius); - while(pz < center + leafRadius) : (pz += chunk.super.pos.voxelSize) { - var px = chunk.startIndex(x - leafRadius); - while(px < x + leafRadius) : (px += chunk.super.pos.voxelSize) { - var py = chunk.startIndex(y - leafRadius); - while(py < y + leafRadius) : (py += chunk.super.pos.voxelSize) { - const distSqr = (pz - center)*(pz - center) + (px - x)*(px - x) + (py - y)*(py - y); - if(chunk.liesInChunk(px, py, pz) and distSqr < radiusSqr and (distSqr < randomRadiusSqr or random.nextInt(u1, seed) != 0)) { // TODO: Use another seed to make this more reliable! - chunk.updateBlockIfDegradable(px, py, pz, .{.typ = self.leavesBlock, .data = 0}); // TODO: Natural standard. - } - } - } - } - } } } \ No newline at end of file