From cbd3abea49f1092ea88e678ed9ecfe946d4c38fc Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Thu, 15 Dec 2022 14:03:38 +0100 Subject: [PATCH] Start working on rotation modes. --- src/blocks.zig | 25 +++++---- src/chunk.zig | 21 ++++---- src/main.zig | 6 ++- src/renderer.zig | 4 +- src/rotation.zig | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/rotation.zig diff --git a/src/blocks.zig b/src/blocks.zig index abedff01..0a28e00d 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -8,6 +8,8 @@ const Color = @import("graphics.zig").Color; const TextureArray = @import("graphics.zig").TextureArray; const items = @import("items.zig"); const models = @import("models.zig"); +const rotation = @import("rotation.zig"); +const RotationMode = rotation.RotationMode; pub const BlockClass = enum(u8) { wood, @@ -29,8 +31,6 @@ pub const BlockDrop = struct { amount: f32, }; -pub const RotationMode = u0; // TODO! - var _lightingTransparent: [MaxBLockCount]bool = undefined; var _transparent: [MaxBLockCount]bool = undefined; var _id: [MaxBLockCount][]u8 = undefined; @@ -50,7 +50,7 @@ var _light: [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 _mode: [MaxBLockCount]*RotationMode = undefined; var reverseIndices = std.StringHashMap(u16).init(arena.allocator()); @@ -62,9 +62,8 @@ pub fn register(_: []const u8, id: []const u8, json: JsonElement) !u16 { } _id[size] = try allocator.dupe(u8, id); try reverseIndices.put(_id[size], @intCast(u16, size)); -// TODO: -// _mode[size] = CubyzRegistries.ROTATION_MODE_REGISTRY.getByID(json.getString("rotation", "cubyz:no_rotation")); -// _blockDrops[size] = new BlockDrop[0]; + + _mode[size] = rotation.getByID(json.get([]const u8, "rotation", "cubyz:no_rotation")); _breakingPower[size] = json.get(f32, "breakingPower", 0); _hardness[size] = json.get(f32, "hardness", 1); @@ -217,7 +216,7 @@ pub const Block = packed struct { return _gui[self.typ]; } - pub fn mode(self: Block) RotationMode { + pub fn mode(self: Block) *RotationMode { return _mode[self.typ]; } @@ -240,7 +239,7 @@ pub const Block = packed struct { pub const meshes = struct { var size: u32 = 0; - var _modelIndices: [MaxBLockCount]u16 = 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; @@ -317,8 +316,12 @@ pub const meshes = struct { arenaForWorld = std.heap.ArenaAllocator.init(std.heap.page_allocator); } - pub fn modelIndices(block: Block) u16 { - return (&_modelIndices[block.typ]).*; + pub fn modelIndex(block: Block) u16 { + return block.mode().modelIndex(block); + } + + pub fn modelIndexStart(block: Block) u16 { + return _modelIndex[block.typ]; } pub fn textureIndices(block: Block) *const [6] u32 { @@ -421,7 +424,7 @@ pub const meshes = struct { } pub fn register(assetFolder: []const u8, _: []const u8, json: JsonElement) !void { - _modelIndices[meshes.size] = models.getModelIndex(json.get([]const u8, "model", "cube")); + _modelIndex[meshes.size] = models.getModelIndex(json.get([]const u8, "model", "cube")); // The actual model is loaded later, in the rendering thread. // But textures can be loaded here: diff --git a/src/chunk.zig b/src/chunk.zig index 8d79d2f5..0f8079ad 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -482,7 +482,7 @@ pub const meshing = struct { } fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool { - const model = &models.voxelModels.items[blocks.meshes.modelIndices(block)]; + const model = &models.voxelModels.items[blocks.meshes.modelIndex(block)]; const freestandingModel = blk:{ switch(neighbor) { Neighbors.dirNegX => { @@ -509,9 +509,8 @@ pub const meshing = struct { return block.typ != 0 and ( freestandingModel or other.typ == 0 - or false // TODO: Blocks.mode(other).checkTransparency(other, neighbor) // TODO: make blocks.meshes.modelIndices(other) != 0 more strict to avoid overdraw. or (!std.meta.eql(block, other) and other.viewThrough()) - or blocks.meshes.modelIndices(other) != 0 + or blocks.meshes.modelIndex(other) != 0 // TODO: make this more strict to avoid overdraw. ); } @@ -538,7 +537,7 @@ pub const meshing = struct { if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) { const normal: u32 = i; const positionNormal: u32 = @intCast(u32, x2) | @intCast(u32, y2)<<5 | @intCast(u32, z2)<<10 | normal<<24; - const textureModel = blocks.meshes.textureIndices(block)[i] | @as(u32, blocks.meshes.modelIndices(block))<<16; + const textureModel = blocks.meshes.textureIndices(block)[i] | @as(u32, blocks.meshes.modelIndex(block))<<16; try self.faces.append(positionNormal); try self.faces.append(textureModel); } @@ -649,8 +648,8 @@ pub const meshing = struct { const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor); const normal: u32 = neighbor; const position: u32 = @intCast(u32, nx) | @intCast(u32, ny)<<5 | @intCast(u32, nz)<<10 | normal<<24; - const newTextureNormal = blocks.meshes.textureIndices(newBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(newBlock))<<16; - const oldTextureNormal = blocks.meshes.textureIndices(oldBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(oldBlock))<<16; + const newTextureNormal = blocks.meshes.textureIndices(newBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(newBlock))<<16; + const oldTextureNormal = blocks.meshes.textureIndices(oldBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(oldBlock))<<16; if(canBeSeenThroughOtherBlock(oldBlock, neighborBlock, neighbor) != newVisibility) { if(newVisibility) { // Adding the face if(neighborMesh == self) { @@ -677,8 +676,8 @@ pub const meshing = struct { const newVisibility = canBeSeenThroughOtherBlock(neighborBlock, newBlock, neighbor ^ 1); const normal: u32 = neighbor ^ 1; const position: u32 = @intCast(u32, x) | @intCast(u32, y)<<5 | @intCast(u32, z)<<10 | normal<<24; - const newTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(neighborBlock))<<16; - const oldTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(neighborBlock))<<16; + const newTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(neighborBlock))<<16; + const oldTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(neighborBlock))<<16; if(canBeSeenThroughOtherBlock(neighborBlock, oldBlock, neighbor ^ 1) != newVisibility) { if(newVisibility) { // Adding the face if(neighborMesh == self) { @@ -757,14 +756,14 @@ pub const meshing = struct { if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { const normal: u32 = neighbor; const position: u32 = @as(u32, otherX) | @as(u32, otherY)<<5 | @as(u32, otherZ)<<10 | normal<<24; - const textureNormal = blocks.meshes.textureIndices(block)[neighbor] | @as(u32, blocks.meshes.modelIndices(block))<<16; + const textureNormal = blocks.meshes.textureIndices(block)[neighbor] | @as(u32, blocks.meshes.modelIndex(block))<<16; try additionalNeighborFaces.append(position); try additionalNeighborFaces.append(textureNormal); } 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 | normal<<24; - const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndices(otherBlock))<<16; + const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndex(otherBlock))<<16; try self.faces.append(position); try self.faces.append(textureNormal); } @@ -818,7 +817,7 @@ pub const meshing = struct { 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 | normal<<24; - const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndices(otherBlock))<<16; + const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndex(otherBlock))<<16; try self.faces.append(position); try self.faces.append(textureNormal); } diff --git a/src/main.zig b/src/main.zig index d005860c..edfbca3c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,8 +8,9 @@ const game = @import("game.zig"); const graphics = @import("graphics.zig"); const items = @import("items.zig"); const models = @import("models.zig"); -const renderer = @import("renderer.zig"); const network = @import("network.zig"); +const renderer = @import("renderer.zig"); +const rotation = @import("rotation.zig"); const settings = @import("settings.zig"); const utils = @import("utils.zig"); @@ -249,6 +250,9 @@ pub fn main() !void { graphics.init(); defer graphics.deinit(); + try rotation.init(); + defer rotation.deinit(); + try models.init(); defer models.deinit(); diff --git a/src/renderer.zig b/src/renderer.zig index 66db3dbd..184e4db2 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -637,7 +637,7 @@ pub const MeshSelection = struct { const block = RenderStructure.getBlock(x, y, z) orelse break; if(block.typ != 0) { // Check the true bounding box (using this algorithm here: https://tavianator.com/2011/ray_box.html): - const voxelModel = &models.voxelModels.items[blocks.meshes.modelIndices(block)]; + const voxelModel = &models.voxelModels.items[blocks.meshes.modelIndex(block)]; const tx1 = (@intToFloat(f64, x) + @intToFloat(f64, voxelModel.minX)/16.0 - pos[0])*invDirX; const tx2 = (@intToFloat(f64, x) + @intToFloat(f64, voxelModel.maxX)/16.0 - pos[0])*invDirX; const ty1 = (@intToFloat(f64, y) + @intToFloat(f64, voxelModel.minY)/16.0 - pos[1])*invDirY; @@ -774,7 +774,7 @@ pub const MeshSelection = struct { pub fn render(projectionMatrix: Mat4f, viewMatrix: Mat4f, playerPos: Vec3d) void { if(selectedBlockPos) |_selectedBlockPos| { var block = RenderStructure.getBlock(_selectedBlockPos[0], _selectedBlockPos[1], _selectedBlockPos[2]) orelse return; - var voxelModel = &models.voxelModels.items[blocks.meshes.modelIndices(block)]; + var voxelModel = &models.voxelModels.items[blocks.meshes.modelIndex(block)]; shader.bind(); c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast([*c]const f32, &projectionMatrix)); diff --git a/src/rotation.zig b/src/rotation.zig new file mode 100644 index 00000000..996fe007 --- /dev/null +++ b/src/rotation.zig @@ -0,0 +1,129 @@ +const std = @import("std"); + +const blocks = @import("blocks.zig"); +const Block = blocks.Block; +const main = @import("main.zig"); + + +/// Each block gets 16 bit of additional storage(apart from the reference to the block type). +/// These 16 bits are accessed and interpreted by the `RotationMode`. +/// With the `RotationMode` interface there is almost no limit to what can be done with those 16 bit. +pub const RotationMode = struct { + const DefaultFunctions = struct { + fn modelIndex(block: Block) u16 { + return blocks.meshes.modelIndexStart(block); + } + }; + + id: []const u8, + /// if the block should be destroyed or changed when a certain neighbor is removed. + dependsOnNeighbors: bool = false, + + modelIndex: *const fn(block: Block) u16 = &DefaultFunctions.modelIndex, +}; + +//public interface RotationMode extends RegistryElement { +// /** +// * Called when generating the chunk mesh. +// * @param bi +// * @param vertices +// * @param faces +// * @return incremented renderIndex +// */ +// void generateChunkMesh(BlockInstance bi, VertexAttribList vertices, IntSimpleList faces); +// +// /** +// * Update or place a block. +// * @param world +// * @param x +// * @param y +// * @param z +// * @param relativePlayerPosition Position of the player head relative to the (0, 0, 0) corner of the block. +// * @param playerDirection +// * @param relativeDir the direction in which the selected neighbor is. +// * @param currentData 0 if no block was there before. +// * @param blockPlacing true if the position of the block was previously empty/nonsolid. +// * @return true if the placing was successful, false otherwise. +// */ +// boolean generateData(World world, int x, int y, int z, Vector3d relativePlayerPosition, Vector3f playerDirection, Vector3i relativeDir, IntWrapper currentData, boolean blockPlacing); +// +// /** +// * Updates data of a placed block if the RotationMode dependsOnNeighbors(). +// * If the returned value is null, then the block will be removed instead of only updating the data. +// * @param oldBlock +// * @param removedDir given as neighbor index (See NormalChunk.) +// * @return new data +// */ +// int updateData(int oldBlock, int removedDir, int newNeighbor); +// +// /** +// * A RotationMode may even alter the blocks transparency. Here is where it's done. +// * @param block The blocks data +// * @param neighbor the inverted(!) neighbor index(see Neighbors.java). +// */ +// boolean checkTransparency(int block, int neighbor); +// +// /** +// * @return standard data for natural generation. +// */ +// int getNaturalStandard(int block); +// +// /** +// * @param min minimal point of the surrounding block. May be overwritten. +// * @param max maximal point of the surrounding block. May be overwritten. +// */ +// float getRayIntersection(RayAabIntersection intersection, int block, Vector3f min, Vector3f max, Vector3f transformedPosition); +// +// /** +// * Check if the entity would collide with the block. +// * @return Whether the entity and block hitboxes overlap. +// */ +// boolean checkEntity(Vector3d pos, double width, double height, int x, int y, int z, int block); +// +// /** +// * Check if the entity would collide with the block, if its position was changed by `vel`. +// * If a collision occurs, adjust the velocity in way such that the entity does not move inside the block. +// * @param vel Velocity of the entity. The 4th element is reserved for stepping: a y-movement that is done exactly once. +// * @return Returns true if the block behaves like a normal block and therefor needs to be handled like a normal block in the specified direction. Returns false if everything has been handled already in here. +// */ +// boolean checkEntityAndDoCollision(Entity ent, Vector4d vel, int x, int y, int z, int block); +//} + +var rotationModes: std.StringHashMap(RotationMode) = undefined; + +const RotationModes = struct { + const NoRotation = struct { + const id: []const u8 = "cubyz:no_rotation"; + }; +}; + +pub fn init() !void { + rotationModes = std.StringHashMap(RotationMode).init(main.globalAllocator); + inline for(@typeInfo(RotationModes).Struct.decls) |declaration| { + try register(@field(RotationModes, declaration.name)); + } +} + +pub fn deinit() void { + rotationModes.deinit(); +} + +pub fn getByID(id: []const u8) *RotationMode { + if(rotationModes.getPtr(id)) |mode| return mode; + std.log.warn("Could not find rotation mode {s}. Using cubyz:no_rotation instead.", .{id}); + return rotationModes.getPtr("cubyz:no_rotation").?; +} + +pub fn register(comptime Mode: type) !void { + var result: RotationMode = RotationMode{.id = Mode.id}; + inline for(@typeInfo(RotationMode).Struct.fields) |field| { + if(@hasDecl(Mode, field.name)) { + if(field.field_type == @TypeOf(@field(Mode, field.name))) { + @field(result, field.name) = @field(Mode, field.name); + } else { + @field(result, field.name) = &@field(Mode, field.name); + } + } + } + try rotationModes.putNoClobber(result.id, result); +} \ No newline at end of file