From 01cfb786aa42d06b600629206684b63d80742f17 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Mar 2025 10:45:09 +0100 Subject: [PATCH] Use a dedicated ModelIndex and QuadIndex to make more clear what it is and how to use it. I also bumped model index size to 32 bits (storage is currently limited to 20 bits though) We can afford this, since it's CPU side and it's only stored perblock type. --- src/blocks.zig | 7 +- src/game.zig | 16 +-- src/graphics.zig | 2 +- src/itemdrop.zig | 11 +-- src/models.zig | 76 +++++++++------ src/renderer/chunk_meshing.zig | 46 ++++----- src/renderer/lighting.zig | 4 +- src/renderer/mesh_storage.zig | 10 +- src/rotation.zig | 172 ++++++++++++++++----------------- 9 files changed, 178 insertions(+), 166 deletions(-) diff --git a/src/blocks.zig b/src/blocks.zig index adc6eeed..963dea1a 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -11,6 +11,7 @@ const Color = graphics.Color; const TextureArray = graphics.TextureArray; const items = @import("items.zig"); const models = @import("models.zig"); +const ModelIndex = models.ModelIndex; const rotation = @import("rotation.zig"); const RotationMode = rotation.RotationMode; const Degrees = rotation.Degrees; @@ -436,7 +437,7 @@ pub const meshes = struct { // MARK: meshes fogColor: u32, }; var size: u32 = 0; - var _modelIndex: [maxBlockCount]u16 = undefined; + var _modelIndex: [maxBlockCount]ModelIndex = undefined; var textureData: [maxBlockCount]TextureData = 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; @@ -546,11 +547,11 @@ pub const meshes = struct { // MARK: meshes _ = arenaForWorld.reset(.free_all); } - pub inline fn model(block: Block) u16 { + pub inline fn model(block: Block) ModelIndex { return block.mode().model(block); } - pub inline fn modelIndexStart(block: Block) u16 { + pub inline fn modelIndexStart(block: Block) ModelIndex { return _modelIndex[block.typ]; } diff --git a/src/game.zig b/src/game.zig index fdb03a62..16ed353e 100644 --- a/src/game.zig +++ b/src/game.zig @@ -154,13 +154,13 @@ pub const collision = struct { var resultBox: ?Box = null; var minDistance: f64 = std.math.floatMax(f64); if(block.collide()) { - const model = &models.models.items[block.mode().model(block)]; + const model = block.mode().model(block).model(); const pos = Vec3d{@floatFromInt(x), @floatFromInt(y), @floatFromInt(z)}; for(model.neighborFacingQuads) |quads| { for(quads) |quadIndex| { - const quad = &models.quads.items[quadIndex]; + const quad = quadIndex.quadInfo(); if(triangleAABB(.{quad.corners[0] + quad.normal + pos, quad.corners[2] + quad.normal + pos, quad.corners[1] + quad.normal + pos}, entityPosition, entityBoundingBoxExtent)) { const min = @min(@min(quad.corners[0], quad.corners[1]), @min(quad.corners[2], quad.corners[3])) + quad.normal + pos; const max = @max(@max(quad.corners[0], quad.corners[1]), @max(quad.corners[2], quad.corners[3])) + quad.normal + pos; @@ -189,7 +189,7 @@ pub const collision = struct { } for(model.internalQuads) |quadIndex| { - const quad = &models.quads.items[quadIndex]; + const quad = quadIndex.quadInfo(); if(triangleAABB(.{quad.corners[0] + pos, quad.corners[2] + pos, quad.corners[1] + pos}, entityPosition, entityBoundingBoxExtent)) { const min = @min(@min(quad.corners[0], quad.corners[1]), @min(quad.corners[2], quad.corners[3])) + pos; const max = @max(@max(quad.corners[0], quad.corners[1]), @max(quad.corners[2], quad.corners[3])) + pos; @@ -303,8 +303,8 @@ pub const collision = struct { const blockPos: Vec3d = .{@floatFromInt(x), @floatFromInt(y), @floatFromInt(z)}; const blockBox: Box = .{ - .min = blockPos + @as(Vec3d, @floatCast(main.models.models.items[block.mode().model(block)].min)), - .max = blockPos + @as(Vec3d, @floatCast(main.models.models.items[block.mode().model(block)].max)), + .min = blockPos + @as(Vec3d, @floatCast(block.mode().model(block).model().min)), + .max = blockPos + @as(Vec3d, @floatCast(block.mode().model(block).model().max)), }; if(boundingBox.min[2] > blockBox.max[2] or boundingBox.max[2] < blockBox.min[2]) { @@ -365,17 +365,17 @@ pub const collision = struct { } fn isBlockIntersecting(block: Block, posX: i32, posY: i32, posZ: i32, center: Vec3d, extent: Vec3d) bool { - const model = &models.models.items[block.mode().model(block)]; + const model = block.mode().model(block).model(); const position = Vec3d{@floatFromInt(posX), @floatFromInt(posY), @floatFromInt(posZ)}; for(model.neighborFacingQuads) |quads| { for(quads) |quadIndex| { - const quad = &models.quads.items[quadIndex]; + const quad = quadIndex.quadInfo(); if(triangleAABB(.{quad.corners[0] + quad.normal + position, quad.corners[2] + quad.normal + position, quad.corners[1] + quad.normal + position}, center, extent) or triangleAABB(.{quad.corners[1] + quad.normal + position, quad.corners[2] + quad.normal + position, quad.corners[3] + quad.normal + position}, center, extent)) return true; } } for(model.internalQuads) |quadIndex| { - const quad = &models.quads.items[quadIndex]; + const quad = quadIndex.quadInfo(); if(triangleAABB(.{quad.corners[0] + position, quad.corners[2] + position, quad.corners[1] + position}, center, extent) or triangleAABB(.{quad.corners[1] + position, quad.corners[2] + position, quad.corners[3] + position}, center, extent)) return true; } diff --git a/src/graphics.zig b/src/graphics.zig index d684893d..14532c4e 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -2081,7 +2081,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)]; + const model = main.blocks.meshes.model(block).model(); if(block.hasBackFace()) { model.appendInternalQuadsToList(&faceData, main.stackAllocator, block, 1, 1, 1, true); for(main.chunk.Neighbor.iterable) |neighbor| { diff --git a/src/itemdrop.zig b/src/itemdrop.zig index 172dc5da..c0ab7842 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -571,19 +571,18 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer // Find sizes and free index: var block = blocks.Block{.typ = self.item.baseItem.block.?, .data = 0}; block.data = block.mode().naturalStandard; - const modelIndex = blocks.meshes.model(block); - const model = &main.models.models.items[modelIndex]; + const model = blocks.meshes.model(block).model(); var data = main.List(u32).init(main.stackAllocator); defer data.deinit(); for(model.internalQuads) |quad| { - const textureIndex = blocks.meshes.textureIndex(block, main.models.quads.items[quad].textureSlot); - data.append(@as(u32, quad) << 16 | textureIndex); // modelAndTexture + const textureIndex = blocks.meshes.textureIndex(block, quad.quadInfo().textureSlot); + data.append(@as(u32, quad.index) << 16 | textureIndex); // modelAndTexture data.append(0); // offsetByNormal } for(model.neighborFacingQuads) |list| { for(list) |quad| { - const textureIndex = blocks.meshes.textureIndex(block, main.models.quads.items[quad].textureSlot); - data.append(@as(u32, quad) << 16 | textureIndex); // modelAndTexture + const textureIndex = blocks.meshes.textureIndex(block, quad.quadInfo().textureSlot); + data.append(@as(u32, quad.index) << 16 | textureIndex); // modelAndTexture data.append(1); // offsetByNormal } } diff --git a/src/models.zig b/src/models.zig index b722e8d6..6a0379d8 100644 --- a/src/models.zig +++ b/src/models.zig @@ -49,11 +49,31 @@ const Quad = struct { uvs: [4]usize, }; +pub const ModelIndex = packed struct { + index: u32, + + pub fn model(self: ModelIndex) *const Model { + return &models.items()[self.index]; + } +}; + +pub const QuadIndex = packed struct { + index: u16, + + pub fn quadInfo(self: QuadIndex) *const QuadInfo { + return &quads.items[self.index]; + } + + pub fn extraQuadInfo(self: QuadIndex) *const ExtraQuadInfo { + return &extraQuadInfos.items[self.index]; + } +}; + pub const Model = struct { min: Vec3f, max: Vec3f, - internalQuads: []u16, - neighborFacingQuads: [6][]u16, + internalQuads: []QuadIndex, + neighborFacingQuads: [6][]QuadIndex, isNeighborOccluded: [6]bool, allNeighborsOccluded: bool, noNeighborsOccluded: bool, @@ -88,7 +108,7 @@ pub const Model = struct { return @popCount(@as(u3, @bitCast(hasTwoOnes))) == 2 and @popCount(@as(u3, @bitCast(hasTwoZeroes))) == 2; } - pub fn init(quadInfos: []const QuadInfo) u16 { + pub fn init(quadInfos: []const QuadInfo) ModelIndex { const adjustedQuads = main.stackAllocator.alloc(QuadInfo, quadInfos.len); defer main.stackAllocator.free(adjustedQuads); for(adjustedQuads, quadInfos) |*dest, *src| { @@ -103,7 +123,7 @@ pub const Model = struct { // Snap the normals as well: dest.normal = snapToGrid(dest.normal); } - const modelIndex: u16 = @intCast(models.items.len); + const modelIndex: ModelIndex = .{.index = models.len}; const self = models.addOne(); var amounts: [6]usize = .{0, 0, 0, 0, 0, 0}; var internalAmount: usize = 0; @@ -123,9 +143,9 @@ pub const Model = struct { } for(0..6) |i| { - self.neighborFacingQuads[i] = main.globalAllocator.alloc(u16, amounts[i]); + self.neighborFacingQuads[i] = main.globalAllocator.alloc(QuadIndex, amounts[i]); } - self.internalQuads = main.globalAllocator.alloc(u16, internalAmount); + self.internalQuads = main.globalAllocator.alloc(QuadIndex, internalAmount); var indices: [6]usize = .{0, 0, 0, 0, 0, 0}; var internalIndex: usize = 0; @@ -153,7 +173,7 @@ pub const Model = struct { self.noNeighborsOccluded = true; for(0..6) |neighbor| { for(self.neighborFacingQuads[neighbor]) |quad| { - if(fullyOccludesNeighbor(&quads.items[quad])) { + if(fullyOccludesNeighbor(quad.quadInfo())) { self.isNeighborOccluded[neighbor] = true; } } @@ -176,7 +196,7 @@ pub const Model = struct { return ind; } - pub fn loadModel(data: []const u8) u16 { + pub fn loadModel(data: []const u8) ModelIndex { const quadInfos = loadRawModelDataFromObj(main.stackAllocator, data); defer main.stackAllocator.free(quadInfos); for(quadInfos) |*quad| { @@ -364,11 +384,11 @@ pub const Model = struct { pub fn getRawFaces(model: Model, quadList: *main.List(QuadInfo)) void { for(model.internalQuads) |quadIndex| { - quadList.append(quads.items[quadIndex]); + quadList.append(quadIndex.quadInfo().*); } for(0..6) |neighbor| { for(model.neighborFacingQuads[neighbor]) |quadIndex| { - var quad = quads.items[quadIndex]; + var quad = quadIndex.quadInfo().*; for(&quad.corners) |*corner| { corner.* += quad.normal; } @@ -377,16 +397,16 @@ pub const Model = struct { } } - pub fn mergeModels(modelList: []u16) u16 { + pub fn mergeModels(modelList: []ModelIndex) ModelIndex { var quadList = main.List(QuadInfo).init(main.stackAllocator); defer quadList.deinit(); for(modelList) |model| { - models.items[model].getRawFaces(&quadList); + model.model().getRawFaces(&quadList); } return Model.init(quadList.items); } - pub fn transformModel(model: Model, transformFunction: anytype, transformFunctionParameters: anytype) u16 { + pub fn transformModel(model: Model, transformFunction: anytype, transformFunctionParameters: anytype) ModelIndex { var quadList = main.List(QuadInfo).init(main.stackAllocator); defer quadList.deinit(); model.getRawFaces(&quadList); @@ -396,9 +416,9 @@ pub const Model = struct { return Model.init(quadList.items); } - fn appendQuadsToList(quadList: []const u16, list: *main.ListUnmanaged(FaceData), allocator: NeverFailingAllocator, block: main.blocks.Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { + fn appendQuadsToList(quadList: []const QuadIndex, list: *main.ListUnmanaged(FaceData), allocator: NeverFailingAllocator, block: main.blocks.Block, x: i32, y: i32, z: i32, comptime backFace: bool) void { for(quadList) |quadIndex| { - const texture = main.blocks.meshes.textureIndex(block, quads.items[quadIndex].textureSlot); + const texture = main.blocks.meshes.textureIndex(block, quadIndex.quadInfo().textureSlot); list.append(allocator, FaceData.init(texture, quadIndex, x, y, z, backFace)); } } @@ -412,22 +432,22 @@ pub const Model = struct { } }; -var nameToIndex: std.StringHashMap(u16) = undefined; +var nameToIndex: std.StringHashMap(ModelIndex) = undefined; -pub fn getModelIndex(string: []const u8) u16 { +pub fn getModelIndex(string: []const u8) ModelIndex { return nameToIndex.get(string) orelse { std.log.err("Couldn't find voxelModel with name: {s}.", .{string}); - return 0; + return .{.index = 0}; }; } -pub var quads: main.List(QuadInfo) = undefined; -pub var extraQuadInfos: main.List(ExtraQuadInfo) = undefined; -pub var models: main.List(Model) = undefined; +var quads: main.List(QuadInfo) = undefined; +var extraQuadInfos: main.List(ExtraQuadInfo) = undefined; +var models: main.VirtualList(Model, 1 << 20) = undefined; -var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo)]u8, u16) = undefined; +var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo)]u8, QuadIndex) = undefined; -fn addQuad(info_: QuadInfo) error{Degenerate}!u16 { +fn addQuad(info_: QuadInfo) error{Degenerate}!QuadIndex { var info = info_; if(quadDeduplication.get(std.mem.toBytes(info))) |id| { return id; @@ -440,7 +460,7 @@ fn addQuad(info_: QuadInfo) error{Degenerate}!u16 { } } if(cornerEqualities >= 2) return error.Degenerate; // One corner equality is fine, since then the quad degenerates to a triangle, which has a non-zero area. - const index: u16 = @intCast(quads.items.len); + const index: QuadIndex = .{.index = @intCast(quads.items.len)}; if(info.opaqueInLod == 2) { info.opaqueInLod = 0; } else { @@ -534,7 +554,7 @@ fn openBox(min: Vec3f, max: Vec3f, uvOffset: Vec2f, openSide: enum {x, y, z}) [4 } } -pub fn registerModel(id: []const u8, data: []const u8) u16 { +pub fn registerModel(id: []const u8, data: []const u8) ModelIndex { const model = Model.loadModel(data); nameToIndex.put(id, model) catch unreachable; return model; @@ -542,7 +562,7 @@ pub fn registerModel(id: []const u8, data: []const u8) u16 { // TODO: Entity models. pub fn init() void { - models = .init(main.globalAllocator); + models = .init(); quads = .init(main.globalAllocator); extraQuadInfos = .init(main.globalAllocator); quadDeduplication = .init(main.globalAllocator.allocator); @@ -553,7 +573,7 @@ pub fn init() void { } pub fn reset() void { - for(models.items) |model| { + for(models.items()) |model| { model.deinit(); } models.clearRetainingCapacity(); @@ -567,7 +587,7 @@ pub fn reset() void { pub fn deinit() void { quadSSBO.deinit(); nameToIndex.deinit(); - for(models.items) |model| { + for(models.items()) |model| { model.deinit(); } models.deinit(); diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 6606dd6b..e5db4ce8 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -7,6 +7,7 @@ const Block = blocks.Block; const chunk = main.chunk; const game = main.game; const models = main.models; +const QuadIndex = models.QuadIndex; const renderer = main.renderer; const graphics = main.graphics; const c = graphics.c; @@ -252,10 +253,10 @@ pub const FaceData = extern struct { }, blockAndQuad: packed struct(u32) { texture: u16, - quadIndex: u16, + quadIndex: QuadIndex, }, - pub inline fn init(texture: u16, quadIndex: u16, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData { + pub inline fn init(texture: u16, quadIndex: QuadIndex, x: i32, y: i32, z: i32, comptime backFace: bool) FaceData { return FaceData{ .position = .{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .isBackFace = backFace}, .blockAndQuad = .{.texture = texture, .quadIndex = quadIndex}, @@ -349,7 +350,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh @floatFromInt(face.position.y), @floatFromInt(face.position.z), }; - for(main.models.quads.items[face.blockAndQuad.quadIndex].corners) |cornerPos| { + for(face.blockAndQuad.quadIndex.quadInfo().corners) |cornerPos| { self.min = @min(self.min, basePos + cornerPos); self.max = @max(self.max, basePos + cornerPos); } @@ -455,8 +456,10 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh return result; } - fn getLight(parent: *ChunkMesh, blockPos: Vec3i, textureIndex: u16, quadIndex: u16) [4]u32 { - const normal = models.quads.items[quadIndex].normal; + fn getLight(parent: *ChunkMesh, blockPos: Vec3i, textureIndex: u16, quadIndex: QuadIndex) [4]u32 { + const quadInfo = quadIndex.quadInfo(); + const extraQuadInfo = quadIndex.extraQuadInfo(); + const normal = quadInfo.normal; if(!blocks.meshes.textureOcclusionData.items[textureIndex]) { // No ambient occlusion (→ no smooth lighting) const fullValues = getLightAt(parent, blockPos[0], blockPos[1], blockPos[2]); var rawVals: [6]u5 = undefined; @@ -465,12 +468,12 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } return packLightValues(@splat(rawVals)); } - if(models.extraQuadInfos.items[quadIndex].hasOnlyCornerVertices) { // Fast path for simple quads. + if(extraQuadInfo.hasOnlyCornerVertices) { // Fast path for simple quads. var rawVals: [4][6]u5 = undefined; for(0..4) |i| { - const vertexPos = models.quads.items[quadIndex].corners[i]; + const vertexPos = quadInfo.corners[i]; const fullPos = blockPos +% @as(Vec3i, @intFromFloat(vertexPos)); - const fullValues = if(models.extraQuadInfos.items[quadIndex].alignedNormalDirection) |dir| + const fullValues = if(extraQuadInfo.alignedNormalDirection) |dir| getCornerLightAligned(parent, fullPos, dir) else getCornerLight(parent, fullPos, normal); @@ -488,7 +491,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh while(dy <= 1) : (dy += 1) { var dz: u31 = 0; while(dz <= 1) : (dz += 1) { - cornerVals[dx][dy][dz] = if(models.extraQuadInfos.items[quadIndex].alignedNormalDirection) |dir| + cornerVals[dx][dy][dz] = if(extraQuadInfo.alignedNormalDirection) |dir| getCornerLightAligned(parent, blockPos +% Vec3i{dx, dy, dz}, dir) else getCornerLight(parent, blockPos +% Vec3i{dx, dy, dz}, normal); @@ -498,7 +501,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } var rawVals: [4][6]u5 = undefined; for(0..4) |i| { - const vertexPos = models.quads.items[quadIndex].corners[i]; + const vertexPos = quadInfo.corners[i]; const lightPos = vertexPos + @as(Vec3f, @floatFromInt(blockPos)); const interp = lightPos - @as(Vec3f, @floatFromInt(blockPos)); var val: [6]f32 = .{0, 0, 0, 0, 0, 0}; @@ -548,7 +551,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh var iStart = i; for(0..7) |normal| { for(coreList) |face| { - if(main.models.extraQuadInfos.items[face.blockAndQuad.quadIndex].alignedNormalDirection) |normalDir| { + if(face.blockAndQuad.quadIndex.extraQuadInfo().alignedNormalDirection) |normalDir| { if(normalDir.toInt() == normal) { fullBuffer[i] = face; i += 1; @@ -568,7 +571,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } for(0..7) |normal| { for(optionalList) |face| { - if(main.models.extraQuadInfos.items[face.blockAndQuad.quadIndex].alignedNormalDirection) |normalDir| { + if(face.blockAndQuad.quadIndex.extraQuadInfo().alignedNormalDirection) |normalDir| { if(normalDir.toInt() == normal) { fullBuffer[i] = face; i += 1; @@ -603,7 +606,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh const dz = z + chunkDz; self.isBackFace = self.face.position.isBackFace; const quadIndex = self.face.blockAndQuad.quadIndex; - const normalVector = models.quads.items[quadIndex].normal; + const normalVector = quadIndex.quadInfo().normal; self.shouldBeCulled = vec.dot(normalVector, @floatFromInt(Vec3i{dx, dy, dz})) > 0; // TODO: Adjust for arbitrary voxel models. const fullDx = dx - @as(i32, @intFromFloat(normalVector[0])); // TODO: This calculation should only be done for border faces. const fullDy = dy - @as(i32, @intFromFloat(normalVector[1])); @@ -750,10 +753,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: chunk.Neighbor) bool { - const rotatedModel = blocks.meshes.model(block); - const model = &models.models.items[rotatedModel]; - _ = model; // TODO: Check if the neighbor model occludes this one. (maybe not that relevant) - return block.typ != 0 and (other.typ == 0 or (block != other and other.viewThrough()) or other.alwaysViewThrough() or !models.models.items[blocks.meshes.model(other)].isNeighborOccluded[neighbor.reverse().toInt()]); + const rotatedModel = blocks.meshes.model(block).model(); + _ = rotatedModel; // TODO: Check if the neighbor model occludes this one. (maybe not that relevant) + return block.typ != 0 and (other.typ == 0 or (block != other and other.viewThrough()) or other.alwaysViewThrough() or !blocks.meshes.model(other).model().isNeighborOccluded[neighbor.reverse().toInt()]); } fn initLight(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void { @@ -851,13 +853,13 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } fn appendInternalQuads(block: Block, x: i32, y: i32, z: i32, comptime backFace: bool, list: *main.ListUnmanaged(FaceData), allocator: main.heap.NeverFailingAllocator) void { - const model = blocks.meshes.model(block); - models.models.items[model].appendInternalQuadsToList(list, allocator, block, x, y, z, backFace); + const model = blocks.meshes.model(block).model(); + model.appendInternalQuadsToList(list, allocator, block, x, y, z, backFace); } fn appendNeighborFacingQuads(block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, list: *main.ListUnmanaged(FaceData), allocator: main.heap.NeverFailingAllocator) void { - const model = blocks.meshes.model(block); - models.models.items[model].appendNeighborFacingQuadsToList(list, allocator, block, neighbor, x, y, z, backFace); + const model = blocks.meshes.model(block).model(); + model.appendNeighborFacingQuadsToList(list, allocator, block, neighbor, x, y, z, backFace); } pub fn generateMesh(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void { @@ -892,7 +894,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh defer main.stackAllocator.free(paletteCache); for(0..self.chunk.data.paletteLength) |i| { const block = self.chunk.data.palette[i]; - const model = &models.models.items[blocks.meshes.model(block)]; + const model = blocks.meshes.model(block).model(); var result: OcclusionInfo = .{}; if(model.noNeighborsOccluded or block.viewThrough()) { result.canSeeAllNeighbors = true; diff --git a/src/renderer/lighting.zig b/src/renderer/lighting.zig index 11701c14..efb5517a 100644 --- a/src/renderer/lighting.zig +++ b/src/renderer/lighting.zig @@ -73,7 +73,7 @@ pub const ChannelChunk = struct { fn calculateIncomingOcclusion(result: *[3]u8, block: blocks.Block, voxelSize: u31, neighbor: chunk.Neighbor) void { if(block.typ == 0) return; - if(main.models.models.items[blocks.meshes.model(block)].isNeighborOccluded[neighbor.toInt()]) { + if(blocks.meshes.model(block).model().isNeighborOccluded[neighbor.toInt()]) { var absorption: [3]u8 = extractColor(block.absorption()); absorption[0] *|= @intCast(voxelSize); absorption[1] *|= @intCast(voxelSize); @@ -86,7 +86,7 @@ pub const ChannelChunk = struct { fn calculateOutgoingOcclusion(result: *[3]u8, block: blocks.Block, voxelSize: u31, neighbor: chunk.Neighbor) void { if(block.typ == 0) return; - const model = &main.models.models.items[blocks.meshes.model(block)]; + const model = blocks.meshes.model(block).model(); if(model.isNeighborOccluded[neighbor.toInt()] and !model.isNeighborOccluded[neighbor.reverse().toInt()]) { // Avoid calculating the absorption twice. var absorption: [3]u8 = extractColor(block.absorption()); absorption[0] *|= @intCast(voxelSize); diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 82c11dc6..0cbb9c1f 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -958,8 +958,7 @@ pub fn addBreakingAnimation(pos: Vec3i, breakingProgress: f32) void { const texture = main.blocks.meshes.blockBreakingTextures.items[animationFrame]; const block = getBlock(pos[0], pos[1], pos[2]) orelse return; - const modelIndex = main.blocks.meshes.model(block); - const model = &main.models.models.items[modelIndex]; + const model = main.blocks.meshes.model(block).model(); for(model.internalQuads) |quadIndex| { addBreakingAnimationFace(pos, quadIndex, texture, null, block.transparent()); @@ -971,7 +970,7 @@ pub fn addBreakingAnimation(pos: Vec3i, breakingProgress: f32) void { } } -fn addBreakingAnimationFace(pos: Vec3i, quadIndex: u16, texture: u16, neighbor: ?chunk.Neighbor, isTransparent: bool) void { +fn addBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, texture: u16, neighbor: ?chunk.Neighbor, isTransparent: bool) void { const worldPos = pos +% if(neighbor) |n| n.relPos() else Vec3i{0, 0, 0}; const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); const mesh = getMeshAndIncreaseRefCount(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return; @@ -1006,7 +1005,7 @@ fn addBreakingAnimationFace(pos: Vec3i, quadIndex: u16, texture: u16, neighbor: }); } -fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: u16, neighbor: ?chunk.Neighbor) void { +fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, neighbor: ?chunk.Neighbor) void { const worldPos = pos +% if(neighbor) |n| n.relPos() else Vec3i{0, 0, 0}; const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); const mesh = getMeshAndIncreaseRefCount(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return; @@ -1022,8 +1021,7 @@ fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: u16, neighbor: ?chunk.Neig pub fn removeBreakingAnimation(pos: Vec3i) void { const block = getBlock(pos[0], pos[1], pos[2]) orelse return; - const modelIndex = main.blocks.meshes.model(block); - const model = &main.models.models.items[modelIndex]; + const model = main.blocks.meshes.model(block).model(); for(model.internalQuads) |quadIndex| { removeBreakingAnimationFace(pos, quadIndex, null); diff --git a/src/rotation.zig b/src/rotation.zig index 2febcf05..e1958cec 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -5,6 +5,7 @@ const Block = blocks.Block; const chunk = @import("chunk.zig"); const Neighbor = chunk.Neighbor; const main = @import("main.zig"); +const ModelIndex = main.models.ModelIndex; const vec = main.vec; const Vec2f = vec.Vec2f; const Vec3i = vec.Vec3i; @@ -31,7 +32,7 @@ pub const Degrees = enum(u2) { /// With the `RotationMode` interface there is almost no limit to what can be done with those 16 bit. pub const RotationMode = struct { // MARK: RotationMode const DefaultFunctions = struct { - fn model(block: Block) u16 { + fn model(block: Block) ModelIndex { return blocks.meshes.modelIndexStart(block); } fn rotateZ(data: u16, _: Degrees) u16 { @@ -40,7 +41,7 @@ pub const RotationMode = struct { // MARK: RotationMode fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, _: Vec3i, _: ?Neighbor, _: *Block, _: Block, blockPlacing: bool) bool { return blockPlacing; } - fn createBlockModel(zon: ZonElement) u16 { + fn createBlockModel(zon: ZonElement) ModelIndex { return main.models.getModelIndex(zon.as([]const u8, "cubyz:cube")); } fn updateData(_: *Block, _: Neighbor, _: Block) bool { @@ -52,10 +53,10 @@ pub const RotationMode = struct { // MARK: RotationMode fn rayIntersection(block: Block, _: ?main.items.Item, relativePlayerPos: Vec3f, playerDir: Vec3f) ?RayIntersectionResult { return rayModelIntersection(blocks.meshes.model(block), relativePlayerPos, playerDir); } - fn rayModelIntersection(modelIndex: u32, relativePlayerPos: Vec3f, playerDir: Vec3f) ?RayIntersectionResult { + fn rayModelIntersection(modelIndex: ModelIndex, relativePlayerPos: Vec3f, playerDir: Vec3f) ?RayIntersectionResult { // Check the true bounding box (using this algorithm here: https://tavianator.com/2011/ray_box.html): const invDir = @as(Vec3f, @splat(1))/playerDir; - const modelData = &main.models.models.items[modelIndex]; + const modelData = modelIndex.model(); const min: Vec3f = modelData.min; const max: Vec3f = modelData.max; const t1 = (min - relativePlayerPos)*invDir; @@ -120,12 +121,12 @@ pub const RotationMode = struct { // MARK: RotationMode /// The default rotation data intended for generation algorithms naturalStandard: u16 = 0, - model: *const fn(block: Block) u16 = &DefaultFunctions.model, + model: *const fn(block: Block) ModelIndex = &DefaultFunctions.model, // Rotates block data counterclockwise around the Z axis. rotateZ: *const fn(data: u16, angle: Degrees) u16 = DefaultFunctions.rotateZ, - createBlockModel: *const fn(zon: ZonElement) u16 = &DefaultFunctions.createBlockModel, + createBlockModel: *const fn(zon: ZonElement) ModelIndex = &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. @@ -161,7 +162,7 @@ pub const RotationModes = struct { }; pub const Log = struct { // MARK: Log pub const id: []const u8 = "log"; - var rotatedModels: std.StringHashMap(u16) = undefined; + var rotatedModels: std.StringHashMap(ModelIndex) = undefined; fn init() void { rotatedModels = .init(main.globalAllocator.allocator); @@ -175,14 +176,13 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; - const baseModelIndex = main.models.getModelIndex(modelId); - const baseModel = main.models.models.items[baseModelIndex]; + const baseModel = main.models.getModelIndex(modelId).model(); // Rotate the model: - const modelIndex: u16 = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.identity()}); + const modelIndex = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.identity()}); _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationY(std.math.pi)}); _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(-std.math.pi/2.0).mul(Mat4f.rotationX(-std.math.pi/2.0))}); _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(std.math.pi/2.0).mul(Mat4f.rotationX(-std.math.pi/2.0))}); @@ -192,8 +192,8 @@ pub const RotationModes = struct { return modelIndex; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + @min(block.data, 5); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + @min(block.data, 5)}; } fn rotateZ(data: u16, angle: Degrees) u16 { @@ -221,7 +221,7 @@ pub const RotationModes = struct { }; pub const Planar = struct { // MARK: Planar pub const id: []const u8 = "planar"; - var rotatedModels: std.StringHashMap(u16) = undefined; + var rotatedModels: std.StringHashMap(ModelIndex) = undefined; fn init() void { rotatedModels = .init(main.globalAllocator.allocator); @@ -235,14 +235,13 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; - const baseModelIndex = main.models.getModelIndex(modelId); - const baseModel = main.models.models.items[baseModelIndex]; + const baseModel = main.models.getModelIndex(modelId).model(); // Rotate the model: - const modelIndex: u16 = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(std.math.pi/2.0)}); + const modelIndex: ModelIndex = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(std.math.pi/2.0)}); _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(-std.math.pi/2.0)}); _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(std.math.pi)}); _ = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.identity()}); @@ -250,8 +249,8 @@ pub const RotationModes = struct { return modelIndex; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + @min(block.data, 3); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + @min(block.data, 3)}; } fn rotateZ(data: u16, angle: Degrees) u16 { @@ -286,7 +285,7 @@ pub const RotationModes = struct { pub const Fence = struct { // MARK: Fence pub const id: []const u8 = "fence"; pub const dependsOnNeighbors = true; - var fenceModels: std.StringHashMap(u16) = undefined; + var fenceModels: std.StringHashMap(ModelIndex) = undefined; const FenceData = packed struct(u4) { isConnectedNegX: bool, isConnectedPosX: bool, @@ -356,14 +355,13 @@ pub const RotationModes = struct { } } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(fenceModels.get(modelId)) |modelIndex| return modelIndex; - const baseModelIndex = main.models.getModelIndex(modelId); - const baseModel = main.models.models.items[baseModelIndex]; + const baseModel = main.models.getModelIndex(modelId).model(); // Rotate the model: - const modelIndex: u16 = baseModel.transformModel(fenceTransform, .{@as(FenceData, @bitCast(@as(u4, 0)))}); + const modelIndex: ModelIndex = baseModel.transformModel(fenceTransform, .{@as(FenceData, @bitCast(@as(u4, 0)))}); for(1..16) |fenceData| { _ = baseModel.transformModel(fenceTransform, .{@as(FenceData, @bitCast(@as(u4, @intCast(fenceData))))}); } @@ -371,15 +369,15 @@ pub const RotationModes = struct { return modelIndex; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + (block.data & 15); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + (block.data & 15)}; } pub fn updateData(block: *Block, neighbor: Neighbor, neighborBlock: Block) bool { - const blockBaseModel = blocks.meshes.modelIndexStart(block.*); - const neighborBaseModel = blocks.meshes.modelIndexStart(neighborBlock); - const neighborModel = blocks.meshes.model(neighborBlock); - const targetVal = neighborBlock.solid() and (blockBaseModel == neighborBaseModel or main.models.models.items[neighborModel].isNeighborOccluded[neighbor.reverse().toInt()]); + const blockBaseModelIndex = blocks.meshes.modelIndexStart(block.*); + const neighborBaseModelIndex = blocks.meshes.modelIndexStart(neighborBlock); + const neighborModel = blocks.meshes.model(neighborBlock).model(); + const targetVal = neighborBlock.solid() and (blockBaseModelIndex == neighborBaseModelIndex or neighborModel.isNeighborOccluded[neighbor.reverse().toInt()]); var currentData: FenceData = @bitCast(@as(u4, @truncate(block.data))); switch(neighbor) { .dirNegX => { @@ -405,7 +403,7 @@ pub const RotationModes = struct { pub const Branch = struct { // MARK: Branch pub const id: []const u8 = "branch"; pub const dependsOnNeighbors = true; - var branchModels: std.AutoHashMap(u32, u16) = undefined; + var branchModels: std.AutoHashMap(u32, ModelIndex) = undefined; const BranchData = packed struct(u6) { enabledConnections: u6, @@ -640,11 +638,11 @@ pub const RotationModes = struct { }; } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const radius = zon.get(f32, "radius", 4); if(branchModels.get(@bitCast(radius))) |modelIndex| return modelIndex; - var modelIndex: u16 = undefined; + var modelIndex: ModelIndex = undefined; for(0..64) |i| { var quads = main.List(main.models.QuadInfo).init(main.stackAllocator); defer quads.deinit(); @@ -668,8 +666,8 @@ pub const RotationModes = struct { return modelIndex; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + (block.data & 63); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + (block.data & 63)}; } fn rotateZ(data: u16, angle: Degrees) u16 { @@ -709,16 +707,16 @@ pub const RotationModes = struct { neighborBlock: Block, blockPlacing: bool, ) bool { - const blockBaseModel = blocks.meshes.modelIndexStart(currentBlock.*); - const neighborBaseModel = blocks.meshes.modelIndexStart(neighborBlock); + const blockBaseModelIndex = blocks.meshes.modelIndexStart(currentBlock.*); + const neighborBaseModelIndex = blocks.meshes.modelIndexStart(neighborBlock); - if(blockPlacing or blockBaseModel == neighborBaseModel or neighborBlock.solid()) { - const neighborModel = blocks.meshes.model(neighborBlock); + if(blockPlacing or blockBaseModelIndex == neighborBaseModelIndex or neighborBlock.solid()) { + const neighborModel = blocks.meshes.model(neighborBlock).model(); var currentData = BranchData.init(currentBlock.data); // Branch block upon placement should extend towards a block it was placed // on if the block is solid or also uses branch model. - const targetVal = ((neighborBlock.solid() and !neighborBlock.viewThrough()) and (blockBaseModel == neighborBaseModel or main.models.models.items[neighborModel].isNeighborOccluded[neighbor.?.reverse().toInt()])); + const targetVal = ((neighborBlock.solid() and !neighborBlock.viewThrough()) and (blockBaseModelIndex == neighborBaseModelIndex or neighborModel.isNeighborOccluded[neighbor.?.reverse().toInt()])); currentData.setConnection(neighbor.?, targetVal); const result: u16 = currentData.enabledConnections; @@ -766,7 +764,7 @@ pub const RotationModes = struct { const directionBitMask = Neighbor.bitMask(direction); if((block.data & directionBitMask) != 0) { - const modelIndex = blocks.meshes.modelIndexStart(block) + directionBitMask; + const modelIndex = ModelIndex{.index = blocks.meshes.modelIndexStart(block).index + directionBitMask}; if(RotationMode.DefaultFunctions.rayModelIntersection(modelIndex, relativePlayerPos, playerDir)) |intersection| { if(@abs(closestIntersectionDistance) > @abs(intersection.distance)) { closestIntersectionDistance = intersection.distance; @@ -793,7 +791,7 @@ pub const RotationModes = struct { }; pub const Stairs = struct { // MARK: Stairs pub const id: []const u8 = "stairs"; - var modelIndex: u16 = 0; + var modelIndex: ?ModelIndex = null; fn subBlockMask(x: u1, y: u1, z: u1) u8 { return @as(u8, 1) << ((@as(u3, x)*2 + @as(u3, y))*2 + z); @@ -834,7 +832,7 @@ pub const RotationModes = struct { fn init() void {} fn deinit() void {} fn reset() void { - modelIndex = 0; + modelIndex = null; } const GreedyFaceInfo = struct {min: Vec2f, max: Vec2f}; @@ -902,10 +900,8 @@ pub const RotationModes = struct { return mem[0..faces]; } - pub fn createBlockModel(_: ZonElement) u16 { - if(modelIndex != 0) { - return modelIndex; - } + pub fn createBlockModel(_: ZonElement) ModelIndex { + if(modelIndex) |idx| return idx; for(0..256) |i| { var quads = main.List(main.models.QuadInfo).init(main.stackAllocator); defer quads.deinit(); @@ -1018,11 +1014,11 @@ pub const RotationModes = struct { modelIndex = index; } } - return modelIndex; + return modelIndex.?; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + (block.data & 255); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + (block.data & 255)}; } pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, _: Vec3i, _: ?Neighbor, currentData: *Block, _: Block, blockPlacing: bool) bool { @@ -1125,7 +1121,7 @@ pub const RotationModes = struct { pub const id: []const u8 = "torch"; pub const naturalStandard: u16 = 1; pub const dependsOnNeighbors = true; - var rotatedModels: std.StringHashMap(u16) = undefined; + var rotatedModels: std.StringHashMap(ModelIndex) = undefined; const TorchData = packed struct(u5) { center: bool, negX: bool, @@ -1146,7 +1142,7 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const baseModelId: []const u8 = zon.get([]const u8, "base", "cubyz:cube"); const sideModelId: []const u8 = zon.get([]const u8, "side", "cubyz:cube"); const key: []const u8 = std.mem.concat(main.stackAllocator.allocator, u8, &.{baseModelId, sideModelId}) catch unreachable; @@ -1154,16 +1150,14 @@ pub const RotationModes = struct { if(rotatedModels.get(key)) |modelIndex| return modelIndex; - const baseModelIndex = main.models.getModelIndex(baseModelId); - const baseModel = main.models.models.items[baseModelIndex]; - const sideModelIndex = main.models.getModelIndex(sideModelId); - const sideModel = main.models.models.items[sideModelIndex]; + const baseModel = main.models.getModelIndex(baseModelId).model(); + const sideModel = main.models.getModelIndex(sideModelId).model(); // Rotate the model: - var centerModel: u16 = undefined; - var negXModel: u16 = undefined; - var posXModel: u16 = undefined; - var negYModel: u16 = undefined; - var posYModel: u16 = undefined; + var centerModel: ModelIndex = undefined; + var negXModel: ModelIndex = undefined; + var posXModel: ModelIndex = undefined; + var negYModel: ModelIndex = undefined; + var posYModel: ModelIndex = undefined; for(1..32) |i| { const torchData: TorchData = @bitCast(@as(u5, @intCast(i))); if(i & i - 1 == 0) { @@ -1173,7 +1167,7 @@ pub const RotationModes = struct { if(torchData.negY) negYModel = sideModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(std.math.pi/2.0)}); if(torchData.posY) posYModel = sideModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationZ(-std.math.pi/2.0)}); } else { - var models: [5]u16 = undefined; + var models: [5]ModelIndex = undefined; var amount: usize = 0; if(torchData.center) { models[amount] = centerModel; @@ -1203,8 +1197,8 @@ pub const RotationModes = struct { return modelIndex; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + (@as(u5, @truncate(block.data)) -| 1); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + (@as(u5, @truncate(block.data)) -| 1)}; } fn rotateZ(data: u16, angle: Degrees) u16 { @@ -1231,8 +1225,8 @@ pub const RotationModes = struct { pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, relativeDir: Vec3i, neighbor: ?Neighbor, currentData: *Block, neighborBlock: Block, _: bool) bool { if(neighbor == null) return false; - const neighborModel = blocks.meshes.model(neighborBlock); - const neighborSupport = neighborBlock.solid() and main.models.models.items[neighborModel].neighborFacingQuads[neighbor.?.reverse().toInt()].len != 0; + const neighborModel = blocks.meshes.model(neighborBlock).model(); + const neighborSupport = neighborBlock.solid() and neighborModel.neighborFacingQuads[neighbor.?.reverse().toInt()].len != 0; if(!neighborSupport) return false; var data: TorchData = @bitCast(@as(u5, @truncate(currentData.data))); if(relativeDir[0] == 1) data.posX = true; @@ -1249,8 +1243,8 @@ pub const RotationModes = struct { } pub fn updateData(block: *Block, neighbor: Neighbor, neighborBlock: Block) bool { - const neighborModel = blocks.meshes.model(neighborBlock); - const neighborSupport = neighborBlock.solid() and main.models.models.items[neighborModel].neighborFacingQuads[neighbor.reverse().toInt()].len != 0; + const neighborModel = blocks.meshes.model(neighborBlock).model(); + const neighborSupport = neighborBlock.solid() and neighborModel.neighborFacingQuads[neighbor.reverse().toInt()].len != 0; var currentData: TorchData = @bitCast(@as(u5, @truncate(block.data))); switch(neighbor) { .dirNegX => { @@ -1282,7 +1276,7 @@ pub const RotationModes = struct { var resultBit: u16 = 0; for([_]u16{1, 2, 4, 8, 16}) |bit| { if(block.data & bit != 0) { - const modelIndex = blocks.meshes.modelIndexStart(block) + bit - 1; + const modelIndex = ModelIndex{.index = blocks.meshes.modelIndexStart(block).index + bit - 1}; if(RotationMode.DefaultFunctions.rayModelIntersection(modelIndex, relativePlayerPos, playerDir)) |intersection| { if(result == null or result.?.distance > intersection.distance) { result = intersection; @@ -1323,7 +1317,7 @@ pub const RotationModes = struct { pub const Carpet = struct { // MARK: Carpet pub const id: []const u8 = "carpet"; pub const naturalStandard: u16 = 0b10000; - var rotatedModels: std.StringHashMap(u16) = undefined; + var rotatedModels: std.StringHashMap(ModelIndex) = undefined; const CarpetData = packed struct(u6) { negX: bool, posX: bool, @@ -1368,19 +1362,18 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; - const baseModelIndex = main.models.getModelIndex(modelId); - const baseModel = main.models.models.items[baseModelIndex]; + const baseModel = main.models.getModelIndex(modelId).model(); // Rotate the model: - var negXModel: u16 = undefined; - var posXModel: u16 = undefined; - var negYModel: u16 = undefined; - var posYModel: u16 = undefined; - var negZModel: u16 = undefined; - var posZModel: u16 = undefined; + var negXModel: ModelIndex = undefined; + var posXModel: ModelIndex = undefined; + var negYModel: ModelIndex = undefined; + var posYModel: ModelIndex = undefined; + var negZModel: ModelIndex = undefined; + var posZModel: ModelIndex = undefined; for(1..64) |i| { const carpetData: CarpetData = @bitCast(@as(u6, @intCast(i))); if(i & i - 1 == 0) { @@ -1391,7 +1384,7 @@ pub const RotationModes = struct { if(carpetData.negZ) negZModel = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.identity()}); if(carpetData.posZ) posZModel = baseModel.transformModel(rotationMatrixTransform, .{Mat4f.rotationY(std.math.pi)}); } else { - var models: [6]u16 = undefined; + var models: [6]ModelIndex = undefined; var amount: usize = 0; if(carpetData.negX) { models[amount] = negXModel; @@ -1425,8 +1418,8 @@ pub const RotationModes = struct { return modelIndex; } - pub fn model(block: Block) u16 { - return blocks.meshes.modelIndexStart(block) + (@as(u6, @truncate(block.data)) -| 1); + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + (@as(u6, @truncate(block.data)) -| 1)}; } pub fn generateData(_: *main.game.World, _: Vec3i, relativePlayerPos: Vec3f, playerDir: Vec3f, relativeDir: Vec3i, _: ?Neighbor, currentData: *Block, neighbor: Block, _: bool) bool { @@ -1460,7 +1453,7 @@ pub const RotationModes = struct { var resultBit: u16 = 0; for([_]u16{1, 2, 4, 8, 16, 32}) |bit| { if(block.data & bit != 0) { - const modelIndex = blocks.meshes.modelIndexStart(block) + bit - 1; + const modelIndex = ModelIndex{.index = blocks.meshes.modelIndexStart(block).index + bit - 1}; if(RotationMode.DefaultFunctions.rayModelIntersection(modelIndex, relativePlayerPos, playerDir)) |intersection| { if(result == null or result.?.distance > intersection.distance) { result = intersection; @@ -1489,7 +1482,7 @@ pub const RotationModes = struct { }; pub const Ore = struct { // MARK: Ore pub const id: []const u8 = "ore"; - var modelCache: ?u16 = null; + var modelCache: ?ModelIndex = null; fn init() void {} fn deinit() void {} @@ -1497,15 +1490,14 @@ pub const RotationModes = struct { modelCache = null; } - pub fn createBlockModel(zon: ZonElement) u16 { + pub fn createBlockModel(zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(!std.mem.eql(u8, modelId, "cubyz:cube")) { std.log.err("Ores can only be use on cube models.", .{modelId}); } if(modelCache) |modelIndex| return modelIndex; - const baseModelIndex = main.models.getModelIndex("cubyz:cube"); - const baseModel = main.models.models.items[baseModelIndex]; + const baseModel = main.models.getModelIndex("cubyz:cube").model(); var quadList = main.List(main.models.QuadInfo).init(main.stackAllocator); defer quadList.deinit(); baseModel.getRawFaces(&quadList); @@ -1526,7 +1518,7 @@ pub const RotationModes = struct { pub fn modifyBlock(block: *Block, newBlockType: u16) bool { if(block.transparent() or block.viewThrough()) return false; - if(!main.models.models.items[main.blocks.meshes.modelIndexStart(block.*)].allNeighborsOccluded) return false; + if(!main.blocks.meshes.modelIndexStart(block.*).model().allNeighborsOccluded) return false; if(block.data != 0) return false; block.data = block.typ; block.typ = newBlockType; @@ -1536,7 +1528,7 @@ pub const RotationModes = struct { pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, _: main.items.ItemStack, shouldDropSourceBlockOnSuccess: *bool) RotationMode.CanBeChangedInto { if(oldBlock == newBlock) return .no; if(oldBlock.transparent() or oldBlock.viewThrough()) return .no; - if(!main.models.models.items[main.blocks.meshes.modelIndexStart(oldBlock)].allNeighborsOccluded) return .no; + if(!main.blocks.meshes.modelIndexStart(oldBlock).model().allNeighborsOccluded) return .no; if(oldBlock.data != 0) return .no; if(newBlock.data != oldBlock.typ) return .no; shouldDropSourceBlockOnSuccess.* = false;