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.
This commit is contained in:
IntegratedQuantum 2025-03-22 10:45:09 +01:00
parent 37eb01ec37
commit 01cfb786aa
9 changed files with 178 additions and 166 deletions

View File

@ -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];
}

View File

@ -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;
}

View File

@ -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| {

View File

@ -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
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;