Start working on rotation modes.

This commit is contained in:
IntegratedQuantum 2022-12-15 14:03:38 +01:00
parent 56f83aedbc
commit cbd3abea49
5 changed files with 160 additions and 25 deletions

View File

@ -8,6 +8,8 @@ const Color = @import("graphics.zig").Color;
const TextureArray = @import("graphics.zig").TextureArray;
const items = @import("items.zig");
const models = @import("models.zig");
const rotation = @import("rotation.zig");
const RotationMode = rotation.RotationMode;
pub const BlockClass = enum(u8) {
wood,
@ -29,8 +31,6 @@ pub const BlockDrop = struct {
amount: f32,
};
pub const RotationMode = u0; // TODO!
var _lightingTransparent: [MaxBLockCount]bool = undefined;
var _transparent: [MaxBLockCount]bool = undefined;
var _id: [MaxBLockCount][]u8 = undefined;
@ -50,7 +50,7 @@ var _light: [MaxBLockCount]u32 = undefined;
var _absorption: [MaxBLockCount]u32 = undefined;
/// GUI that is opened on click.
var _gui: [MaxBLockCount][]u8 = undefined;
var _mode: [MaxBLockCount]RotationMode = undefined;
var _mode: [MaxBLockCount]*RotationMode = undefined;
var reverseIndices = std.StringHashMap(u16).init(arena.allocator());
@ -62,9 +62,8 @@ pub fn register(_: []const u8, id: []const u8, json: JsonElement) !u16 {
}
_id[size] = try allocator.dupe(u8, id);
try reverseIndices.put(_id[size], @intCast(u16, size));
// TODO:
// _mode[size] = CubyzRegistries.ROTATION_MODE_REGISTRY.getByID(json.getString("rotation", "cubyz:no_rotation"));
// _blockDrops[size] = new BlockDrop[0];
_mode[size] = rotation.getByID(json.get([]const u8, "rotation", "cubyz:no_rotation"));
_breakingPower[size] = json.get(f32, "breakingPower", 0);
_hardness[size] = json.get(f32, "hardness", 1);
@ -217,7 +216,7 @@ pub const Block = packed struct {
return _gui[self.typ];
}
pub fn mode(self: Block) RotationMode {
pub fn mode(self: Block) *RotationMode {
return _mode[self.typ];
}
@ -240,7 +239,7 @@ pub const Block = packed struct {
pub const meshes = struct {
var size: u32 = 0;
var _modelIndices: [MaxBLockCount]u16 = undefined;
var _modelIndex: [MaxBLockCount]u16 = undefined;
var _textureIndices: [MaxBLockCount][6]u32 = undefined;
/// Stores the number of textures after each block was added. Used to clean additional textures when the world is switched.
var maxTextureCount: [MaxBLockCount]u32 = undefined;
@ -317,8 +316,12 @@ pub const meshes = struct {
arenaForWorld = std.heap.ArenaAllocator.init(std.heap.page_allocator);
}
pub fn modelIndices(block: Block) u16 {
return (&_modelIndices[block.typ]).*;
pub fn modelIndex(block: Block) u16 {
return block.mode().modelIndex(block);
}
pub fn modelIndexStart(block: Block) u16 {
return _modelIndex[block.typ];
}
pub fn textureIndices(block: Block) *const [6] u32 {
@ -421,7 +424,7 @@ pub const meshes = struct {
}
pub fn register(assetFolder: []const u8, _: []const u8, json: JsonElement) !void {
_modelIndices[meshes.size] = models.getModelIndex(json.get([]const u8, "model", "cube"));
_modelIndex[meshes.size] = models.getModelIndex(json.get([]const u8, "model", "cube"));
// The actual model is loaded later, in the rendering thread.
// But textures can be loaded here:

View File

@ -482,7 +482,7 @@ pub const meshing = struct {
}
fn canBeSeenThroughOtherBlock(block: Block, other: Block, neighbor: u3) bool {
const model = &models.voxelModels.items[blocks.meshes.modelIndices(block)];
const model = &models.voxelModels.items[blocks.meshes.modelIndex(block)];
const freestandingModel = blk:{
switch(neighbor) {
Neighbors.dirNegX => {
@ -509,9 +509,8 @@ pub const meshing = struct {
return block.typ != 0 and (
freestandingModel
or other.typ == 0
or false // TODO: Blocks.mode(other).checkTransparency(other, neighbor) // TODO: make blocks.meshes.modelIndices(other) != 0 more strict to avoid overdraw.
or (!std.meta.eql(block, other) and other.viewThrough())
or blocks.meshes.modelIndices(other) != 0
or blocks.meshes.modelIndex(other) != 0 // TODO: make this more strict to avoid overdraw.
);
}
@ -538,7 +537,7 @@ pub const meshing = struct {
if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) {
const normal: u32 = i;
const positionNormal: u32 = @intCast(u32, x2) | @intCast(u32, y2)<<5 | @intCast(u32, z2)<<10 | normal<<24;
const textureModel = blocks.meshes.textureIndices(block)[i] | @as(u32, blocks.meshes.modelIndices(block))<<16;
const textureModel = blocks.meshes.textureIndices(block)[i] | @as(u32, blocks.meshes.modelIndex(block))<<16;
try self.faces.append(positionNormal);
try self.faces.append(textureModel);
}
@ -649,8 +648,8 @@ pub const meshing = struct {
const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor);
const normal: u32 = neighbor;
const position: u32 = @intCast(u32, nx) | @intCast(u32, ny)<<5 | @intCast(u32, nz)<<10 | normal<<24;
const newTextureNormal = blocks.meshes.textureIndices(newBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(newBlock))<<16;
const oldTextureNormal = blocks.meshes.textureIndices(oldBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(oldBlock))<<16;
const newTextureNormal = blocks.meshes.textureIndices(newBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(newBlock))<<16;
const oldTextureNormal = blocks.meshes.textureIndices(oldBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(oldBlock))<<16;
if(canBeSeenThroughOtherBlock(oldBlock, neighborBlock, neighbor) != newVisibility) {
if(newVisibility) { // Adding the face
if(neighborMesh == self) {
@ -677,8 +676,8 @@ pub const meshing = struct {
const newVisibility = canBeSeenThroughOtherBlock(neighborBlock, newBlock, neighbor ^ 1);
const normal: u32 = neighbor ^ 1;
const position: u32 = @intCast(u32, x) | @intCast(u32, y)<<5 | @intCast(u32, z)<<10 | normal<<24;
const newTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(neighborBlock))<<16;
const oldTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndices(neighborBlock))<<16;
const newTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(neighborBlock))<<16;
const oldTextureNormal = blocks.meshes.textureIndices(neighborBlock)[neighbor] | @as(u32, blocks.meshes.modelIndex(neighborBlock))<<16;
if(canBeSeenThroughOtherBlock(neighborBlock, oldBlock, neighbor ^ 1) != newVisibility) {
if(newVisibility) { // Adding the face
if(neighborMesh == self) {
@ -757,14 +756,14 @@ pub const meshing = struct {
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
const normal: u32 = neighbor;
const position: u32 = @as(u32, otherX) | @as(u32, otherY)<<5 | @as(u32, otherZ)<<10 | normal<<24;
const textureNormal = blocks.meshes.textureIndices(block)[neighbor] | @as(u32, blocks.meshes.modelIndices(block))<<16;
const textureNormal = blocks.meshes.textureIndices(block)[neighbor] | @as(u32, blocks.meshes.modelIndex(block))<<16;
try additionalNeighborFaces.append(position);
try additionalNeighborFaces.append(textureNormal);
}
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) {
const normal: u32 = neighbor ^ 1;
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10 | normal<<24;
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndices(otherBlock))<<16;
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndex(otherBlock))<<16;
try self.faces.append(position);
try self.faces.append(textureNormal);
}
@ -818,7 +817,7 @@ pub const meshing = struct {
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) {
const normal: u32 = neighbor ^ 1;
const position: u32 = @as(u32, x) | @as(u32, y)<<5 | @as(u32, z)<<10 | normal<<24;
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndices(otherBlock))<<16;
const textureNormal = blocks.meshes.textureIndices(otherBlock)[neighbor ^ 1] | @as(u32, blocks.meshes.modelIndex(otherBlock))<<16;
try self.faces.append(position);
try self.faces.append(textureNormal);
}

View File

@ -8,8 +8,9 @@ const game = @import("game.zig");
const graphics = @import("graphics.zig");
const items = @import("items.zig");
const models = @import("models.zig");
const renderer = @import("renderer.zig");
const network = @import("network.zig");
const renderer = @import("renderer.zig");
const rotation = @import("rotation.zig");
const settings = @import("settings.zig");
const utils = @import("utils.zig");
@ -249,6 +250,9 @@ pub fn main() !void {
graphics.init();
defer graphics.deinit();
try rotation.init();
defer rotation.deinit();
try models.init();
defer models.deinit();

View File

@ -637,7 +637,7 @@ pub const MeshSelection = struct {
const block = RenderStructure.getBlock(x, y, z) orelse break;
if(block.typ != 0) {
// Check the true bounding box (using this algorithm here: https://tavianator.com/2011/ray_box.html):
const voxelModel = &models.voxelModels.items[blocks.meshes.modelIndices(block)];
const voxelModel = &models.voxelModels.items[blocks.meshes.modelIndex(block)];
const tx1 = (@intToFloat(f64, x) + @intToFloat(f64, voxelModel.minX)/16.0 - pos[0])*invDirX;
const tx2 = (@intToFloat(f64, x) + @intToFloat(f64, voxelModel.maxX)/16.0 - pos[0])*invDirX;
const ty1 = (@intToFloat(f64, y) + @intToFloat(f64, voxelModel.minY)/16.0 - pos[1])*invDirY;
@ -774,7 +774,7 @@ pub const MeshSelection = struct {
pub fn render(projectionMatrix: Mat4f, viewMatrix: Mat4f, playerPos: Vec3d) void {
if(selectedBlockPos) |_selectedBlockPos| {
var block = RenderStructure.getBlock(_selectedBlockPos[0], _selectedBlockPos[1], _selectedBlockPos[2]) orelse return;
var voxelModel = &models.voxelModels.items[blocks.meshes.modelIndices(block)];
var voxelModel = &models.voxelModels.items[blocks.meshes.modelIndex(block)];
shader.bind();
c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast([*c]const f32, &projectionMatrix));

129
src/rotation.zig Normal file
View File

@ -0,0 +1,129 @@
const std = @import("std");
const blocks = @import("blocks.zig");
const Block = blocks.Block;
const main = @import("main.zig");
/// Each block gets 16 bit of additional storage(apart from the reference to the block type).
/// These 16 bits are accessed and interpreted by the `RotationMode`.
/// With the `RotationMode` interface there is almost no limit to what can be done with those 16 bit.
pub const RotationMode = struct {
const DefaultFunctions = struct {
fn modelIndex(block: Block) u16 {
return blocks.meshes.modelIndexStart(block);
}
};
id: []const u8,
/// if the block should be destroyed or changed when a certain neighbor is removed.
dependsOnNeighbors: bool = false,
modelIndex: *const fn(block: Block) u16 = &DefaultFunctions.modelIndex,
};
//public interface RotationMode extends RegistryElement {
// /**
// * Called when generating the chunk mesh.
// * @param bi
// * @param vertices
// * @param faces
// * @return incremented renderIndex
// */
// void generateChunkMesh(BlockInstance bi, VertexAttribList vertices, IntSimpleList faces);
//
// /**
// * Update or place a block.
// * @param world
// * @param x
// * @param y
// * @param z
// * @param relativePlayerPosition Position of the player head relative to the (0, 0, 0) corner of the block.
// * @param playerDirection
// * @param relativeDir the direction in which the selected neighbor is.
// * @param currentData 0 if no block was there before.
// * @param blockPlacing true if the position of the block was previously empty/nonsolid.
// * @return true if the placing was successful, false otherwise.
// */
// boolean generateData(World world, int x, int y, int z, Vector3d relativePlayerPosition, Vector3f playerDirection, Vector3i relativeDir, IntWrapper currentData, boolean blockPlacing);
//
// /**
// * Updates data of a placed block if the RotationMode dependsOnNeighbors().
// * If the returned value is null, then the block will be removed instead of only updating the data.
// * @param oldBlock
// * @param removedDir given as neighbor index (See NormalChunk.)
// * @return new data
// */
// int updateData(int oldBlock, int removedDir, int newNeighbor);
//
// /**
// * A RotationMode may even alter the blocks transparency. Here is where it's done.
// * @param block The blocks data
// * @param neighbor the inverted(!) neighbor index(see Neighbors.java).
// */
// boolean checkTransparency(int block, int neighbor);
//
// /**
// * @return standard data for natural generation.
// */
// int getNaturalStandard(int block);
//
// /**
// * @param min minimal point of the surrounding block. May be overwritten.
// * @param max maximal point of the surrounding block. May be overwritten.
// */
// float getRayIntersection(RayAabIntersection intersection, int block, Vector3f min, Vector3f max, Vector3f transformedPosition);
//
// /**
// * Check if the entity would collide with the block.
// * @return Whether the entity and block hitboxes overlap.
// */
// boolean checkEntity(Vector3d pos, double width, double height, int x, int y, int z, int block);
//
// /**
// * Check if the entity would collide with the block, if its position was changed by `vel`.
// * If a collision occurs, adjust the velocity in way such that the entity does not move inside the block.
// * @param vel Velocity of the entity. The 4th element is reserved for stepping: a y-movement that is done exactly once.
// * @return Returns true if the block behaves like a normal block and therefor needs to be handled like a normal block in the specified direction. Returns false if everything has been handled already in here.
// */
// boolean checkEntityAndDoCollision(Entity ent, Vector4d vel, int x, int y, int z, int block);
//}
var rotationModes: std.StringHashMap(RotationMode) = undefined;
const RotationModes = struct {
const NoRotation = struct {
const id: []const u8 = "cubyz:no_rotation";
};
};
pub fn init() !void {
rotationModes = std.StringHashMap(RotationMode).init(main.globalAllocator);
inline for(@typeInfo(RotationModes).Struct.decls) |declaration| {
try register(@field(RotationModes, declaration.name));
}
}
pub fn deinit() void {
rotationModes.deinit();
}
pub fn getByID(id: []const u8) *RotationMode {
if(rotationModes.getPtr(id)) |mode| return mode;
std.log.warn("Could not find rotation mode {s}. Using cubyz:no_rotation instead.", .{id});
return rotationModes.getPtr("cubyz:no_rotation").?;
}
pub fn register(comptime Mode: type) !void {
var result: RotationMode = RotationMode{.id = Mode.id};
inline for(@typeInfo(RotationMode).Struct.fields) |field| {
if(@hasDecl(Mode, field.name)) {
if(field.field_type == @TypeOf(@field(Mode, field.name))) {
@field(result, field.name) = @field(Mode, field.name);
} else {
@field(result, field.name) = &@field(Mode, field.name);
}
}
}
try rotationModes.putNoClobber(result.id, result);
}