diff --git a/src/rotation/_list.zig b/src/rotation/_list.zig index f6a280e1..18987f4f 100644 --- a/src/rotation/_list.zig +++ b/src/rotation/_list.zig @@ -5,6 +5,7 @@ pub const log = @import("log.zig"); pub const no_rotation = @import("no_rotation.zig"); pub const ore = @import("ore.zig"); pub const planar = @import("planar.zig"); +pub const sign = @import("sign.zig"); pub const stairs = @import("stairs.zig"); pub const texture_pile = @import("texture_pile.zig"); pub const torch = @import("torch.zig"); diff --git a/src/rotation/sign.zig b/src/rotation/sign.zig new file mode 100644 index 00000000..45e25dd6 --- /dev/null +++ b/src/rotation/sign.zig @@ -0,0 +1,162 @@ +const std = @import("std"); + +const main = @import("main"); +const blocks = main.blocks; +const Block = blocks.Block; +const Neighbor = main.chunk.Neighbor; +const ModelIndex = main.models.ModelIndex; +const rotation = main.rotation; +const Degrees = rotation.Degrees; +const RayIntersectionResult = rotation.RayIntersectionResult; +const RotationMode = rotation.RotationMode; +const vec = main.vec; +const Mat4f = vec.Mat4f; +const Vec3f = vec.Vec3f; +const Vec3i = vec.Vec3i; +const ZonElement = main.ZonElement; + +pub const naturalStandard: u16 = 0; +pub const dependsOnNeighbors = true; +var rotatedModels: std.StringHashMap(ModelIndex) = undefined; + +pub fn init() void { + rotatedModels = .init(main.globalAllocator.allocator); +} + +pub fn deinit() void { + rotatedModels.deinit(); +} + +pub fn reset() void { + rotatedModels.clearRetainingCapacity(); +} + +const centerRotations = 8; +const sideRotations = 4; + +pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { + const floorModelId: []const u8 = zon.get([]const u8, "floor", "cubyz:cube"); + const sideModelId: []const u8 = zon.get([]const u8, "side", "cubyz:cube"); + const ceilingModelId: []const u8 = zon.get([]const u8, "ceiling", "cubyz:cube"); + const key: []const u8 = std.mem.concat(main.stackAllocator.allocator, u8, &.{floorModelId, sideModelId, ceilingModelId}) catch unreachable; + defer main.stackAllocator.free(key); + + if(rotatedModels.get(key)) |modelIndex| return modelIndex; + + const floorModel = main.models.getModelIndex(floorModelId).model(); + const sideModel = main.models.getModelIndex(sideModelId).model(); + const ceilingModel = main.models.getModelIndex(ceilingModelId).model(); + var modelIndex: ModelIndex = undefined; + // Rotate the model: + for(0..centerRotations) |i| { + const index = floorModel.transformModel(rotation.rotationMatrixTransform, .{Mat4f.rotationZ(@as(f32, @floatFromInt(i))*2.0*std.math.pi/centerRotations)}); + if(i == 0) modelIndex = index; + } + for(0..centerRotations) |i| { + _ = ceilingModel.transformModel(rotation.rotationMatrixTransform, .{Mat4f.rotationZ(@as(f32, @floatFromInt(i))*2.0*std.math.pi/centerRotations)}); + } + for(0..sideRotations) |i| { + _ = sideModel.transformModel(rotation.rotationMatrixTransform, .{Mat4f.rotationZ(@as(f32, @floatFromInt(i))*2.0*std.math.pi/sideRotations)}); + } + rotatedModels.put(key, modelIndex) catch unreachable; + return modelIndex; +} + +pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + @min(centerRotations*2 + sideRotations, block.data)}; +} + +pub fn rotateZ(data: u16, angle: Degrees) u16 { + const rotationTable: [4][2*centerRotations + sideRotations]u8 = .{ + .{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, + }, + .{ + 2, 3, 4, 5, 6, 7, 0, 1, + 10, 11, 12, 13, 14, 15, 8, 9, + 17, 18, 19, 16, + }, + .{ + 4, 5, 6, 7, 0, 1, 2, 3, + 12, 13, 14, 15, 8, 9, 10, 11, + 18, 19, 16, 17, + }, + .{ + 6, 7, 0, 1, 2, 3, 4, 5, + 14, 15, 8, 9, 10, 11, 12, 13, + 19, 16, 17, 18, + }, + }; + if(data >= 2*centerRotations + sideRotations) return 0; + return rotationTable[@intFromEnum(angle)][data]; +} + +fn getRotationFromDir(dir: Vec3f) u16 { + const x = dir[0]; + const y = dir[1]; + var data: u3 = 0; + if(@abs(x) > @abs(y)) { + if(x < 0) { + data = 0; + } else { + data = 4; + } + if(@abs(x) < 2*@abs(y)) { + if((x < 0) == (y < 0)) { + data +%= 1; + } else { + data -%= 1; + } + } + } else { + if(y < 0) { + data = 2; + } else { + data = 6; + } + if(@abs(y) < 2*@abs(x)) { + if((x < 0) == (y < 0)) { + data -%= 1; + } else { + data +%= 1; + } + } + } + return data; +} + +pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, playerDir: Vec3f, relativeDir: Vec3i, neighbor: ?Neighbor, currentData: *Block, neighborBlock: Block, blockPlacing: bool) bool { + if(neighbor == null) return false; + const neighborModel = blocks.meshes.model(neighborBlock).model(); + const neighborSupport = !neighborBlock.replacable() and neighborModel.neighborFacingQuads[neighbor.?.reverse().toInt()].len != 0; + if(!neighborSupport) return false; + if(!blockPlacing) return false; + currentData.data = switch(Neighbor.fromRelPos(relativeDir) orelse unreachable) { + .dirNegX => 2*centerRotations, + .dirNegY => 2*centerRotations + 1, + .dirPosX => 2*centerRotations + 2, + .dirPosY => 2*centerRotations + 3, + .dirUp => centerRotations + getRotationFromDir(playerDir), + .dirDown => getRotationFromDir(playerDir), + }; + return true; +} + +pub fn updateData(block: *Block, neighbor: Neighbor, neighborBlock: Block) bool { + const neighborModel = blocks.meshes.model(neighborBlock).model(); + const neighborSupport = !neighborBlock.replacable() and neighborModel.neighborFacingQuads[neighbor.reverse().toInt()].len != 0; + if(neighborSupport) return false; + const shouldBeBroken = switch(neighbor) { + .dirNegX => block.data == 2*centerRotations, + .dirNegY => block.data == 2*centerRotations + 1, + .dirPosX => block.data == 2*centerRotations + 2, + .dirPosY => block.data == 2*centerRotations + 3, + .dirDown => block.data < centerRotations, + .dirUp => block.data >= centerRotations and block.data < 2*centerRotations, + }; + if(!shouldBeBroken) return false; + block.* = .{.typ = 0, .data = 0}; + return true; +}