mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 11:44:21 -04:00
Add the ability to insert ranges of equal blocks into the palette compressed data structure.
The index is then only calculated once. Individual insertions are not optimized though. fixes #600 Also it seems that in my previous performance commit I had made a mistake in my measurement, I must have measured a buggy version which turned out to be super fast. In actuality the performance difference was much smaller, so I decided to optimize that section as well. Now it's at 1.7 ms per chunk. Still needs more improvements, I guess.
This commit is contained in:
parent
c3711868c5
commit
4fa08bec95
@ -385,6 +385,20 @@ pub const ServerChunk = struct { // MARK: ServerChunk
|
||||
self.super.data.setValue(index, newBlock);
|
||||
}
|
||||
|
||||
/// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes.
|
||||
/// Does not do any bound checks. They are expected to be done with the `liesInChunk` function.
|
||||
pub fn updateBlockColumnInGeneration(self: *ServerChunk, _x: i32, _y: i32, _zStartInclusive: i32, _zEndInclusive: i32, newBlock: Block) void {
|
||||
std.debug.assert(_zStartInclusive <= _zEndInclusive);
|
||||
main.utils.assertLocked(&self.mutex);
|
||||
const x = _x >> self.super.voxelSizeShift;
|
||||
const y = _y >> self.super.voxelSizeShift;
|
||||
const zStartInclusive = _zStartInclusive >> self.super.voxelSizeShift;
|
||||
const zEndInclusive = _zEndInclusive >> self.super.voxelSizeShift;
|
||||
const indexStart = getIndex(x, y, zStartInclusive);
|
||||
const indexEnd = getIndex(x, y, zEndInclusive) + 1;
|
||||
self.super.data.setValueInColumn(indexStart, indexEnd, newBlock);
|
||||
}
|
||||
|
||||
pub fn updateFromLowerResolution(self: *ServerChunk, other: *ServerChunk) void {
|
||||
const xOffset = if(other.super.pos.wx != self.super.pos.wx) chunkSize/2 else 0; // Offsets of the lower resolution chunk in this chunk.
|
||||
const yOffset = if(other.super.pos.wy != self.super.pos.wy) chunkSize/2 else 0;
|
||||
|
@ -46,7 +46,7 @@ pub const CaveBiomeMapFragment = struct { // MARK: caveBiomeMapFragment
|
||||
@Vector(3, i64){-12*fac, 15*fac, 16*fac},
|
||||
}; // divide result by shift to do a proper rotation
|
||||
|
||||
const inverseRotationMatrix = .{
|
||||
const transposeRotationMatrix = .{
|
||||
@Vector(3, i64){20*fac, 9*fac, -12*fac},
|
||||
@Vector(3, i64){ 0*fac, 20*fac, 15*fac},
|
||||
@Vector(3, i64){15*fac, -12*fac, 16*fac},
|
||||
@ -62,9 +62,9 @@ pub const CaveBiomeMapFragment = struct { // MARK: caveBiomeMapFragment
|
||||
|
||||
pub fn rotateInverse(in: Vec3i) Vec3i {
|
||||
return @truncate(@Vector(3, i64){
|
||||
vec.dot(inverseRotationMatrix[0], in) >> rotationMatrixShift,
|
||||
vec.dot(inverseRotationMatrix[1], in) >> rotationMatrixShift,
|
||||
vec.dot(inverseRotationMatrix[2], in) >> rotationMatrixShift,
|
||||
vec.dot(transposeRotationMatrix[0], in) >> rotationMatrixShift,
|
||||
vec.dot(transposeRotationMatrix[1], in) >> rotationMatrixShift,
|
||||
vec.dot(transposeRotationMatrix[2], in) >> rotationMatrixShift,
|
||||
});
|
||||
}
|
||||
|
||||
@ -402,15 +402,14 @@ pub const InterpolatableCaveBiomeMapView = struct { // MARK: InterpolatableCaveB
|
||||
return frag.biomeMap[indexInArray][map];
|
||||
}
|
||||
|
||||
fn getGridPoint(pos: Vec3i, map: *u1) Vec3i {
|
||||
const rotatedPos = CaveBiomeMapFragment.rotate(pos);
|
||||
fn getGridPointFromPrerotated(rotatedPos: Vec3i, map: *u1) Vec3i {
|
||||
var gridPoint = rotatedPos +% @as(Vec3i, @splat(CaveBiomeMapFragment.caveBiomeSize/2)) & @as(Vec3i, @splat(~@as(i32, CaveBiomeMapFragment.caveBiomeMask)));
|
||||
|
||||
const distance = rotatedPos -% gridPoint;
|
||||
const totalDistance = @reduce(.Add, @abs(distance));
|
||||
if(totalDistance > CaveBiomeMapFragment.caveBiomeSize*3/4) {
|
||||
// Or with 1 to prevent errors if the value is 0.
|
||||
gridPoint +%= (std.math.sign(distance | @as(Vec3i, @splat(1)) - @as(Vec3i, @splat(1))))*@as(Vec3i, @splat(CaveBiomeMapFragment.caveBiomeSize/2));
|
||||
gridPoint +%= std.math.sign(distance)*@as(Vec3i, @splat(CaveBiomeMapFragment.caveBiomeSize/2));
|
||||
map.* = 1;
|
||||
} else {
|
||||
map.* = 0;
|
||||
@ -418,6 +417,42 @@ pub const InterpolatableCaveBiomeMapView = struct { // MARK: InterpolatableCaveB
|
||||
return gridPoint;
|
||||
}
|
||||
|
||||
fn getGridPoint(pos: Vec3i, map: *u1) Vec3i {
|
||||
const rotatedPos = CaveBiomeMapFragment.rotate(pos);
|
||||
return getGridPointFromPrerotated(rotatedPos, map);
|
||||
}
|
||||
|
||||
fn getGridPointAndHeight(pos: Vec3i, map: *u1, returnHeight: *i32, voxelSize: u31) Vec3i {
|
||||
const preRotatedPos = @Vector(3, i64){
|
||||
vec.dot(CaveBiomeMapFragment.rotationMatrix[0], pos),
|
||||
vec.dot(CaveBiomeMapFragment.rotationMatrix[1], pos),
|
||||
vec.dot(CaveBiomeMapFragment.rotationMatrix[2], pos),
|
||||
};
|
||||
var startMap: u1 = undefined;
|
||||
const gridPoint = getGridPointFromPrerotated(@truncate(preRotatedPos >> @splat(CaveBiomeMapFragment.rotationMatrixShift)), &startMap);
|
||||
|
||||
var start: i32 = 0;
|
||||
var end = @min(returnHeight.*, @as(comptime_int, @intFromFloat(@ceil(CaveBiomeMapFragment.caveBiomeSize*@sqrt(5.0)/2.0)))) & ~@as(i32, voxelSize-1);
|
||||
{
|
||||
var otherMap: u1 = undefined;
|
||||
const nextGridPoint = getGridPointFromPrerotated(@truncate(preRotatedPos +% CaveBiomeMapFragment.transposeRotationMatrix[2]*@as(Vec3i, @splat(end)) >> @splat(CaveBiomeMapFragment.rotationMatrixShift)), &otherMap);
|
||||
if(@reduce(.And, nextGridPoint == gridPoint) and otherMap == startMap) start = end;
|
||||
}
|
||||
while(start + voxelSize < end) {
|
||||
const mid = start +% @divTrunc(end -% start, 2) & ~@as(i32, voxelSize-1);
|
||||
var otherMap: u1 = undefined;
|
||||
const nextGridPoint = getGridPointFromPrerotated(@truncate(preRotatedPos +% CaveBiomeMapFragment.transposeRotationMatrix[2]*@as(Vec3i, @splat(mid)) >> @splat(CaveBiomeMapFragment.rotationMatrixShift)), &otherMap);
|
||||
if(@reduce(.Or, nextGridPoint != gridPoint) or otherMap != startMap) {
|
||||
end = mid;
|
||||
} else {
|
||||
start = mid;
|
||||
}
|
||||
}
|
||||
returnHeight.* = end;
|
||||
map.* = startMap;
|
||||
return gridPoint;
|
||||
}
|
||||
|
||||
/// Useful when the rough biome location is enough, for example for music.
|
||||
pub noinline fn getRoughBiome(self: InterpolatableCaveBiomeMapView, wx: i32, wy: i32, wz: i32, comptime getSeed: bool, seed: *u64, comptime _checkSurfaceBiome: bool) *const Biome {
|
||||
if(_checkSurfaceBiome) {
|
||||
@ -444,25 +479,7 @@ pub const InterpolatableCaveBiomeMapView = struct { // MARK: InterpolatableCaveB
|
||||
}
|
||||
}
|
||||
var map: u1 = undefined;
|
||||
const gridPoint = getGridPoint(.{wx, wy, wz}, &map);
|
||||
var start = wz;
|
||||
var end = start +% returnHeight.*;
|
||||
{
|
||||
var otherMap: u1 = undefined;
|
||||
const nextGridPoint = getGridPoint(.{wx, wy, end}, &otherMap);
|
||||
if(@reduce(.And, nextGridPoint == gridPoint) and otherMap == map) start = end;
|
||||
}
|
||||
while(start + 1 < end) {
|
||||
const mid = start +% @divTrunc(end -% start, 2);
|
||||
var otherMap: u1 = undefined;
|
||||
const nextGridPoint = getGridPoint(.{wx, wy, mid}, &otherMap);
|
||||
if(@reduce(.Or, nextGridPoint != gridPoint) or otherMap != map) {
|
||||
end = mid;
|
||||
} else {
|
||||
start = mid;
|
||||
}
|
||||
}
|
||||
returnHeight.* = end -% wz;
|
||||
const gridPoint = getGridPointAndHeight(.{wx, wy, wz}, &map, returnHeight, self.pos.voxelSize);
|
||||
|
||||
if(getSeed) {
|
||||
// A good old "I don't know what I'm doing" hash (TODO: Use some standard hash maybe):
|
||||
|
@ -42,57 +42,75 @@ pub fn generate(worldSeed: u64, chunk: *main.chunk.ServerChunk, caveMap: CaveMap
|
||||
var baseSeed: u64 = undefined;
|
||||
const biome = biomeMap.getBiomeColumnAndSeed(x, y, zBiome, true, &baseSeed, &biomeHeight);
|
||||
defer zBiome = chunk.startIndex(zBiome + biomeHeight - 1 + chunk.super.pos.voxelSize);
|
||||
var makeSurfaceStructure = true;
|
||||
var z: i32 = @min(chunk.super.width - chunk.super.pos.voxelSize, chunk.startIndex(zBiome + biomeHeight - 1));
|
||||
while(z >= zBiome) : (z -= chunk.super.pos.voxelSize) {
|
||||
const mask = @as(u64, 1) << @intCast(z >> voxelSizeShift);
|
||||
if(heightData & mask != 0) {
|
||||
if(makeSurfaceStructure) {
|
||||
const surfaceBlock = caveMap.findTerrainChangeAbove(x, y, z) - chunk.super.pos.voxelSize;
|
||||
var bseed: u64 = random.initSeed3D(worldSeed, .{chunk.super.pos.wx + x, chunk.super.pos.wy + y, chunk.super.pos.wz + z});
|
||||
// Add the biomes surface structure:
|
||||
z = @min(z + chunk.super.pos.voxelSize, biome.structure.addSubTerranian(chunk, surfaceBlock, @max(caveMap.findTerrainChangeBelow(x, y, z), zBiome - 1), x, y, &bseed));
|
||||
makeSurfaceStructure = false;
|
||||
} else {
|
||||
var typ = biome.stoneBlockType;
|
||||
var seed = baseSeed;
|
||||
for (biome.stripes) |stripe| {
|
||||
const pos: Vec3d = .{
|
||||
@as(f64, @floatFromInt(x + chunk.super.pos.wx)),
|
||||
@as(f64, @floatFromInt(y + chunk.super.pos.wy)),
|
||||
@as(f64, @floatFromInt(z + chunk.super.pos.wz))
|
||||
};
|
||||
var d: f64 = 0;
|
||||
if (stripe.direction) |direction| {
|
||||
d = vec.dot(direction, pos);
|
||||
} else {
|
||||
const dx = main.random.nextDoubleSigned(&seed);
|
||||
const dy = main.random.nextDoubleSigned(&seed);
|
||||
const dz = main.random.nextDoubleSigned(&seed);
|
||||
const dir: Vec3d = .{dx, dy, dz};
|
||||
d = vec.dot(vec.normalize(dir), pos);
|
||||
}
|
||||
|
||||
const distance = (stripe.maxDistance - stripe.minDistance) * main.random.nextDouble(&seed) + stripe.minDistance;
|
||||
|
||||
const offset = (stripe.maxOffset - stripe.minOffset) * main.random.nextDouble(&seed) + stripe.minOffset;
|
||||
|
||||
const width = (stripe.maxWidth - stripe.minWidth) * main.random.nextDouble(&seed) + stripe.minWidth;
|
||||
|
||||
if (@mod(d + offset, distance) < width) {
|
||||
typ = stripe.block;
|
||||
break;
|
||||
const surfaceBlock = caveMap.findTerrainChangeAbove(x, y, z) - chunk.super.pos.voxelSize;
|
||||
var bseed: u64 = random.initSeed3D(worldSeed, .{chunk.super.pos.wx + x, chunk.super.pos.wy + y, chunk.super.pos.wz + z});
|
||||
const airBlockBelow = caveMap.findTerrainChangeBelow(x, y, z);
|
||||
// Add the biomes surface structure:
|
||||
z = @min(z + chunk.super.pos.voxelSize, biome.structure.addSubTerranian(chunk, surfaceBlock, @max(airBlockBelow, zBiome - 1), x, y, &bseed));
|
||||
z -= chunk.super.pos.voxelSize;
|
||||
if(z < zBiome) break;
|
||||
if(z > airBlockBelow) {
|
||||
const zMin = @max(airBlockBelow + 1, zBiome);
|
||||
if(biome.stripes.len == 0) {
|
||||
const typ = biome.stoneBlockType;
|
||||
chunk.updateBlockColumnInGeneration(x, y, zMin, z, .{.typ = typ, .data = 0}); // TODO: Natural standard.
|
||||
z = zMin;
|
||||
} else {
|
||||
while(z >= zMin) : (z -= chunk.super.pos.voxelSize) {
|
||||
var typ = biome.stoneBlockType;
|
||||
var seed = baseSeed;
|
||||
for (biome.stripes) |stripe| {
|
||||
const pos: Vec3d = .{
|
||||
@as(f64, @floatFromInt(x + chunk.super.pos.wx)),
|
||||
@as(f64, @floatFromInt(y + chunk.super.pos.wy)),
|
||||
@as(f64, @floatFromInt(z + chunk.super.pos.wz))
|
||||
};
|
||||
var d: f64 = 0;
|
||||
if (stripe.direction) |direction| {
|
||||
d = vec.dot(direction, pos);
|
||||
} else {
|
||||
const dx = main.random.nextDoubleSigned(&seed);
|
||||
const dy = main.random.nextDoubleSigned(&seed);
|
||||
const dz = main.random.nextDoubleSigned(&seed);
|
||||
const dir: Vec3d = .{dx, dy, dz};
|
||||
d = vec.dot(vec.normalize(dir), pos);
|
||||
}
|
||||
|
||||
const distance = (stripe.maxDistance - stripe.minDistance) * main.random.nextDouble(&seed) + stripe.minDistance;
|
||||
|
||||
const offset = (stripe.maxOffset - stripe.minOffset) * main.random.nextDouble(&seed) + stripe.minOffset;
|
||||
|
||||
const width = (stripe.maxWidth - stripe.minWidth) * main.random.nextDouble(&seed) + stripe.minWidth;
|
||||
|
||||
if (@mod(d + offset, distance) < width) {
|
||||
typ = stripe.block;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunk.updateBlockInGeneration(x, y, z, .{.typ = typ, .data = 0}); // TODO: Natural standard.
|
||||
}
|
||||
z += chunk.super.pos.voxelSize;
|
||||
}
|
||||
chunk.updateBlockInGeneration(x, y, z, .{.typ = typ, .data = 0}); // TODO: Natural standard.
|
||||
}
|
||||
} else {
|
||||
if(z + chunk.super.pos.wz < 0 and z + chunk.super.pos.wz >= biomeMap.getSurfaceHeight(x + chunk.super.pos.wx, y + chunk.super.pos.wy) - (chunk.super.pos.voxelSize - 1)) {
|
||||
chunk.updateBlockInGeneration(x, y, z, .{.typ = water, .data = 0}); // TODO: Natural standard.
|
||||
const surface = biomeMap.getSurfaceHeight(x + chunk.super.pos.wx, y + chunk.super.pos.wy) - (chunk.super.pos.voxelSize - 1) -% chunk.super.pos.wz;
|
||||
const oceanHeight = 0 -% chunk.super.pos.wz;
|
||||
const airVolumeStart = caveMap.findTerrainChangeBelow(x, y, z) + chunk.super.pos.voxelSize;
|
||||
const zStart = @max(airVolumeStart, zBiome);
|
||||
if(z < surface or zStart >= oceanHeight) {
|
||||
chunk.updateBlockColumnInGeneration(x, y, zStart, z, .{.typ = 0, .data = 0});
|
||||
} else {
|
||||
chunk.updateBlockInGeneration(x, y, z, .{.typ = 0, .data = 0});
|
||||
if(z >= oceanHeight) {
|
||||
chunk.updateBlockColumnInGeneration(x, y, oceanHeight, z, .{.typ = 0, .data = 0});
|
||||
z = oceanHeight - chunk.super.pos.voxelSize;
|
||||
}
|
||||
chunk.updateBlockColumnInGeneration(x, y, zStart, z, .{.typ = water, .data = 0}); // TODO: Natural standard.
|
||||
}
|
||||
makeSurfaceStructure = true;
|
||||
z = zStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1252,7 +1252,7 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
|
||||
return self.palette[self.data.getValue(i)];
|
||||
}
|
||||
|
||||
pub fn setValue(noalias self: *Self, i: usize, val: T) void {
|
||||
fn getOrInsertPaletteIndex(noalias self: *Self, val: T) u32 {
|
||||
std.debug.assert(self.paletteLength <= self.palette.len);
|
||||
var paletteIndex: u32 = 0;
|
||||
while(paletteIndex < self.paletteLength) : (paletteIndex += 1) { // TODO: There got to be a faster way to do this. Either using SIMD or using a cache or hashmap.
|
||||
@ -1272,7 +1272,11 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
|
||||
self.paletteLength += 1;
|
||||
std.debug.assert(self.paletteLength <= self.palette.len);
|
||||
}
|
||||
return paletteIndex;
|
||||
}
|
||||
|
||||
pub fn setValue(noalias self: *Self, i: usize, val: T) void {
|
||||
const paletteIndex = self.getOrInsertPaletteIndex(val);
|
||||
const previousPaletteIndex = self.data.setAndGetValue(i, paletteIndex);
|
||||
if(previousPaletteIndex != paletteIndex) {
|
||||
if(self.paletteOccupancy[paletteIndex] == 0) {
|
||||
@ -1286,6 +1290,22 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setValueInColumn(noalias self: *Self, startIndex: usize, endIndex: usize, val: T) void {
|
||||
std.debug.assert(startIndex < endIndex);
|
||||
const paletteIndex = self.getOrInsertPaletteIndex(val);
|
||||
for(startIndex..endIndex) |i| {
|
||||
const previousPaletteIndex = self.data.setAndGetValue(i, paletteIndex);
|
||||
self.paletteOccupancy[previousPaletteIndex] -= 1;
|
||||
if(self.paletteOccupancy[previousPaletteIndex] == 0) {
|
||||
self.activePaletteEntries -= 1;
|
||||
}
|
||||
}
|
||||
if(self.paletteOccupancy[paletteIndex] == 0) {
|
||||
self.activePaletteEntries += 1;
|
||||
}
|
||||
self.paletteOccupancy[paletteIndex] += @intCast(endIndex - startIndex);
|
||||
}
|
||||
|
||||
pub fn optimizeLayout(self: *Self) void {
|
||||
if(std.math.log2_int_ceil(usize, self.palette.len) == std.math.log2_int_ceil(usize, self.activePaletteEntries)) return;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user