Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 57 additions & 33 deletions src/server/terrain/simple_structures/Stalagmite.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const CaveBiomeMapView = terrain.CaveBiomeMap.CaveBiomeMapView;
const CaveMapView = terrain.CaveMap.CaveMapView;
const GenerationMode = terrain.biomes.SimpleStructureModel.GenerationMode;
const vec = main.vec;
const Vec2f = vec.Vec2f;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
Expand All @@ -22,59 +23,82 @@ const Stalagmite = @This();
block: main.blocks.Block,
size: f32,
sizeVariation: f32,
topSlope: f32,
baseSlope: f32,

pub fn loadModel(parameters: ZonElement) ?*Stalagmite {
const self = main.worldArena.create(Stalagmite);
const baseSlope = parameters.get(f32, "baseSlope", 4.0);
self.* = .{
.block = main.blocks.parseBlock(parameters.get([]const u8, "block", "cubyz:stalagmite")),
.size = parameters.get(f32, "size", 12),
.sizeVariation = parameters.get(f32, "size_variation", 8),
.baseSlope = baseSlope,
.topSlope = parameters.get(f32, "topSlope", baseSlope),
};
return self;
}

pub fn generate(self: *Stalagmite, _: GenerationMode, x: i32, y: i32, z: i32, chunk: *main.chunk.ServerChunk, _: CaveMapView, _: CaveBiomeMapView, seed: *u64, isCeiling: bool) void {
const relX: f32 = @as(f32, @floatFromInt(x)) + main.random.nextFloat(seed);
const relY: f32 = @as(f32, @floatFromInt(y)) + main.random.nextFloat(seed);
var relZ: f32 = @as(f32, @floatFromInt(z)) + main.random.nextFloat(seed);
pub fn generate(self: *Stalagmite, _: GenerationMode, x: i32, y: i32, z: i32, chunk: *main.chunk.ServerChunk, _: CaveMapView, _: CaveBiomeMapView, seed: *u64, _: bool) void {
const relX: f32 = @as(f32, @floatFromInt(x)) + main.random.nextFloat(seed)*0.6 - 0.3;
const relY: f32 = @as(f32, @floatFromInt(y)) + main.random.nextFloat(seed)*0.6 - 0.3;
const relZ: f32 = @as(f32, @floatFromInt(z)) + main.random.nextFloat(seed)*0.6 - 0.3;

var length = self.size + random.nextFloat(seed)*self.sizeVariation;
const height = self.size + random.nextFloat(seed)*self.sizeVariation;

const delZ: f32 = if(isCeiling) -1 else 1;
relZ -= delZ*length/4;
length += length/4;
// We want to ensure the following properties:
// height(r = 0) = height
// height'(r = 0) = -topSlope
// height(r = baseRadius) = 0
// height'(r = baseRadius) = -baseSlope
// With height(r) = a·r² + b·r + c → height'(r) = 2a·r + b
// c = height, b = -topSlope
// 0 = a·baseRadius² + b·baseRadius + c
// -baseSlope = 2a·baseRadius + b
// → a·baseRadius = (-baseSlope - b)/2
// This permits both positive and negative values for baseRadius, so we need to account for that during substitution:
// = (-baseSlope - b)/2·±baseRadius + b·baseRadius + c
// → baseRadius = -c/(±(-baseSlope - b)/2 + b)
const c = height;
const b = -self.topSlope;
var baseRadius: f32 = undefined;
var a: f32 = undefined;
if(self.baseSlope == self.topSlope) {
baseRadius = height/self.topSlope;
a = 0;
} else {
baseRadius = -c/((-self.baseSlope - b)/2 + b);
if(baseRadius < 0) {
baseRadius = -c/(-(-self.baseSlope - b)/2 + b);
}
a = (-self.baseSlope - b)/(2*baseRadius);
}

var j: f32 = 0;
while(j < length) {
const z2 = relZ + delZ*j;
var size: f32 = 0;
size = (length - j)/4;
const xMin: i32 = @intFromFloat(relX - size);
const xMax: i32 = @intFromFloat(relX + size);
const yMin: i32 = @intFromFloat(relY - size);
const yMax: i32 = @intFromFloat(relY + size);
const zMin: i32 = @intFromFloat(z2 - size);
const zMax: i32 = @intFromFloat(z2 + size);
var x3: i32 = xMin;
while(x3 <= xMax) : (x3 += 1) {
var y3: i32 = yMin;
while(y3 <= yMax) : (y3 += 1) {
const xMin: i32 = @intFromFloat(@floor(relX - baseRadius));
const xMax: i32 = @intFromFloat(@ceil(relX + baseRadius));
const yMin: i32 = @intFromFloat(@floor(relY - baseRadius));
const yMax: i32 = @intFromFloat(@ceil(relY + baseRadius));
var x3: i32 = xMin;
while(x3 <= xMax) : (x3 += 1) {
var y3: i32 = yMin;
while(y3 <= yMax) : (y3 += 1) {
const distSquare = vec.lengthSquare(Vec2f{@as(f32, @floatFromInt(x3)) - relX, @as(f32, @floatFromInt(y3)) - relY});
if(distSquare >= baseRadius*baseRadius) continue;
const r = @sqrt(distSquare);
const columnHeight = a*r*r + b*r + c;
if(x3 >= 0 and x3 < chunk.super.width and y3 >= 0 and y3 < chunk.super.width) {
const zMin: i32 = @intFromFloat(@round(relZ - columnHeight));
const zMax: i32 = @intFromFloat(@round(relZ + columnHeight));
var z3: i32 = zMin;
while(z3 <= zMax) : (z3 += 1) {
const dist = vec.lengthSquare(Vec3f{@as(f32, @floatFromInt(x3)) - relX, @as(f32, @floatFromInt(y3)) - relY, @as(f32, @floatFromInt(z3)) - z2});
if(dist < size*size) {
if(x3 >= 0 and x3 < chunk.super.width and y3 >= 0 and y3 < chunk.super.width and z3 >= 0 and z3 < chunk.super.width) {
const block: main.blocks.Block = chunk.getBlock(x3, y3, z3);
if(block.typ == 0 or block.degradable()) {
chunk.updateBlockInGeneration(x3, y3, z3, self.block);
}
if(z3 >= 0 and z3 < chunk.super.width) {
const block: main.blocks.Block = chunk.getBlock(x3, y3, z3);
if(block.typ == 0 or block.degradable()) {
chunk.updateBlockInGeneration(x3, y3, z3, self.block);
}
}
}
}
}
if(size > 2) size = 2;
j += size/2; // Make sure there are no stalagmite bits floating in the air.
if(size < 0.5) break; // Also preventing floating stalagmite bits.
}
}