mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 03:29:48 -04:00
Various performance improvements:
The NoiseCaveGenerator now produces all (noise + interpolateValue) values at once instead of recalculating them 7 times on average. The TerrainGenerator no longer uses integer division. The chunk meshing doesn't calculate the giant switch case in the middle of `cenBeSeenThroughOtherBlock()` if the block as the simple cube model. And some smaller things.
This commit is contained in:
parent
83f4503390
commit
f15d1adc56
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user