mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Add Structure Building Blocks based Generator (#1227)
* Revert "Remove SBBGen" This reverts commit b49048412f21c57d5638144da7f039753a94cafc. * Revert "Remove example SBB" This reverts commit afc5d6fed0ba92b558ccda91893fdb3fc63f69ec. * Revert "Remove blueprint code" This reverts commit 2553950adbdcef9c1c68afe4109f9247b74abb92. * Fix compilation errors * Fix compilation errors #2 * Fix test errors * Fix rotateZ * Resolve structure reference while instantiating SBBGen * Fix formatting issues * Add new trees to forest * Add new trees to grassland * Decrease forest density so you can find new trees * Add degradable paste mode * Remove substitutions * Apply review suggestions * Use lookup table for alignment * Apply suggestions from code review * Update src/server/terrain/simple_structures/_list.zig Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> * Fix inital SBB offset * Never place air in placeInGeneration * Integrate void block with paste * Revert "Move hashInt and hashCombine to utils" This reverts commit 9bb276f69f60ad60f1170d07ec30ebd02307a36a. * Make PasteMode comptime * Fix remaining issues with void block integration * Fix formatting * Apply review change requests * Remove origin and child blocks while resolving sbbs * Apply review change requests * I hate indexing * Fix example tree models * Use single index for chunk and blueprint in pasteInGeneration * Fix formatting * Extract blueprintOffset * Fix formatting * Apply suggestions from code review Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> * Apply Quantums suggestion for Y and Z * No cast * Use pos instead of chunkOffset * Remove test tree * Apply suggestions from code review --------- Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com>
This commit is contained in:
parent
3ba4d5fd14
commit
61268fb374
@ -108,9 +108,46 @@ pub const Blueprint = struct {
|
|||||||
}
|
}
|
||||||
return .{.success = self};
|
return .{.success = self};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const PasteMode = enum {all, degradable};
|
||||||
|
|
||||||
|
pub fn pasteInGeneration(self: Blueprint, pos: Vec3i, chunk: *ServerChunk, mode: PasteMode) void {
|
||||||
|
switch(mode) {
|
||||||
|
inline else => |comptimeMode| _pasteInGeneration(self, pos, chunk, comptimeMode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _pasteInGeneration(self: Blueprint, pos: Vec3i, chunk: *ServerChunk, comptime mode: PasteMode) void {
|
||||||
|
const indexEndX: i32 = @min(@as(i32, chunk.super.width) - pos[0], @as(i32, @intCast(self.blocks.width)));
|
||||||
|
const indexEndY: i32 = @min(@as(i32, chunk.super.width) - pos[1], @as(i32, @intCast(self.blocks.depth)));
|
||||||
|
const indexEndZ: i32 = @min(@as(i32, chunk.super.width) - pos[2], @as(i32, @intCast(self.blocks.height)));
|
||||||
|
|
||||||
|
var indexX: u31 = @max(0, -pos[0]);
|
||||||
|
while(indexX < indexEndX) : (indexX += chunk.super.pos.voxelSize) {
|
||||||
|
var indexY: u31 = @max(0, -pos[1]);
|
||||||
|
while(indexY < indexEndY) : (indexY += chunk.super.pos.voxelSize) {
|
||||||
|
var indexZ: u31 = @max(0, -pos[2]);
|
||||||
|
while(indexZ < indexEndZ) : (indexZ += chunk.super.pos.voxelSize) {
|
||||||
|
const block = self.blocks.get(indexX, indexY, indexZ);
|
||||||
|
|
||||||
|
if(block.typ == voidType) continue;
|
||||||
|
|
||||||
|
const chunkX = indexX + pos[0];
|
||||||
|
const chunkY = indexY + pos[1];
|
||||||
|
const chunkZ = indexZ + pos[2];
|
||||||
|
switch(mode) {
|
||||||
|
.all => chunk.updateBlockInGeneration(chunkX, chunkY, chunkZ, block),
|
||||||
|
.degradable => chunk.updateBlockIfDegradable(chunkX, chunkY, chunkZ, block),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const PasteFlags = struct {
|
pub const PasteFlags = struct {
|
||||||
preserveVoid: bool = false,
|
preserveVoid: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn paste(self: Blueprint, pos: Vec3i, flags: PasteFlags) void {
|
pub fn paste(self: Blueprint, pos: Vec3i, flags: PasteFlags) void {
|
||||||
const startX = pos[0];
|
const startX = pos[0];
|
||||||
const startY = pos[1];
|
const startY = pos[1];
|
||||||
@ -339,3 +376,7 @@ pub fn registerVoidBlock(block: Block) void {
|
|||||||
voidType = block.typ;
|
voidType = block.typ;
|
||||||
std.debug.assert(voidType != 0);
|
std.debug.assert(voidType != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getVoidBlock() Block {
|
||||||
|
return Block{.typ = voidType.?, .data = 0};
|
||||||
|
}
|
||||||
|
90
src/server/terrain/simple_structures/SbbGen.zig
Normal file
90
src/server/terrain/simple_structures/SbbGen.zig
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const main = @import("main");
|
||||||
|
const terrain = main.server.terrain;
|
||||||
|
const Vec3i = main.vec.Vec3i;
|
||||||
|
const GenerationMode = terrain.biomes.SimpleStructureModel.GenerationMode;
|
||||||
|
const CaveMapView = terrain.CaveMap.CaveMapView;
|
||||||
|
const CaveBiomeMapView = terrain.CaveBiomeMap.CaveBiomeMapView;
|
||||||
|
const sbb = terrain.structure_building_blocks;
|
||||||
|
const Blueprint = main.blueprint.Blueprint;
|
||||||
|
const ZonElement = main.ZonElement;
|
||||||
|
const Neighbor = main.chunk.Neighbor;
|
||||||
|
const ServerChunk = main.chunk.ServerChunk;
|
||||||
|
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
||||||
|
|
||||||
|
pub const id = "cubyz:sbb";
|
||||||
|
pub const generationMode = .floor;
|
||||||
|
|
||||||
|
const SbbGen = @This();
|
||||||
|
|
||||||
|
structureRef: *const sbb.StructureBuildingBlock,
|
||||||
|
placeMode: Blueprint.PasteMode,
|
||||||
|
|
||||||
|
pub fn getHash(self: SbbGen) u64 {
|
||||||
|
return std.hash.Wyhash.hash(@intFromEnum(self.placeMode), self.structureRef.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadModel(arenaAllocator: NeverFailingAllocator, parameters: ZonElement) *SbbGen {
|
||||||
|
const structureId = parameters.get(?[]const u8, "structure", null) orelse unreachable;
|
||||||
|
const structureRef = sbb.getByStringId(structureId) orelse {
|
||||||
|
std.log.err("Could not find structure building block with id '{s}'", .{structureId});
|
||||||
|
unreachable;
|
||||||
|
};
|
||||||
|
const self = arenaAllocator.create(SbbGen);
|
||||||
|
self.* = .{
|
||||||
|
.structureRef = structureRef,
|
||||||
|
.placeMode = std.meta.stringToEnum(Blueprint.PasteMode, parameters.get([]const u8, "placeMode", "degradable")) orelse Blueprint.PasteMode.degradable,
|
||||||
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate(self: *SbbGen, _: GenerationMode, x: i32, y: i32, z: i32, chunk: *ServerChunk, _: CaveMapView, _: CaveBiomeMapView, seed: *u64, _: bool) void {
|
||||||
|
placeSbb(self, self.structureRef, Vec3i{x, y, z}, Neighbor.dirUp, chunk, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placementPosition: Vec3i, placementDirection: Neighbor, chunk: *ServerChunk, seed: *u64) void {
|
||||||
|
const origin = structure.blueprints[0].originBlock;
|
||||||
|
const rotationCount = alignDirections(origin.direction(), placementDirection) catch |err| {
|
||||||
|
std.log.err("Could not align directions for structure '{s}' for directions '{s}'' and '{s}', error: {s}", .{structure.id, @tagName(origin.direction()), @tagName(placementDirection), @errorName(err)});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const rotated = &structure.blueprints[rotationCount];
|
||||||
|
const rotatedOrigin = rotated.originBlock.pos();
|
||||||
|
const pastePosition = placementPosition - rotatedOrigin - placementDirection.relPos();
|
||||||
|
|
||||||
|
rotated.blueprint.pasteInGeneration(pastePosition, chunk, self.placeMode);
|
||||||
|
|
||||||
|
for(rotated.childBlocks) |childBlock| {
|
||||||
|
const child = structure.pickChild(childBlock, seed);
|
||||||
|
placeSbb(self, child, pastePosition + childBlock.pos(), childBlock.direction(), chunk, seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alignDirections(input: Neighbor, desired: Neighbor) !usize {
|
||||||
|
const Rotation = enum(u3) {
|
||||||
|
@"0" = 0,
|
||||||
|
@"90" = 1,
|
||||||
|
@"180" = 2,
|
||||||
|
@"270" = 3,
|
||||||
|
NotPossibleToAlign = 4,
|
||||||
|
};
|
||||||
|
comptime var alignTable: [6][6]Rotation = undefined;
|
||||||
|
comptime for(Neighbor.iterable) |in| {
|
||||||
|
for(Neighbor.iterable) |out| blk: {
|
||||||
|
var current = in;
|
||||||
|
for(0..4) |i| {
|
||||||
|
if(current == out) {
|
||||||
|
alignTable[in.toInt()][out.toInt()] = @enumFromInt(i);
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
current = current.rotateZ();
|
||||||
|
}
|
||||||
|
alignTable[in.toInt()][out.toInt()] = Rotation.NotPossibleToAlign;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch(alignTable[input.toInt()][desired.toInt()]) {
|
||||||
|
.NotPossibleToAlign => return error.NotPossibleToAlign,
|
||||||
|
else => |v| return @intFromEnum(v),
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ pub const Boulder = @import("Boulder.zig");
|
|||||||
pub const FallenTree = @import("FallenTree.zig");
|
pub const FallenTree = @import("FallenTree.zig");
|
||||||
pub const FlowerPatch = @import("FlowerPatch.zig");
|
pub const FlowerPatch = @import("FlowerPatch.zig");
|
||||||
pub const GroundPatch = @import("GroundPatch.zig");
|
pub const GroundPatch = @import("GroundPatch.zig");
|
||||||
|
pub const SbbGen = @import("SbbGen.zig");
|
||||||
pub const SimpleTreeModel = @import("SimpleTreeModel.zig");
|
pub const SimpleTreeModel = @import("SimpleTreeModel.zig");
|
||||||
pub const SimpleVegetation = @import("SimpleVegetation.zig");
|
pub const SimpleVegetation = @import("SimpleVegetation.zig");
|
||||||
pub const Stalagmite = @import("Stalagmite.zig");
|
pub const Stalagmite = @import("Stalagmite.zig");
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const main = @import("main");
|
const main = @import("main");
|
||||||
|
const Vec3i = main.vec.Vec3i;
|
||||||
const ZonElement = main.ZonElement;
|
const ZonElement = main.ZonElement;
|
||||||
const Blueprint = main.blueprint.Blueprint;
|
const Blueprint = main.blueprint.Blueprint;
|
||||||
const List = main.List;
|
const List = main.List;
|
||||||
@ -40,6 +41,10 @@ const BlueprintEntry = struct {
|
|||||||
pub inline fn direction(self: StructureBlock) Neighbor {
|
pub inline fn direction(self: StructureBlock) Neighbor {
|
||||||
return @enumFromInt(self.data);
|
return @enumFromInt(self.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn pos(self: StructureBlock) Vec3i {
|
||||||
|
return Vec3i{self.x, self.y, self.z};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn init(blueprint: Blueprint, stringId: []const u8) !BlueprintEntry {
|
fn init(blueprint: Blueprint, stringId: []const u8) !BlueprintEntry {
|
||||||
@ -70,6 +75,7 @@ const BlueprintEntry = struct {
|
|||||||
.data = block.data,
|
.data = block.data,
|
||||||
};
|
};
|
||||||
hasOrigin = true;
|
hasOrigin = true;
|
||||||
|
self.blueprint.blocks.set(x, y, z, main.blueprint.getVoidBlock());
|
||||||
}
|
}
|
||||||
} else if(isChildBlock(block)) {
|
} else if(isChildBlock(block)) {
|
||||||
const childBlockLocalId = childBlockNumericIdMap.get(block.typ) orelse return error.ChildBlockNotRecognized;
|
const childBlockLocalId = childBlockNumericIdMap.get(block.typ) orelse return error.ChildBlockNotRecognized;
|
||||||
@ -80,6 +86,7 @@ const BlueprintEntry = struct {
|
|||||||
.index = childBlockLocalId,
|
.index = childBlockLocalId,
|
||||||
.data = block.data,
|
.data = block.data,
|
||||||
});
|
});
|
||||||
|
self.blueprint.blocks.set(x, y, z, main.blueprint.getVoidBlock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,6 +110,7 @@ pub fn isOriginBlock(block: Block) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const StructureBuildingBlock = struct {
|
pub const StructureBuildingBlock = struct {
|
||||||
|
id: []const u8,
|
||||||
children: []AliasTable(Child),
|
children: []AliasTable(Child),
|
||||||
blueprints: *[4]BlueprintEntry,
|
blueprints: *[4]BlueprintEntry,
|
||||||
|
|
||||||
@ -116,6 +124,7 @@ pub const StructureBuildingBlock = struct {
|
|||||||
return error.MissingBlueprint;
|
return error.MissingBlueprint;
|
||||||
};
|
};
|
||||||
const self = StructureBuildingBlock{
|
const self = StructureBuildingBlock{
|
||||||
|
.id = stringId,
|
||||||
.children = arenaAllocator.alloc(AliasTable(Child), childBlockStringId.items.len),
|
.children = arenaAllocator.alloc(AliasTable(Child), childBlockStringId.items.len),
|
||||||
.blueprints = blueprints,
|
.blueprints = blueprints,
|
||||||
};
|
};
|
||||||
@ -172,6 +181,7 @@ pub fn registerSBB(structures: *std.StringHashMap(ZonElement)) !void {
|
|||||||
std.debug.assert(structureCache.capacity() == 0);
|
std.debug.assert(structureCache.capacity() == 0);
|
||||||
structureCache.ensureTotalCapacity(arenaAllocator.allocator, structures.count()) catch unreachable;
|
structureCache.ensureTotalCapacity(arenaAllocator.allocator, structures.count()) catch unreachable;
|
||||||
childrenToResolve = .init(main.stackAllocator);
|
childrenToResolve = .init(main.stackAllocator);
|
||||||
|
defer childrenToResolve.deinit();
|
||||||
{
|
{
|
||||||
var iterator = structures.iterator();
|
var iterator = structures.iterator();
|
||||||
while(iterator.next()) |entry| {
|
while(iterator.next()) |entry| {
|
||||||
@ -195,7 +205,6 @@ pub fn registerSBB(structures: *std.StringHashMap(ZonElement)) !void {
|
|||||||
std.log.debug("Resolved child structure '{s}'->'{s}'->'{d}' to '{s}'", .{entry.parentId, entry.colorName, entry.childIndex, entry.structureId});
|
std.log.debug("Resolved child structure '{s}'->'{s}'->'{d}' to '{s}'", .{entry.parentId, entry.colorName, entry.childIndex, entry.structureId});
|
||||||
parent.children[entry.colorIndex].items[entry.childIndex].structure = child;
|
parent.children[entry.colorIndex].items[entry.childIndex].structure = child;
|
||||||
}
|
}
|
||||||
childrenToResolve.deinit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,17 +230,22 @@ pub fn registerBlueprints(blueprints: *std.StringHashMap([]u8)) !void {
|
|||||||
var iterator = blueprints.iterator();
|
var iterator = blueprints.iterator();
|
||||||
while(iterator.next()) |entry| {
|
while(iterator.next()) |entry| {
|
||||||
const stringId = entry.key_ptr.*;
|
const stringId = entry.key_ptr.*;
|
||||||
|
// Rotated copies need to be made before initializing BlueprintEntry as it removes origin and child blocks.
|
||||||
const blueprint0 = Blueprint.load(arenaAllocator, entry.value_ptr.*) catch |err| {
|
const blueprint0 = Blueprint.load(arenaAllocator, entry.value_ptr.*) catch |err| {
|
||||||
std.log.err("Could not load blueprint '{s}' ({s})", .{stringId, @errorName(err)});
|
std.log.err("Could not load blueprint '{s}' ({s})", .{stringId, @errorName(err)});
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
const blueprint90 = blueprint0.rotateZ(arenaAllocator, .@"90");
|
||||||
|
const blueprint180 = blueprint0.rotateZ(arenaAllocator, .@"180");
|
||||||
|
const blueprint270 = blueprint0.rotateZ(arenaAllocator, .@"270");
|
||||||
|
|
||||||
const rotatedBlueprints = arenaAllocator.create([4]BlueprintEntry);
|
const rotatedBlueprints = arenaAllocator.create([4]BlueprintEntry);
|
||||||
|
|
||||||
rotatedBlueprints.* = .{
|
rotatedBlueprints.* = .{
|
||||||
BlueprintEntry.init(blueprint0, stringId) catch continue,
|
BlueprintEntry.init(blueprint0, stringId) catch continue,
|
||||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"90"), stringId) catch continue,
|
BlueprintEntry.init(blueprint90, stringId) catch continue,
|
||||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"180"), stringId) catch continue,
|
BlueprintEntry.init(blueprint180, stringId) catch continue,
|
||||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"270"), stringId) catch continue,
|
BlueprintEntry.init(blueprint270, stringId) catch continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
blueprintCache.put(arenaAllocator.allocator, arenaAllocator.dupe(u8, stringId), rotatedBlueprints) catch unreachable;
|
blueprintCache.put(arenaAllocator.allocator, arenaAllocator.dupe(u8, stringId), rotatedBlueprints) catch unreachable;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user