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};
|
||||
}
|
||||
|
||||
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 {
|
||||
preserveVoid: bool = false,
|
||||
};
|
||||
|
||||
pub fn paste(self: Blueprint, pos: Vec3i, flags: PasteFlags) void {
|
||||
const startX = pos[0];
|
||||
const startY = pos[1];
|
||||
@ -339,3 +376,7 @@ pub fn registerVoidBlock(block: Block) void {
|
||||
voidType = block.typ;
|
||||
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 FlowerPatch = @import("FlowerPatch.zig");
|
||||
pub const GroundPatch = @import("GroundPatch.zig");
|
||||
pub const SbbGen = @import("SbbGen.zig");
|
||||
pub const SimpleTreeModel = @import("SimpleTreeModel.zig");
|
||||
pub const SimpleVegetation = @import("SimpleVegetation.zig");
|
||||
pub const Stalagmite = @import("Stalagmite.zig");
|
||||
|
@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("main");
|
||||
const Vec3i = main.vec.Vec3i;
|
||||
const ZonElement = main.ZonElement;
|
||||
const Blueprint = main.blueprint.Blueprint;
|
||||
const List = main.List;
|
||||
@ -40,6 +41,10 @@ const BlueprintEntry = struct {
|
||||
pub inline fn direction(self: StructureBlock) Neighbor {
|
||||
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 {
|
||||
@ -70,6 +75,7 @@ const BlueprintEntry = struct {
|
||||
.data = block.data,
|
||||
};
|
||||
hasOrigin = true;
|
||||
self.blueprint.blocks.set(x, y, z, main.blueprint.getVoidBlock());
|
||||
}
|
||||
} else if(isChildBlock(block)) {
|
||||
const childBlockLocalId = childBlockNumericIdMap.get(block.typ) orelse return error.ChildBlockNotRecognized;
|
||||
@ -80,6 +86,7 @@ const BlueprintEntry = struct {
|
||||
.index = childBlockLocalId,
|
||||
.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 {
|
||||
id: []const u8,
|
||||
children: []AliasTable(Child),
|
||||
blueprints: *[4]BlueprintEntry,
|
||||
|
||||
@ -116,6 +124,7 @@ pub const StructureBuildingBlock = struct {
|
||||
return error.MissingBlueprint;
|
||||
};
|
||||
const self = StructureBuildingBlock{
|
||||
.id = stringId,
|
||||
.children = arenaAllocator.alloc(AliasTable(Child), childBlockStringId.items.len),
|
||||
.blueprints = blueprints,
|
||||
};
|
||||
@ -172,6 +181,7 @@ pub fn registerSBB(structures: *std.StringHashMap(ZonElement)) !void {
|
||||
std.debug.assert(structureCache.capacity() == 0);
|
||||
structureCache.ensureTotalCapacity(arenaAllocator.allocator, structures.count()) catch unreachable;
|
||||
childrenToResolve = .init(main.stackAllocator);
|
||||
defer childrenToResolve.deinit();
|
||||
{
|
||||
var iterator = structures.iterator();
|
||||
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});
|
||||
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();
|
||||
while(iterator.next()) |entry| {
|
||||
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| {
|
||||
std.log.err("Could not load blueprint '{s}' ({s})", .{stringId, @errorName(err)});
|
||||
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);
|
||||
|
||||
rotatedBlueprints.* = .{
|
||||
BlueprintEntry.init(blueprint0, stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"90"), stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"180"), stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"270"), stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint90, stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint180, stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint270, stringId) catch continue,
|
||||
};
|
||||
|
||||
blueprintCache.put(arenaAllocator.allocator, arenaAllocator.dupe(u8, stringId), rotatedBlueprints) catch unreachable;
|
||||
|
Loading…
x
Reference in New Issue
Block a user