diff --git a/src/blocks.zig b/src/blocks.zig index 4da0d054..2225cbb5 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -25,7 +25,7 @@ pub const BlockClass = enum(u8) { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var allocator = arena.allocator(); -pub const MaxBLockCount: usize = 65536; // 16 bit limit +pub const maxBlockCount: usize = 65536; // 16 bit limit pub const BlockDrop = struct { item: items.Item, @@ -56,26 +56,26 @@ pub const Ore = struct { } }; -var _lightingTransparent: [MaxBLockCount]bool = undefined; -var _transparent: [MaxBLockCount]bool = undefined; -var _id: [MaxBLockCount][]u8 = undefined; +var _lightingTransparent: [maxBlockCount]bool = undefined; +var _transparent: [maxBlockCount]bool = undefined; +var _id: [maxBlockCount][]u8 = undefined; /// Time in seconds to break this block by hand. -var _hardness: [MaxBLockCount]f32 = undefined; +var _hardness: [maxBlockCount]f32 = undefined; /// Minimum pickaxe/axe/shovel power required. -var _breakingPower: [MaxBLockCount]f32 = undefined; -var _solid: [MaxBLockCount]bool = undefined; -var _selectable: [MaxBLockCount]bool = undefined; -var _blockDrops: [MaxBLockCount][]BlockDrop = undefined; +var _breakingPower: [maxBlockCount]f32 = undefined; +var _solid: [maxBlockCount]bool = undefined; +var _selectable: [maxBlockCount]bool = undefined; +var _blockDrops: [maxBlockCount][]BlockDrop = undefined; /// Meaning undegradable parts of trees or other structures can grow through this block. -var _degradable: [MaxBLockCount]bool = undefined; -var _viewThrough: [MaxBLockCount]bool = undefined; -var _blockClass: [MaxBLockCount]BlockClass = undefined; -var _light: [MaxBLockCount]u32 = undefined; +var _degradable: [maxBlockCount]bool = undefined; +var _viewThrough: [maxBlockCount]bool = undefined; +var _blockClass: [maxBlockCount]BlockClass = undefined; +var _light: [maxBlockCount]u32 = undefined; /// How much light this block absorbs if it is transparent -var _absorption: [MaxBLockCount]u32 = undefined; +var _absorption: [maxBlockCount]u32 = undefined; /// GUI that is opened on click. -var _gui: [MaxBLockCount][]u8 = undefined; -var _mode: [MaxBLockCount]*RotationMode = undefined; +var _gui: [maxBlockCount][]u8 = undefined; +var _mode: [maxBlockCount]*RotationMode = undefined; var reverseIndices = std.StringHashMap(u16).init(arena.allocator()); @@ -310,10 +310,10 @@ pub const meshes = struct { time: i32, }; var size: u32 = 0; - var _modelIndex: [MaxBLockCount]u16 = undefined; - var _textureIndices: [MaxBLockCount][6]u32 = undefined; + var _modelIndex: [maxBlockCount]u16 = undefined; + var _textureIndices: [maxBlockCount][6]u32 = undefined; /// Stores the number of textures after each block was added. Used to clean additional textures when the world is switched. - var maxTextureCount: [MaxBLockCount]u32 = undefined; + var maxTextureCount: [maxBlockCount]u32 = undefined; /// Number of loaded meshes. Used to determine if an update is needed. var loadedMeshes: u32 = 0; diff --git a/src/chunk.zig b/src/chunk.zig index ba88e4c5..d6ef7acd 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -488,7 +488,7 @@ pub const meshing = struct { fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool { const rotatedModel = blocks.meshes.model(block); const model = &models.voxelModels.items[rotatedModel.modelIndex]; - const freestandingModel = switch(rotatedModel.permutation.permuteNeighborIndex(neighbor)) { + const freestandingModel = rotatedModel.modelIndex != models.fullCube and switch(rotatedModel.permutation.permuteNeighborIndex(neighbor)) { Neighbors.dirNegX => model.min[0] != 0, Neighbors.dirPosX => model.max[0] != 16, Neighbors.dirDown => model.min[1] != 0, diff --git a/src/models.zig b/src/models.zig index 8fad9d22..aff7eedb 100644 --- a/src/models.zig +++ b/src/models.zig @@ -208,6 +208,7 @@ pub fn getModelIndex(string: []const u8) u16 { } pub var voxelModels: std.ArrayList(VoxelModel) = undefined; +pub var fullCube: u16 = 0; // TODO: Allow loading from world assets. // TODO: Editable player models. @@ -220,6 +221,7 @@ pub fn init() !void { nameToIndex = std.StringHashMap(u16).init(main.threadAllocator); try nameToIndex.put("cube", @intCast(u16, voxelModels.items.len)); + fullCube = @intCast(u16, voxelModels.items.len); (try voxelModels.addOne()).init(cube); try nameToIndex.put("log", @intCast(u16, voxelModels.items.len)); diff --git a/src/server/terrain/CaveBiomeMap.zig b/src/server/terrain/CaveBiomeMap.zig index 79c3e060..60f544ec 100644 --- a/src/server/terrain/CaveBiomeMap.zig +++ b/src/server/terrain/CaveBiomeMap.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const main = @import("root"); +const Array3D = main.utils.Array3D; const Cache = main.utils.Cache; const Chunk = main.chunk.Chunk; const ChunkPosition = main.chunk.ChunkPosition; @@ -25,8 +26,7 @@ pub const CaveBiomeMapFragment = struct { pub const caveBiomeMapMask = caveBiomeMapSize - 1; pos: main.chunk.ChunkPosition, - biomeMap0: [1 << 3*(caveBiomeMapShift - caveBiomeShift)]*const Biome = undefined, - biomeMap1: [1 << 3*(caveBiomeMapShift - caveBiomeShift)]*const Biome = undefined, + biomeMap: [1 << 3*(caveBiomeMapShift - caveBiomeShift)][2]*const Biome = undefined, refCount: std.atomic.Atomic(u16) = std.atomic.Atomic(u16).init(0), pub fn init(self: *CaveBiomeMapFragment, wx: i32, wy: i32, wz: i32) !void { @@ -178,6 +178,24 @@ pub const InterpolatableCaveBiomeMapView = struct { return @select(i32, in >= Vec3i{0, 0, 0}, Vec3i{1, 1, 1}, Vec3i{-1, -1, -1}); } + pub fn bulkInterpolateValue(self: InterpolatableCaveBiomeMapView, comptime field: []const u8, wx: i32, wy: i32, wz: i32, voxelSize: u31, map: Array3D(f32), comptime mode: enum{addToMap}, comptime scale: f32) void { + var x: u31 = 0; + while(x < map.width) : (x += 1) { + var y: u31 = 0; + while(y < map.height) : (y += 1) { + var z: u31 = 0; + while(z < map.depth) : (z += 1) { + switch (mode) { + .addToMap => { + // TODO: Do a tetrahedron voxelization here, so parts of the tetrahedral barycentric coordinates can be precomputed. + map.ptr(x, y, z).* += scale*interpolateValue(self, wx + x*voxelSize, wy + y*voxelSize, wz + z*voxelSize, field); + } + } + } + } + } + } + pub noinline fn interpolateValue(self: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32, comptime field: []const u8) f32 { const worldPos = Vec3i{wx, wy, wz}; const closestGridpoint0 = (worldPos + @splat(3, @as(i32, CaveBiomeMapFragment.caveBiomeSize/2))) & @splat(3, ~@as(i32, CaveBiomeMapFragment.caveBiomeMask)); @@ -283,7 +301,7 @@ pub const InterpolatableCaveBiomeMapView = struct { return self.surfaceFragments[index].getBiome(wx, wz); } - fn _getBiome(self: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32, map: u1) *const Biome { + noinline fn _getBiome(self: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32, map: u1) *const Biome { var index: u8 = 0; if(wx - self.fragments[0].pos.wx >= CaveBiomeMapFragment.caveBiomeMapSize) { index += 4; @@ -298,15 +316,11 @@ pub const InterpolatableCaveBiomeMapView = struct { const relY = @intCast(u31, wy - self.fragments[index].pos.wy); const relZ = @intCast(u31, wz - self.fragments[index].pos.wz); const indexInArray = CaveBiomeMapFragment.getIndex(relX, relY, relZ); - if(map == 0) { - return self.fragments[index].biomeMap0[indexInArray]; - } else { - return self.fragments[index].biomeMap1[indexInArray]; - } + return self.fragments[index].biomeMap[indexInArray][map]; } /// Useful when the rough biome location is enough, for example for music. - pub fn getRoughBiome(self: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32, nullSeed: ?*u64, comptime _checkSurfaceBiome: bool) *const Biome { + pub noinline fn getRoughBiome(self: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32, comptime getSeed: bool, seed: *u64, comptime _checkSurfaceBiome: bool) *const Biome { if(_checkSurfaceBiome) { if(self.checkSurfaceBiome(wx, wy, wz)) |surfaceBiome| { return surfaceBiome; @@ -328,7 +342,7 @@ pub const InterpolatableCaveBiomeMapView = struct { map = 1; } - if(nullSeed) |seed| { + if(getSeed) { // A good old "I don't know what I'm doing" hash: seed.* = @bitCast(u64, @as(i64, gridPointX) << 48 ^ @as(i64, gridPointY) << 23 ^ @as(i64, gridPointZ) << 11 ^ @as(i64, gridPointX) >> 5 ^ @as(i64, gridPointY) << 3 ^ @as(i64, gridPointZ) ^ @as(i64, map)*5427642781) ^ main.server.world.?.seed; } @@ -383,11 +397,11 @@ pub const CaveBiomeMapView = struct { } pub fn getBiome(self: CaveBiomeMapView, relX: i32, relY: i32, relZ: i32) *const Biome { - return self.getBiomeAndSeed(relX, relY, relZ, null); + return self.getBiomeAndSeed(relX, relY, relZ, false, undefined); } /// Also returns a seed that is unique for the corresponding biome position. - pub fn getBiomeAndSeed(self: CaveBiomeMapView, relX: i32, relY: i32, relZ: i32, seed: ?*u64) *const Biome { + pub noinline fn getBiomeAndSeed(self: CaveBiomeMapView, relX: i32, relY: i32, relZ: i32, comptime getSeed: bool, seed: *u64) *const Biome { std.debug.assert(relX >= -32 and relX < self.super.width + 32); // coordinate out of bounds std.debug.assert(relY >= -32 and relY < self.super.width + 32); // coordinate out of bounds std.debug.assert(relZ >= -32 and relZ < self.super.width + 32); // coordinate out of bounds @@ -407,7 +421,7 @@ pub const CaveBiomeMapView = struct { wz += @floatToInt(i32, valueZ); }; - return self.super.getRoughBiome(wx, wy, wz, seed, false); + return self.super.getRoughBiome(wx, wy, wz, getSeed, seed, false); } }; diff --git a/src/server/terrain/cavebiomegen/RandomBiomeDistribution.zig b/src/server/terrain/cavebiomegen/RandomBiomeDistribution.zig index 293be759..71fcf1cd 100644 --- a/src/server/terrain/cavebiomegen/RandomBiomeDistribution.zig +++ b/src/server/terrain/cavebiomegen/RandomBiomeDistribution.zig @@ -83,11 +83,7 @@ pub fn generate(map: *CaveBiomeMapFragment, worldSeed: u64) Allocator.Error!void if(randomValue < 0) break; } var index = CaveBiomeMapFragment.getIndex(x, y, z); - if(_map == 0) { - map.biomeMap0[index] = biome; - } else { - map.biomeMap1[index] = biome; - } + map.biomeMap[index][_map] = biome; } } } diff --git a/src/server/terrain/cavegen/NoiseCaveGenerator.zig b/src/server/terrain/cavegen/NoiseCaveGenerator.zig index 1490f29a..43d07bfc 100644 --- a/src/server/terrain/cavegen/NoiseCaveGenerator.zig +++ b/src/server/terrain/cavegen/NoiseCaveGenerator.zig @@ -38,8 +38,9 @@ pub fn deinit() void { const scale = 64; const interpolatedPart = 4; -fn getValue(noise: Array3D(f32), map: *CaveMapFragment, biomeMap: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32) f32 { - return noise.get(@intCast(u31, wx - map.pos.wx) >> map.voxelShift, @intCast(u31, wy - map.pos.wy) >> map.voxelShift, @intCast(u31, wz - map.pos.wz) >> map.voxelShift) + biomeMap.interpolateValue(wx, wy, wz, "caves")*scale; +fn getValue(noise: Array3D(f32), map: *CaveMapFragment, outerSize: u31, biomeMap: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32) f32 { + _ = biomeMap; // TODO: clean this up at some point. + return noise.get(@intCast(u31, wx - map.pos.wx)/outerSize, @intCast(u31, wy - map.pos.wy)/outerSize, @intCast(u31, wz - map.pos.wz)/outerSize);// + biomeMap.interpolateValue(wx, wy, wz, "caves")*scale; } pub fn generate(map: *CaveMapFragment, worldSeed: u64) Allocator.Error!void { @@ -47,22 +48,23 @@ pub fn generate(map: *CaveMapFragment, worldSeed: u64) Allocator.Error!void { const biomeMap = try InterpolatableCaveBiomeMapView.init(map.pos, CaveMapFragment.width*map.pos.voxelSize); defer biomeMap.deinit(); const outerSize = @max(map.pos.voxelSize, interpolatedPart); - var noise = try FractalNoise3D.generateAligned(main.threadAllocator, map.pos.wx, map.pos.wy, map.pos.wz, map.pos.voxelSize, CaveMapFragment.width + 1, CaveMapFragment.height + 1, CaveMapFragment.width + 1, worldSeed, scale);//try Cached3DFractalNoise.init(map.pos.wx, map.pos.wy & ~@as(i32, CaveMapFragment.width*map.pos.voxelSize - 1), map.pos.wz, outerSize, map.pos.voxelSize*CaveMapFragment.width, worldSeed, scale); + var noise = try FractalNoise3D.generateAligned(main.threadAllocator, map.pos.wx, map.pos.wy, map.pos.wz, outerSize, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.height*map.pos.voxelSize/outerSize + 1, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, worldSeed, scale);//try Cached3DFractalNoise.init(map.pos.wx, map.pos.wy & ~@as(i32, CaveMapFragment.width*map.pos.voxelSize - 1), map.pos.wz, outerSize, map.pos.voxelSize*CaveMapFragment.width, worldSeed, scale); defer noise.deinit(main.threadAllocator); + biomeMap.bulkInterpolateValue("caves", map.pos.wx, map.pos.wy, map.pos.wz, outerSize, noise, .addToMap, scale); var x: u31 = 0; while(x < map.pos.voxelSize*CaveMapFragment.width) : (x += outerSize) { var y: u31 = 0; while(y < map.pos.voxelSize*CaveMapFragment.height) : (y += outerSize) { var z: u31 = 0; while(z < map.pos.voxelSize*CaveMapFragment.width) : (z += outerSize) { - const val000 = getValue(noise, map, biomeMap, x + map.pos.wx, y + map.pos.wy, z + map.pos.wz); - const val001 = getValue(noise, map, biomeMap, x + map.pos.wx, y + map.pos.wy, z + map.pos.wz + outerSize); - const val010 = getValue(noise, map, biomeMap, x + map.pos.wx, y + map.pos.wy + outerSize, z + map.pos.wz); - const val011 = getValue(noise, map, biomeMap, x + map.pos.wx, y + map.pos.wy + outerSize, z + map.pos.wz + outerSize); - const val100 = getValue(noise, map, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy, z + map.pos.wz); - const val101 = getValue(noise, map, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy, z + map.pos.wz + outerSize); - const val110 = getValue(noise, map, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy + outerSize, z + map.pos.wz); - const val111 = getValue(noise, map, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy + outerSize, z + map.pos.wz + outerSize); + const val000 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx, y + map.pos.wy, z + map.pos.wz); + const val001 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx, y + map.pos.wy, z + map.pos.wz + outerSize); + const val010 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx, y + map.pos.wy + outerSize, z + map.pos.wz); + const val011 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx, y + map.pos.wy + outerSize, z + map.pos.wz + outerSize); + const val100 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy, z + map.pos.wz); + const val101 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy, z + map.pos.wz + outerSize); + const val110 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy + outerSize, z + map.pos.wz); + const val111 = getValue(noise, map, outerSize, biomeMap, x + map.pos.wx + outerSize, y + map.pos.wy + outerSize, z + map.pos.wz + outerSize); // Test if they are all inside or all outside the cave to skip these cases: const measureForEquality = sign(val000) + sign(val001) + sign(val010) + sign(val011) + sign(val100) + sign(val101) + sign(val110) + sign(val111); if(measureForEquality == -8) { diff --git a/src/server/terrain/chunkgen/CrystalGenerator.zig b/src/server/terrain/chunkgen/CrystalGenerator.zig index 24411daf..b9d99925 100644 --- a/src/server/terrain/chunkgen/CrystalGenerator.zig +++ b/src/server/terrain/chunkgen/CrystalGenerator.zig @@ -129,7 +129,7 @@ fn considerCrystal(x: i32, y: i32, z: i32, chunk: *main.chunk.Chunk, seed: *u64, fn considerCoordinates(x: i32, y: i32, z: i32, chunk: *main.chunk.Chunk, caveMap: CaveMap.CaveMapView, biomeMap: CaveBiomeMap.CaveBiomeMapView, seed: *u64) void { var oldSeed = seed.*; - const crystalSpawns = biomeMap.getBiomeAndSeed(x + main.chunk.chunkSize/2 - chunk.pos.wx, y + main.chunk.chunkSize/2 - chunk.pos.wy, z + main.chunk.chunkSize/2 - chunk.pos.wz, seed).crystals; + const crystalSpawns = biomeMap.getBiomeAndSeed(x + main.chunk.chunkSize/2 - chunk.pos.wx, y + main.chunk.chunkSize/2 - chunk.pos.wy, z + main.chunk.chunkSize/2 - chunk.pos.wz, true, seed).crystals; random.scrambleSeed(seed); var differendColors: u32 = 1; if(random.nextInt(u1, seed) != 0) { diff --git a/src/server/terrain/chunkgen/TerrainGenerator.zig b/src/server/terrain/chunkgen/TerrainGenerator.zig index a6fc4e34..50995ddb 100644 --- a/src/server/terrain/chunkgen/TerrainGenerator.zig +++ b/src/server/terrain/chunkgen/TerrainGenerator.zig @@ -38,6 +38,7 @@ pub fn deinit() void { } pub fn generate(worldSeed: u64, chunk: *main.chunk.Chunk, caveMap: CaveMap.CaveMapView, biomeMap: CaveBiomeMap.CaveBiomeMapView) Allocator.Error!void { + const voxelSizeShift = @ctz(chunk.pos.voxelSize); var x: u31 = 0; while(x < chunk.width) : (x += chunk.pos.voxelSize) { var z: u31 = 0; @@ -46,7 +47,7 @@ pub fn generate(worldSeed: u64, chunk: *main.chunk.Chunk, caveMap: CaveMap.CaveM var makeSurfaceStructure = true; var y: i32 = chunk.width - chunk.pos.voxelSize; while(y >= 0) : (y -= chunk.pos.voxelSize) { - const mask = @as(u64, 1) << @intCast(u6, @divExact(y, chunk.pos.voxelSize)); + const mask = @as(u64, 1) << @intCast(u6, y >> voxelSizeShift); if(heightData & mask != 0) { const biome = biomeMap.getBiome(x, y, z);