mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
Get block updates from the server and update the mesh.
This commit is contained in:
parent
cd5c466bc8
commit
a04fc3369d
@ -102,6 +102,12 @@ pub fn getByID(id: []const u8) u16 {
|
||||
pub const Block = packed struct {
|
||||
typ: u16,
|
||||
data: u16,
|
||||
pub fn toInt(self: Block) u32 {
|
||||
return @as(u32, self.typ) | @as(u32, self.data)<<16;
|
||||
}
|
||||
pub fn fromInt(self: u32) Block {
|
||||
return Block{.typ=@truncate(u16, self), .data=@intCast(u16, self>>16)};
|
||||
}
|
||||
pub fn lightingTransparent(self: Block) bool {
|
||||
return _lightingTransparent[self.typ];
|
||||
}
|
||||
|
169
src/chunk.zig
169
src/chunk.zig
@ -479,7 +479,7 @@ pub const meshing = struct {
|
||||
|
||||
fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool {
|
||||
_ = neighbor; // TODO: ↓← Blocks.mode(other).checkTransparency(other, neighbor)
|
||||
return other.typ == 0 or false or (!std.meta.eql(block, other) and other.viewThrough());
|
||||
return block.typ != 0 and (other.typ == 0 or false or (!std.meta.eql(block, other) and other.viewThrough()));
|
||||
}
|
||||
|
||||
pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void {
|
||||
@ -523,6 +523,164 @@ pub const meshing = struct {
|
||||
self.neighborStart = [_]u31{self.coreCount} ** 7;
|
||||
}
|
||||
|
||||
fn addFace(self: *ChunkMesh, position: u32, textureNormal: u32, fromNeighborChunk: ?u3) !void {
|
||||
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
|
||||
var insertionIndex: u31 = undefined;
|
||||
if(fromNeighborChunk) |neighbor| {
|
||||
insertionIndex = self.neighborStart[neighbor];
|
||||
for(self.neighborStart[neighbor+1..]) |*start| {
|
||||
start.* += 2;
|
||||
}
|
||||
} else {
|
||||
insertionIndex = self.coreCount;
|
||||
self.coreCount += 2;
|
||||
for(self.neighborStart) |*start| {
|
||||
start.* += 2;
|
||||
}
|
||||
}
|
||||
try self.faces.insert(insertionIndex, position);
|
||||
try self.faces.insert(insertionIndex+1, textureNormal);
|
||||
}
|
||||
|
||||
fn removeFace(self: *ChunkMesh, position: u32, textureNormal: u32, fromNeighborChunk: ?u3) void {
|
||||
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
|
||||
var searchStart: u32 = undefined;
|
||||
var searchEnd: u32 = undefined;
|
||||
if(fromNeighborChunk) |neighbor| {
|
||||
searchStart = self.neighborStart[neighbor];
|
||||
searchEnd = self.neighborStart[neighbor+1];
|
||||
for(self.neighborStart[neighbor+1..]) |*start| {
|
||||
start.* -= 2;
|
||||
}
|
||||
} else {
|
||||
searchStart = 1;
|
||||
searchEnd = self.coreCount;
|
||||
self.coreCount -= 2;
|
||||
for(self.neighborStart) |*start| {
|
||||
start.* -= 2;
|
||||
}
|
||||
}
|
||||
var i: u32 = searchStart;
|
||||
while(i < searchEnd): (i += 2) {
|
||||
if(self.faces.items[i] == position and self.faces.items[i+1] == textureNormal) {
|
||||
_ = self.faces.orderedRemove(i+1);
|
||||
_ = self.faces.orderedRemove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@panic("Couldn't find the face to remove. This case is not handled.");
|
||||
}
|
||||
|
||||
fn changeFace(self: *ChunkMesh, position: u32, oldTextureNormal: u32, newTextureNormal: u32, fromNeighborChunk: ?u3) void {
|
||||
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
|
||||
var searchRange: []u32 = undefined;
|
||||
if(fromNeighborChunk) |neighbor| {
|
||||
searchRange = self.faces.items[self.neighborStart[neighbor]..self.neighborStart[neighbor+1]];
|
||||
} else {
|
||||
searchRange = self.faces.items[1..self.coreCount];
|
||||
}
|
||||
var i: u32 = 0;
|
||||
while(i < searchRange.len): (i += 2) {
|
||||
if(searchRange[i] == position and searchRange[i+1] == oldTextureNormal) {
|
||||
searchRange[i+1] = newTextureNormal;
|
||||
return;
|
||||
}
|
||||
}
|
||||
std.log.err("Couldn't find the face to replace.", .{});
|
||||
}
|
||||
|
||||
pub fn updateBlock(self: *ChunkMesh, _x: ChunkCoordinate, _y: ChunkCoordinate, _z: ChunkCoordinate, newBlock: Block) !void {
|
||||
const x = _x & chunkMask;
|
||||
const y = _y & chunkMask;
|
||||
const z = _z & chunkMask;
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
if(!self.generated) return;
|
||||
const oldBlock = self.chunk.?.blocks[getIndex(x, y, z)];
|
||||
for(Neighbors.iterable) |neighbor| {
|
||||
var neighborMesh = self;
|
||||
var nx = x + Neighbors.relX[neighbor];
|
||||
var ny = y + Neighbors.relY[neighbor];
|
||||
var nz = z + Neighbors.relZ[neighbor];
|
||||
if(nx & chunkMask != nx or ny & chunkMask != ny or nz & chunkMask != nz) { // Outside this chunk.
|
||||
neighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor) orelse continue;
|
||||
if(!neighborMesh.generated) continue;
|
||||
neighborMesh.mutex.lock();
|
||||
}
|
||||
defer if(neighborMesh != self) neighborMesh.mutex.unlock();
|
||||
nx &= chunkMask;
|
||||
ny &= chunkMask;
|
||||
nz &= chunkMask;
|
||||
const neighborBlock = neighborMesh.chunk.?.blocks[getIndex(nx, ny, nz)];
|
||||
{
|
||||
{ // The face of the changed block
|
||||
const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor);
|
||||
const position: u32 = @intCast(u32, nx) | @intCast(u32, ny)<<5 | @intCast(u32, nz)<<10;
|
||||
const normal: u32 = neighbor;
|
||||
const newTextureNormal = blocks.meshes.textureIndices(newBlock)[neighbor] | normal<<24;
|
||||
const oldTextureNormal = blocks.meshes.textureIndices(oldBlock)[neighbor] | normal<<24;
|
||||
if(canBeSeenThroughOtherBlock(oldBlock, neighborBlock, neighbor) != newVisibility) {
|
||||
if(newVisibility) { // Adding the face
|
||||
if(neighborMesh == self) {
|
||||
try self.addFace(position, newTextureNormal, null);
|
||||
} else {
|
||||
try neighborMesh.addFace(position, newTextureNormal, neighbor);
|
||||
}
|
||||
} else { // Removing the face
|
||||
if(neighborMesh == self) {
|
||||
self.removeFace(position, oldTextureNormal, null);
|
||||
} else {
|
||||
neighborMesh.removeFace(position, oldTextureNormal, neighbor);
|
||||
}
|
||||
}
|
||||
} else if(newVisibility) { // Changing the face
|
||||
if(neighborMesh == self) {
|
||||
self.changeFace(position, oldTextureNormal, newTextureNormal, null);
|
||||
} else {
|
||||
neighborMesh.changeFace(position, oldTextureNormal, newTextureNormal, neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
{ // The face of the neighbor block
|
||||
const newVisibility = canBeSeenThroughOtherBlock(neighborBlock, newBlock, neighbor ^ 1);
|
||||
const position: u32 = @intCast(u32, x) | @intCast(u32, y)<<5 | @intCast(u32, z)<<10;
|
||||
const normal: u32 = neighbor ^ 1;
|
||||
const newTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | normal<<24;
|
||||
const oldTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | normal<<24;
|
||||
if(canBeSeenThroughOtherBlock(neighborBlock, oldBlock, neighbor ^ 1) != newVisibility) {
|
||||
if(newVisibility) { // Adding the face
|
||||
if(neighborMesh == self) {
|
||||
try self.addFace(position, newTextureNormal, null);
|
||||
} else {
|
||||
try self.addFace(position, newTextureNormal, neighbor);
|
||||
}
|
||||
} else { // Removing the face
|
||||
if(neighborMesh == self) {
|
||||
self.removeFace(position, oldTextureNormal, null);
|
||||
} else {
|
||||
self.removeFace(position, oldTextureNormal, neighbor);
|
||||
}
|
||||
}
|
||||
} else if(newVisibility) { // Changing the face
|
||||
if(neighborMesh == self) {
|
||||
self.changeFace(position, oldTextureNormal, newTextureNormal, null);
|
||||
} else {
|
||||
self.changeFace(position, oldTextureNormal, newTextureNormal, neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(neighborMesh != self) neighborMesh.uploadData();
|
||||
}
|
||||
self.chunk.?.blocks[getIndex(x, y, z)] = newBlock;
|
||||
self.uploadData();
|
||||
}
|
||||
|
||||
fn uploadData(self: *ChunkMesh) void {
|
||||
self.vertexCount = @intCast(u31, 6*(self.faces.items.len-1)/2);
|
||||
self.faceData.bufferData(u32, self.faces.items);
|
||||
}
|
||||
|
||||
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
|
||||
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
|
||||
if(self.chunk == null) return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
|
||||
@ -563,14 +721,14 @@ pub const meshing = struct {
|
||||
var otherZ = @intCast(u8, z+%Neighbors.relZ[neighbor] & chunkMask);
|
||||
var block = (&self.chunk.?.blocks)[getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed.
|
||||
var otherBlock = (&neighborMesh.chunk.?.blocks)[getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed.
|
||||
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor) and block.typ != 0) {
|
||||
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
|
||||
const normal: u32 = neighbor;
|
||||
const position: u32 = @as(u32, otherX) | @as(u32, otherY)<<5 | @as(u32, otherZ)<<10;
|
||||
const textureNormal = blocks.meshes.textureIndices(block)[neighbor] | normal<<24;
|
||||
try additionalNeighborFaces.append(position);
|
||||
try additionalNeighborFaces.append(textureNormal);
|
||||
}
|
||||
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1) and otherBlock.typ != 0) {
|
||||
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) {
|
||||
const normal: u32 = neighbor ^ 1;
|
||||
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10;
|
||||
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | normal<<24;
|
||||
@ -625,7 +783,7 @@ pub const meshing = struct {
|
||||
var otherZ = @intCast(u8, (z+%Neighbors.relZ[neighbor]+%offsetZ >> 1) & chunkMask);
|
||||
var block = (&self.chunk.?.blocks)[getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed.
|
||||
var otherBlock = (&neighborMesh.chunk.?.blocks)[getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed.
|
||||
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1) and otherBlock.typ != 0) {
|
||||
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) {
|
||||
const normal: u32 = neighbor ^ 1;
|
||||
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10;
|
||||
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | normal<<24;
|
||||
@ -639,8 +797,7 @@ pub const meshing = struct {
|
||||
}
|
||||
}
|
||||
self.neighborStart[6] = @intCast(u31, self.faces.items.len);
|
||||
self.vertexCount = @intCast(u31, 6*(self.faces.items.len-1)/2);
|
||||
self.faceData.bufferData(u32, self.faces.items);
|
||||
self.uploadData();
|
||||
self.generated = true;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const assets = @import("assets.zig");
|
||||
const Block = @import("blocks.zig").Block;
|
||||
const chunk = @import("chunk.zig");
|
||||
const entity = @import("entity.zig");
|
||||
const main = @import("main.zig");
|
||||
@ -690,14 +691,15 @@ pub const Protocols: struct {
|
||||
.voxelSize = @intCast(chunk.UChunkCoordinate, std.mem.readIntBig(chunk.ChunkCoordinate, data[12..16])),
|
||||
};
|
||||
const _inflatedData = try utils.Compression.inflate(main.threadAllocator, data[16..]);
|
||||
if(_inflatedData.len != chunk.chunkVolume*4) {
|
||||
std.log.err("Transmission of chunk has invalid size: {}. Input data: {any}, After inflate: {any}", .{_inflatedData.len, data, _inflatedData});
|
||||
}
|
||||
data = _inflatedData;
|
||||
defer main.threadAllocator.free(_inflatedData);
|
||||
var ch = try renderer.RenderStructure.allocator.create(chunk.Chunk);
|
||||
ch.init(pos);
|
||||
for(ch.blocks) |*block| {
|
||||
var blockTypeAndData = std.mem.readIntBig(u32, data[0..4]);
|
||||
block.typ = @intCast(u16, blockTypeAndData & 0xffff);
|
||||
block.data = @intCast(u16, blockTypeAndData >> 16);
|
||||
block.* = Block.fromInt(std.mem.readIntBig(u32, data[0..4]));
|
||||
data = data[4..];
|
||||
}
|
||||
try renderer.RenderStructure.updateChunkMesh(ch);
|
||||
@ -813,6 +815,30 @@ pub const Protocols: struct {
|
||||
conn.sendUnimportant(id, fullItemData);
|
||||
}
|
||||
},
|
||||
blockUpdate: type = struct {
|
||||
const id: u8 = 7;
|
||||
fn receive(_: *Connection, data: []const u8) !void {
|
||||
var x = std.mem.readIntBig(chunk.ChunkCoordinate, data[0..4]);
|
||||
var y = std.mem.readIntBig(chunk.ChunkCoordinate, data[4..8]);
|
||||
var z = std.mem.readIntBig(chunk.ChunkCoordinate, data[8..12]);
|
||||
var newBlock = Block.fromInt(std.mem.readIntBig(u32, data[12..16]));
|
||||
try renderer.RenderStructure.updateBlock(x, y, z, newBlock);
|
||||
// TODO:
|
||||
// if(conn instanceof User) {
|
||||
// Server.world.updateBlock(x, y, z, newBlock);
|
||||
// } else {
|
||||
// Cubyz.world.remoteUpdateBlock(x, y, z, newBlock);
|
||||
// }
|
||||
}
|
||||
pub fn send(conn: *Connection, x: chunk.ChunkCoordinate, y: chunk.ChunkCoordinate, z: chunk.ChunkCoordinate, newBlock: Block) !void {
|
||||
var data: [16]u8 = undefined;
|
||||
std.mem.writeIntBig(chunk.ChunkCoordinate, data[0..4], x);
|
||||
std.mem.writeIntBig(chunk.ChunkCoordinate, data[4..8], y);
|
||||
std.mem.writeIntBig(chunk.ChunkCoordinate, data[8..12], z);
|
||||
std.mem.writeIntBig(chunk.ChunkCoordinate, data[12..16], newBlock.toInt());
|
||||
try conn.sendImportant(id, &data);
|
||||
}
|
||||
},
|
||||
entity: type = struct {
|
||||
const id: u8 = 8;
|
||||
fn receive(_: *Connection, data: []const u8) !void {
|
||||
|
@ -444,6 +444,14 @@ pub const RenderStructure = struct {
|
||||
var lastSize: [settings.highestLOD + 1]chunk.ChunkCoordinate = [_]chunk.ChunkCoordinate{0} ** (settings.highestLOD + 1);
|
||||
var lodMutex: [settings.highestLOD + 1]std.Thread.Mutex = [_]std.Thread.Mutex{std.Thread.Mutex{}} ** (settings.highestLOD + 1);
|
||||
var mutex = std.Thread.Mutex{};
|
||||
var blockUpdateMutex = std.Thread.Mutex{};
|
||||
const BlockUpdate = struct {
|
||||
x: chunk.ChunkCoordinate,
|
||||
y: chunk.ChunkCoordinate,
|
||||
z: chunk.ChunkCoordinate,
|
||||
newBlock: blocks.Block,
|
||||
};
|
||||
var blockUpdateList: std.ArrayList(BlockUpdate) = undefined;
|
||||
|
||||
pub fn init() !void {
|
||||
lastRD = 0;
|
||||
@ -451,6 +459,7 @@ pub const RenderStructure = struct {
|
||||
gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
allocator = gpa.allocator();
|
||||
updatableList = std.ArrayList(chunk.ChunkPosition).init(allocator);
|
||||
blockUpdateList = std.ArrayList(BlockUpdate).init(allocator);
|
||||
clearList = std.ArrayList(*ChunkMeshNode).init(allocator);
|
||||
for(storageLists) |*storageList| {
|
||||
storageList.* = try allocator.alloc(?*ChunkMeshNode, 0);
|
||||
@ -472,6 +481,7 @@ pub const RenderStructure = struct {
|
||||
chunkMesh.mesh.deinit();
|
||||
allocator.destroy(chunkMesh);
|
||||
}
|
||||
blockUpdateList.deinit();
|
||||
clearList.deinit();
|
||||
game.world.?.blockPalette.deinit();
|
||||
if(gpa.deinit()) {
|
||||
@ -654,6 +664,17 @@ pub const RenderStructure = struct {
|
||||
}
|
||||
|
||||
pub fn updateMeshes(targetTime: i64) !void {
|
||||
{ // First of all process all the block updates:
|
||||
blockUpdateMutex.lock();
|
||||
defer blockUpdateMutex.unlock();
|
||||
for(blockUpdateList.items) |blockUpdate| {
|
||||
const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1};
|
||||
if(_getNode(pos)) |node| {
|
||||
try node.mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock);
|
||||
}
|
||||
}
|
||||
blockUpdateList.clearRetainingCapacity();
|
||||
}
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
while(updatableList.items.len != 0) {
|
||||
@ -757,6 +778,12 @@ pub const RenderStructure = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn updateBlock(x: chunk.ChunkCoordinate, y: chunk.ChunkCoordinate, z: chunk.ChunkCoordinate, newBlock: blocks.Block) !void {
|
||||
blockUpdateMutex.lock();
|
||||
try blockUpdateList.append(BlockUpdate{.x=x, .y=y, .z=z, .newBlock=newBlock});
|
||||
defer blockUpdateMutex.unlock();
|
||||
}
|
||||
|
||||
pub fn updateChunkMesh(mesh: *chunk.Chunk) !void {
|
||||
try MeshGenerationTask.schedule(mesh);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user