diff --git a/src/blocks.zig b/src/blocks.zig index a15c0d46..fcdc2028 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -414,7 +414,7 @@ pub const meshes = struct { _ = arenaForWorld.reset(.free_all); } - pub inline fn model(block: Block) rotation.RotatedModel { + pub inline fn model(block: Block) u16 { return block.mode().model(block); } diff --git a/src/graphics.zig b/src/graphics.zig index 13912eed..243a02bf 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1880,7 +1880,7 @@ pub fn generateBlockTexture(blockType: u16) Texture { var faceData: main.ListUnmanaged(main.renderer.chunk_meshing.FaceData) = .{}; defer faceData.deinit(main.stackAllocator); - const model = &main.models.models.items[main.blocks.meshes.model(block).modelIndex]; + const model = &main.models.models.items[main.blocks.meshes.model(block)]; model.appendInternalQuadsToList(&faceData, main.stackAllocator, block, 1, 1, 1, false); for(main.chunk.Neighbors.iterable) |neighbor| { model.appendNeighborFacingQuadsToList(&faceData, main.stackAllocator, block, neighbor, 1 + main.chunk.Neighbors.relX[neighbor], 1 + main.chunk.Neighbors.relY[neighbor], 1 + main.chunk.Neighbors.relZ[neighbor], false); diff --git a/src/itemdrop.zig b/src/itemdrop.zig index 94fb9f14..78fcc5f2 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -761,7 +761,7 @@ pub const ItemDropRenderer = struct { if(item == .baseItem and item.baseItem.block != null) { const blockType = item.baseItem.block.?; const block = blocks.Block{.typ = blockType, .data = 0}; - c.glUniform1i(itemUniforms.modelIndex, block.mode().model(block).modelIndex); + c.glUniform1i(itemUniforms.modelIndex, block.mode().model(block)); c.glUniform1i(itemUniforms.block, blockType); } else { const index = getModelIndex(item); diff --git a/src/models.zig b/src/models.zig index 4b216325..8668ade5 100644 --- a/src/models.zig +++ b/src/models.zig @@ -14,7 +14,7 @@ const NeverFailingAllocator = main.utils.NeverFailingAllocator; var quadSSBO: graphics.SSBO = undefined; -const QuadInfo = extern struct { +pub const QuadInfo = extern struct { normal: Vec3f, corners: [4]Vec3f, cornerUV: [4]Vec2f, @@ -98,7 +98,7 @@ const Model = struct { allocator.free(self.internalQuads); } - pub fn transformModel(model: Model, transformMatrix: Mat4f) u16 { + pub fn transformModel(model: Model, transformFunction: anytype, transformFunctionParameters: anytype) u16 { var quadList = main.List(QuadInfo).init(main.stackAllocator); defer quadList.deinit(); for(model.internalQuads) |quadIndex| { @@ -114,10 +114,7 @@ const Model = struct { } } for(quadList.items) |*quad| { - quad.normal = vec.xyz(Mat4f.mulVec(transformMatrix, vec.combine(quad.normal, 0))); - for(&quad.corners) |*corner| { - corner.* = vec.xyz(Mat4f.mulVec(transformMatrix, vec.combine(corner.* - Vec3f{0.5, 0.5, 0.5}, 1))) + Vec3f{0.5, 0.5, 0.5}; - } + @call(.auto, transformFunction, .{quad} ++ transformFunctionParameters); } return Model.init(main.globalAllocator, quadList.items); } diff --git a/src/renderer.zig b/src/renderer.zig index d2d2f3dc..2a1bd19b 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -731,7 +731,7 @@ pub const MeshSelection = struct { if(block.typ != 0) { // Check the true bounding box (using this algorithm here: https://tavianator.com/2011/ray_box.html): const model = blocks.meshes.model(block); - const modelData = &models.models.items[model.modelIndex]; + const modelData = &models.models.items[model]; const min: Vec3d = @floatCast(modelData.min); const max: Vec3d = @floatCast(modelData.max); const voxelPosFloat: Vec3d = @floatFromInt(voxelPos); @@ -864,7 +864,7 @@ pub const MeshSelection = struct { c.glPolygonOffset(-2, 0); const block = mesh_storage.getBlockFromRenderThread(_selectedBlockPos[0], _selectedBlockPos[1], _selectedBlockPos[2]) orelse return; const model = blocks.meshes.model(block); - const modelData = &models.models.items[model.modelIndex]; + const modelData = &models.models.items[model]; const min: Vec3f = @floatCast(modelData.min); const max: Vec3f = @floatCast(modelData.max); drawCube(projectionMatrix, viewMatrix, @as(Vec3d, @floatFromInt(_selectedBlockPos)) - playerPos, min, max); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 3abbdcba..54c6211e 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -188,20 +188,20 @@ const PrimitiveMesh = struct { fn appendInternalQuadsToCore(self: *PrimitiveMesh, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { const model = blocks.meshes.model(block); - models.models.items[model.modelIndex].appendInternalQuadsToList(&self.coreFaces, main.globalAllocator, block, x, y, z, backFace); + models.models.items[model].appendInternalQuadsToList(&self.coreFaces, main.globalAllocator, block, x, y, z, backFace); } fn appendNeighborFacingQuadsToCore(self: *PrimitiveMesh, block: Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool) void { const model = blocks.meshes.model(block); - models.models.items[model.modelIndex].appendNeighborFacingQuadsToList(&self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace); + models.models.items[model].appendNeighborFacingQuadsToList(&self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace); } fn appendNeighborFacingQuadsToNeighbor(self: *PrimitiveMesh, block: Block, neighbor: u3, x: i32, y: i32, z: i32, comptime backFace: bool, comptime isLod: bool) void { const model = blocks.meshes.model(block); if(isLod) { - models.models.items[model.modelIndex].appendNeighborFacingQuadsToList(&self.neighborFacesHigherLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); + models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesHigherLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); } else { - models.models.items[model.modelIndex].appendNeighborFacingQuadsToList(&self.neighborFacesSameLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); + models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesSameLod[neighbor ^ 1], main.globalAllocator, block, neighbor, x, y, z, backFace); } } @@ -526,13 +526,13 @@ pub const ChunkMesh = struct { fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool { const rotatedModel = blocks.meshes.model(block); - const model = &models.models.items[rotatedModel.modelIndex]; + const model = &models.models.items[rotatedModel]; _ = neighbor; _ = model; // TODO: Check if the neighbor model occludes this one. (maybe not that relevant) return block.typ != 0 and ( other.typ == 0 or (!std.meta.eql(block, other) and other.viewThrough()) - or blocks.meshes.model(other).modelIndex != 0 + or blocks.meshes.model(other) != 0 ); } diff --git a/src/rotation.zig b/src/rotation.zig index 52c165cc..87cfa907 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -12,124 +12,14 @@ const Vec3d = vec.Vec3d; const Mat4f = vec.Mat4f; -pub const Permutation = packed struct(u6) { - /// 0 if x is x, 1 if x and y are swapped, 2 if x and z are swapped - permutationX: u2 = 0, - /// whether y and z are swapped (applied after permutationX) - permutationYZ: bool = false, - /// whether the x coordinate of the original model(applied before permutations) is flipped - mirrorX: bool = false, - /// whether the y coordinate of the original model(applied before permutations) is flipped - mirrorY: bool = false, - /// whether the z coordinate of the original model(applied before permutations) is flipped - mirrorZ: bool = false, - - pub fn toInt(self: Permutation) u6 { - return @bitCast(self); - } - - pub fn transform(self: Permutation, _x: anytype) @TypeOf(_x) { - var x = _x; - if(@typeInfo(@TypeOf(x)) != .Vector) @compileError("Can only transform vector types."); - if(@typeInfo(@TypeOf(x)).Vector.len != 3) @compileError("Vector needs to have length 3."); - if(self.mirrorX) x[0] = -x[0]; - if(self.mirrorY) x[1] = -x[1]; - if(self.mirrorZ) x[2] = -x[2]; - switch(self.permutationX) { - 0 => {}, - 1 => { - const swap = x[0]; - x[0] = x[1]; - x[1] = swap; - }, - 2 => { - const swap = x[0]; - x[0] = x[2]; - x[2] = swap; - }, - else => unreachable, - } - if(self.permutationYZ) { - const swap = x[1]; - x[1] = x[2]; - x[2] = swap; - } - return x; - } - - pub fn permuteNeighborIndex(self: Permutation, neighbor: u3) u3 { - // TODO: Make this more readable. Not sure how though. - const mirrored: u3 = switch(neighbor) { - Neighbors.dirNegX, - Neighbors.dirPosX => ( - if(self.mirrorX) neighbor ^ 1 - else neighbor - ), - Neighbors.dirNegY, - Neighbors.dirPosY => ( - if(self.mirrorY) neighbor ^ 1 - else neighbor - ), - Neighbors.dirDown, - Neighbors.dirUp => ( - if(self.mirrorZ) neighbor ^ 1 - else neighbor - ), - else => unreachable, - }; - const afterXPermutation: u3 = switch(mirrored) { - Neighbors.dirNegX, - Neighbors.dirPosX => ( - if(self.permutationX == 1) mirrored +% (Neighbors.dirNegY -% Neighbors.dirNegX) - else if(self.permutationX == 2) mirrored +% (Neighbors.dirDown -% Neighbors.dirNegX) - else mirrored - ), - Neighbors.dirNegY, - Neighbors.dirPosY => ( - if(self.permutationX == 1) mirrored +% (Neighbors.dirNegX -% Neighbors.dirNegY) - else mirrored - ), - Neighbors.dirDown, - Neighbors.dirUp => ( - if(self.permutationX == 2) mirrored +% (Neighbors.dirNegX -% Neighbors.dirDown) - else mirrored - ), - else => unreachable, - }; - const afterYZPermutation: u3 = switch(afterXPermutation) { - Neighbors.dirNegX, - Neighbors.dirPosX => afterXPermutation, - Neighbors.dirNegY, - Neighbors.dirPosY => ( - if(self.permutationYZ) afterXPermutation +% (Neighbors.dirDown -% Neighbors.dirNegY) - else afterXPermutation - ), - Neighbors.dirDown, - Neighbors.dirUp => ( - if(self.permutationYZ) afterXPermutation +% (Neighbors.dirNegY -% Neighbors.dirDown) - else afterXPermutation - ), - else => unreachable, - }; - return afterYZPermutation; - } -}; - -pub const RotatedModel = struct { - modelIndex: u16, - permutation: Permutation = Permutation{}, -}; - // TODO: Why not just use a tagged union? /// 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 model(block: Block) RotatedModel { - return RotatedModel{ - .modelIndex = blocks.meshes.modelIndexStart(block), - }; + fn model(block: Block) u16 { + return blocks.meshes.modelIndexStart(block); } fn generateData(_: *main.game.World, _: Vec3i, _: Vec3d, _: Vec3f, _: Vec3i, _: *Block, blockPlacing: bool) bool { return blockPlacing; @@ -142,32 +32,17 @@ pub const RotationMode = struct { /// if the block should be destroyed or changed when a certain neighbor is removed. dependsOnNeighbors: bool = false, - model: *const fn(block: Block) RotatedModel = &DefaultFunctions.model, + model: *const fn(block: Block) u16 = &DefaultFunctions.model, + + createBlockModel: *const fn(modelId: []const u8) u16 = &DefaultFunctions.createBlockModel, /// Updates the block data of a block in the world or places a block in the world. /// return true if the placing was successful, false otherwise. generateData: *const fn(world: *main.game.World, pos: Vec3i, relativePlayerPos: Vec3d, playerDir: Vec3f, relativeDir: Vec3i, currentData: *Block, blockPlacing: bool) bool = DefaultFunctions.generateData, - - createBlockModel: *const fn(modelId: []const u8) u16 = &DefaultFunctions.createBlockModel, }; //public interface RotationMode extends RegistryElement { // /** -// * 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 @@ -187,12 +62,6 @@ pub const RotationMode = struct { // * @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. @@ -211,6 +80,13 @@ pub const RotationMode = struct { var rotationModes: std.StringHashMap(RotationMode) = undefined; +fn rotationMatrixTransform(quad: *main.models.QuadInfo, transformMatrix: Mat4f) void { + quad.normal = vec.xyz(Mat4f.mulVec(transformMatrix, vec.combine(quad.normal, 0))); + for(&quad.corners) |*corner| { + corner.* = vec.xyz(Mat4f.mulVec(transformMatrix, vec.combine(corner.* - Vec3f{0.5, 0.5, 0.5}, 1))) + Vec3f{0.5, 0.5, 0.5}; + } +} + // TODO: Instead of using a permutation, rotation modes should directly return a rotated version of the model. const RotationModes = struct { @@ -231,29 +107,26 @@ const RotationModes = struct { rotatedModels.deinit(); } - pub fn model(block: Block) RotatedModel { - return RotatedModel{ - .modelIndex = blocks.meshes.modelIndexStart(block) + @min(block.data, 5), - .permutation = undefined, - }; - } - pub fn createBlockModel(modelId: []const u8) u16 { if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; const baseModelIndex = main.models.getModelIndex(modelId); const baseModel = main.models.models.items[baseModelIndex]; // Rotate the model: - const modelIndex: u16 = baseModel.transformModel(Mat4f.identity()); - _ = baseModel.transformModel(Mat4f.rotationY(std.math.pi)); - _ = baseModel.transformModel(Mat4f.rotationY(std.math.pi/2.0)); - _ = baseModel.transformModel(Mat4f.rotationY(-std.math.pi/2.0)); - _ = baseModel.transformModel(Mat4f.rotationX(-std.math.pi/2.0)); - _ = baseModel.transformModel(Mat4f.rotationX(std.math.pi/2.0)); + const modelIndex: u16 = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.identity()}); + _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationY(std.math.pi)}); + _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationY(std.math.pi/2.0)}); + _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationY(-std.math.pi/2.0)}); + _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationX(-std.math.pi/2.0)}); + _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationX(std.math.pi/2.0)}); rotatedModels.put(modelId, modelIndex) catch unreachable; return modelIndex; } + pub fn model(block: Block) u16 { + return blocks.meshes.modelIndexStart(block) + @min(block.data, 5); + } + pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3d, _: Vec3f, relativeDir: Vec3i, currentData: *Block, blockPlacing: bool) bool { if(blockPlacing) { if(relativeDir[0] == 1) currentData.data = chunk.Neighbors.dirNegX; @@ -269,52 +142,60 @@ const RotationModes = struct { }; pub const Fence = struct { pub const id: []const u8 = "fence"; - // TODO: - fn init() void {} - fn deinit() void {} + pub const dependsOnNeighbors = true; + var fenceModels: std.StringHashMap(u16) = undefined; + const FenceData = packed struct(u4) { + isConnectedNegX: bool, + isConnectedPosX: bool, + isConnectedNegY: bool, + isConnectedPosY: bool, + }; - pub fn model(block: Block) RotatedModel { - const data = block.data>>2 & 15; // TODO: This is just for compatibility with the java version. Remove it. - const modelIndexOffsets = [16]u16 { - 0, // 0b0000 - 1, // 0b0001 - 1, // 0b0010 - 3, // 0b0011 - 1, // 0b0100 - 2, // 0b0101 - 2, // 0b0110 - 4, // 0b0111 - 1, // 0b1000 - 2, // 0b1001 - 2, // 0b1010 - 4, // 0b1011 - 3, // 0b1100 - 4, // 0b1101 - 4, // 0b1110 - 5, // 0b1111 - }; - const permutations = [16]Permutation { - Permutation{}, // 0b0000 - Permutation{.mirrorX = true, .mirrorZ = true}, // 0b0001 - Permutation{}, // 0b0010 - Permutation{}, // 0b0011 - Permutation{.permutationX = 2, .mirrorZ = true}, // 0b0100 - Permutation{.mirrorX = true, .mirrorZ = true}, // 0b0101 - Permutation{.permutationX = 2, .mirrorZ = true}, // 0b0110 - Permutation{.permutationX = 2, .mirrorX = true}, // 0b0111 - Permutation{.permutationX = 2, .mirrorX = true}, // 0b1000 - Permutation{.permutationX = 2, .mirrorX = true}, // 0b1001 - Permutation{}, // 0b1010 - Permutation{.permutationX = 2, .mirrorZ = true}, // 0b1011 - Permutation{.permutationX = 2, .mirrorX = true}, // 0b1100 - Permutation{}, // 0b1101 - Permutation{.mirrorX = true, .mirrorZ = true}, // 0b1110 - Permutation{}, // 0b1111 - }; - return RotatedModel{ - .modelIndex = blocks.meshes.modelIndexStart(block) + modelIndexOffsets[data], - .permutation = permutations[data], - }; + fn init() void { + fenceModels = std.StringHashMap(u16).init(main.globalAllocator.allocator); + } + + fn deinit() void { + fenceModels.deinit(); + } + + fn fenceTransform(quad: *main.models.QuadInfo, data: FenceData) void { + for(&quad.corners, &quad.cornerUV) |*corner, *cornerUV| { + if(!data.isConnectedNegX and corner[0] == 0) { + corner[0] = 0.5; + cornerUV[0] = 0.5; + } + if(!data.isConnectedPosX and corner[0] == 1) { + corner[0] = 0.5; + cornerUV[0] = 0.5; + } + if(!data.isConnectedNegY and corner[1] == 0) { + corner[1] = 0.5; + cornerUV[0] = 0.5; + } + if(!data.isConnectedPosY and corner[1] == 1) { + corner[1] = 0.5; + cornerUV[0] = 0.5; + } + } + } + + pub fn createBlockModel(modelId: []const u8) u16 { + if(fenceModels.get(modelId)) |modelIndex| return modelIndex; + + const baseModelIndex = main.models.getModelIndex(modelId); + const baseModel = main.models.models.items[baseModelIndex]; + // Rotate the model: + const modelIndex: u16 = baseModel.transformModel(fenceTransform, .{@as(FenceData, @bitCast(@as(u4, 0)))}); + for(1..16) |fenceData| { + _ = baseModel.transformModel(fenceTransform, .{@as(FenceData, @bitCast(@as(u4, @intCast(fenceData))))}); + } + fenceModels.put(modelId, modelIndex) catch unreachable; + return modelIndex; + } + + pub fn model(block: Block) u16 { + return blocks.meshes.modelIndexStart(block) + @min(block.data, 15); } }; };