diff --git a/assets/cubyz/blocks/cloth/_defaults.zig.zon b/assets/cubyz/blocks/cloth/_defaults.zig.zon index 436ea7c316..5bb0dea0b5 100644 --- a/assets/cubyz/blocks/cloth/_defaults.zig.zon +++ b/assets/cubyz/blocks/cloth/_defaults.zig.zon @@ -7,5 +7,6 @@ .collide = false, .model = "cubyz:carpet", .rotation = "cubyz:carpet", + .placementMode = .gridNeighbor, .degradable = true, } diff --git a/assets/cubyz/blocks/daisies.zig.zon b/assets/cubyz/blocks/daisies.zig.zon index 3645996d61..a726760c5c 100644 --- a/assets/cubyz/blocks/daisies.zig.zon +++ b/assets/cubyz/blocks/daisies.zig.zon @@ -12,6 +12,7 @@ .absorbedLight = 0x121012, .model = "cubyz:plane", .rotation = "cubyz:carpet", + .placementMode = .gridNeighbor, .onUpdate = .{ .type = .check_support_blocks, }, diff --git a/assets/cubyz/blocks/dandelions.zig.zon b/assets/cubyz/blocks/dandelions.zig.zon index 8fd742b0cc..346dc6b228 100644 --- a/assets/cubyz/blocks/dandelions.zig.zon +++ b/assets/cubyz/blocks/dandelions.zig.zon @@ -12,6 +12,7 @@ .absorbedLight = 0x121012, .model = "cubyz:plane", .rotation = "cubyz:carpet", + .placementMode = .gridNeighbor, .onUpdate = .{ .type = .check_support_blocks, }, diff --git a/assets/cubyz/blocks/ivy.zig.zon b/assets/cubyz/blocks/ivy.zig.zon index d7084a756a..96cadbc490 100644 --- a/assets/cubyz/blocks/ivy.zig.zon +++ b/assets/cubyz/blocks/ivy.zig.zon @@ -12,6 +12,7 @@ .absorbedLight = 0x121012, .model = "cubyz:plane", .rotation = "cubyz:carpet", + .placementMode = .gridNeighbor, .texture = "cubyz:ivy", .item = .{ .texture = "ivy.png", diff --git a/assets/cubyz/blocks/osier.zig.zon b/assets/cubyz/blocks/osier.zig.zon index 61cf66ccb5..9ec8969ec4 100644 --- a/assets/cubyz/blocks/osier.zig.zon +++ b/assets/cubyz/blocks/osier.zig.zon @@ -12,6 +12,7 @@ .absorbedLight = 0x121012, .model = "cubyz:plane", .rotation = "cubyz:carpet", + .placementMode = .gridNeighbor, .onUpdate = .{ .type = .check_support_blocks, }, diff --git a/src/blocks.zig b/src/blocks.zig index 571699df88..3deb4032fd 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -53,6 +53,11 @@ pub const Ore = struct { seed: u64, }; +const PlacementMode = enum { + boundingBox, + gridNeighbor, +}; + const SelectionCapabilities = union(enum) { always: void, custom: packed struct(u1) { @@ -117,6 +122,7 @@ var _degradable: [maxBlockCount]bool = undefined; var _viewThrough: [maxBlockCount]bool = undefined; var _alwaysViewThrough: [maxBlockCount]bool = undefined; var _hasBackFace: [maxBlockCount]bool = undefined; +var _placementMode: [maxBlockCount]PlacementMode = undefined; var _tags: [maxBlockCount][]Tag = undefined; var _light: [maxBlockCount]u32 = undefined; /// How much light this block absorbs if it is transparent @@ -185,6 +191,7 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 { _alwaysViewThrough[size] = zon.get(bool, "alwaysViewThrough") orelse false; _viewThrough[size] = (zon.get(bool, "viewThrough") orelse false) or _transparent[size] or _alwaysViewThrough[size]; _hasBackFace[size] = zon.get(bool, "hasBackFace") orelse false; + _placementMode[size] = zon.get(PlacementMode, "placementMode") orelse .boundingBox; _friction[size] = zon.get(f32, "friction") orelse 20; _bounciness[size] = zon.get(f32, "bounciness") orelse 0.0; _density[size] = zon.get(f32, "density") orelse main.physics.airDensity; @@ -478,6 +485,10 @@ pub const Block = packed struct(u32) { // MARK: Block return _hasBackFace[self.typ]; } + pub inline fn placementMode(self: Block) PlacementMode { + return _placementMode[self.typ]; + } + pub inline fn tags(self: Block) []const Tag { return _tags[self.typ]; } diff --git a/src/renderer.zig b/src/renderer.zig index c572640f7e..02a9ad8db2 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -993,6 +993,25 @@ pub const MeshSelection = struct { // MARK: MeshSelection // TODO: Test entities } + fn boundingBoxFaceNormal(relativePos: Vec3f, dir: Vec3f, min: Vec3f, max: Vec3f) ?Vec3i { + var tEnter: f32 = -std.math.inf(f32); + var result: ?Vec3i = null; + inline for (0..3) |axis| { + if (dir[axis] != 0) { + const t1 = (min[axis] - relativePos[axis])/dir[axis]; + const t2 = (max[axis] - relativePos[axis])/dir[axis]; + const t = @min(t1, t2); + if (t > tEnter) { + tEnter = t; + var normal: Vec3i = .{0, 0, 0}; + normal[axis] = if (dir[axis] > 0) -1 else 1; + result = normal; + } + } + } + return if (tEnter >= 0) result else null; + } + fn canPlaceBlock(pos: Vec3i, block: main.blocks.Block) bool { if (main.physics.collision.collideWithBlock(block, pos[0], pos[1], pos[2], main.game.Player.getPosBlocking() + main.game.Player.outerBoundingBox.center(), main.game.Player.outerBoundingBox.extent(), .{0, 0, 0}) != null) { return false; @@ -1025,14 +1044,17 @@ pub const MeshSelection = struct { // MARK: MeshSelection } } // Check the block in front of it: - const neighborPos = posBeforeBlock; - neighborDir = selectedPos - posBeforeBlock; + const selectedRelPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selectedPos))); + const boxFaceNormal: ?Vec3i = if (oldBlock.placementMode() == .boundingBox) boundingBoxFaceNormal(selectedRelPos, lastDir, selectionMin, selectionMax) else null; + const neighborPos = if (boxFaceNormal) |normal| selectedPos + normal else posBeforeBlock; + neighborDir = if (boxFaceNormal) |normal| -normal else selectedPos - posBeforeBlock; + const neighborOfSelectionValue = if (boxFaceNormal != null) chunk.Neighbor.fromRelPos(neighborDir).? else neighborOfSelection; const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); const neighborBlock = block; oldBlock = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; block = oldBlock; if (block.typ == itemBlock) { - if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, false)) { + if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelectionValue, &block, neighborBlock, false)) { if (!canPlaceBlock(neighborPos, block)) return; updateBlockAndSendUpdate(inventory, slot, neighborPos, oldBlock, block); return; @@ -1041,7 +1063,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection if (!block.replaceable()) return; block.typ = itemBlock; block.data = 0; - if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, true)) { + if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelectionValue, &block, neighborBlock, true)) { if (!canPlaceBlock(neighborPos, block)) return; updateBlockAndSendUpdate(inventory, slot, neighborPos, oldBlock, block); return; diff --git a/src/rotation.zig b/src/rotation.zig index 8b68e539f6..44a062aa3a 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -84,7 +84,7 @@ pub const RotationMode = struct { // MARK: RotationMode } if (minimum != null) { // Invert the normal if the player is behind the face (eg. cross model) - if (vec.dot(normal.?, relativePlayerPos) < 0.0) { + if (vec.dot(normal.?, playerDir) > 0.0) { normal = -normal.?; } return .{