Allow branches to have a shell model that's always rendered and add leafy branches for all wood types.

This commit is contained in:
IntegratedQuantum 2025-03-23 20:21:37 +01:00
parent 8f0693f7b0
commit 42268c4b22
8 changed files with 146 additions and 23 deletions

View File

@ -0,0 +1,12 @@
.{
.tags = .{.leaf},
.blockHealth = 0.5,
.absorbedLight = 0x202830,
.alwaysViewThrough = true,
.rotation = .branch,
.model = .{
.radius = 4,
.shellModel = "cubyz:cube",
.textureSlotOffset = 6,
},
}

View File

@ -0,0 +1,14 @@
.{
.drops = .{
.{.items = .{"cubyz:baobab_leaves", "cubyz:baobab_branch"}},
},
.texture = "cubyz:baobab_leaves",
.texture6 = "cubyz:branch/baobab/dot",
.texture7 = "cubyz:branch/baobab/half_line",
.texture8 = "cubyz:branch/baobab/line",
.texture9 = "cubyz:branch/baobab/bend",
.texture10 = "cubyz:branch/baobab/intersection",
.texture11 = "cubyz:branch/baobab/cross",
.lodReplacement = "cubyz:baobab_leaves_opaque",
.opaqueVariant = "cubyz:baobab_leaves_opaque",
}

View File

@ -0,0 +1,14 @@
.{
.drops = .{
.{.items = .{"cubyz:birch_leaves", "cubyz:birch_branch"}},
},
.texture = "cubyz:birch_leaves",
.texture6 = "cubyz:branch/birch/dot",
.texture7 = "cubyz:branch/birch/half_line",
.texture8 = "cubyz:branch/birch/line",
.texture9 = "cubyz:branch/birch/bend",
.texture10 = "cubyz:branch/birch/intersection",
.texture11 = "cubyz:branch/birch/cross",
.lodReplacement = "cubyz:birch_leaves_opaque",
.opaqueVariant = "cubyz:birch_leaves_opaque",
}

View File

@ -0,0 +1,14 @@
.{
.drops = .{
.{.items = .{"cubyz:mahogany_leaves", "cubyz:mahogany_branch"}},
},
.texture = "cubyz:mahogany_leaves",
.texture6 = "cubyz:branch/mahogany/dot",
.texture7 = "cubyz:branch/mahogany/half_line",
.texture8 = "cubyz:branch/mahogany/line",
.texture9 = "cubyz:branch/mahogany/bend",
.texture10 = "cubyz:branch/mahogany/intersection",
.texture11 = "cubyz:branch/mahogany/cross",
.lodReplacement = "cubyz:mahogany_leaves_opaque",
.opaqueVariant = "cubyz:mahogany_leaves_opaque",
}

View File

@ -0,0 +1,14 @@
.{
.drops = .{
.{.items = .{"cubyz:oak_leaves", "cubyz:oak_branch"}},
},
.texture = "cubyz:oak_leaves",
.texture6 = "cubyz:branch/oak/dot",
.texture7 = "cubyz:branch/oak/half_line",
.texture8 = "cubyz:branch/oak/line",
.texture9 = "cubyz:branch/oak/bend",
.texture10 = "cubyz:branch/oak/intersection",
.texture11 = "cubyz:branch/oak/cross",
.lodReplacement = "cubyz:oak_leaves_opaque",
.opaqueVariant = "cubyz:oak_leaves_opaque",
}

View File

@ -0,0 +1,14 @@
.{
.drops = .{
.{.items = .{"cubyz:pine_needles", "cubyz:pine_branch"}},
},
.texture = "cubyz:pine_needles",
.texture6 = "cubyz:branch/pine/dot",
.texture7 = "cubyz:branch/pine/half_line",
.texture8 = "cubyz:branch/pine/line",
.texture9 = "cubyz:branch/pine/bend",
.texture10 = "cubyz:branch/pine/intersection",
.texture11 = "cubyz:branch/pine/cross",
.lodReplacement = "cubyz:pine_needles_opaque",
.opaqueVariant = "cubyz:pine_needles_opaque",
}

View File

@ -0,0 +1,14 @@
.{
.drops = .{
.{.items = .{"cubyz:willow_leaves", "cubyz:willow_branch"}},
},
.texture = "cubyz:willow_leaves",
.texture6 = "cubyz:branch/willow/dot",
.texture7 = "cubyz:branch/willow/half_line",
.texture8 = "cubyz:branch/willow/line",
.texture9 = "cubyz:branch/willow/bend",
.texture10 = "cubyz:branch/willow/intersection",
.texture11 = "cubyz:branch/willow/cross",
.lodReplacement = "cubyz:willow_leaves_opaque",
.opaqueVariant = "cubyz:willow_leaves_opaque",
}

View File

@ -403,7 +403,23 @@ 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, ModelIndex) = undefined;
var branchModels: std.HashMap(HashMapKey, ModelIndex, HashMapKey, std.hash_map.default_max_load_percentage) = undefined;
const HashMapKey = struct {
radius: u16,
shellModelId: []const u8,
textureSlotOffset: u32,
pub fn hash(_: HashMapKey, val: HashMapKey) u64 {
var hasher = std.hash.Wyhash.init(0);
std.hash.autoHashStrat(&hasher, val, .DeepRecursive);
return hasher.final();
}
pub fn eql(_: HashMapKey, val1: HashMapKey, val2: HashMapKey) bool {
if(val1.radius != val2.radius) return false;
if(val1.textureSlotOffset != val2.textureSlotOffset) return false;
return std.mem.eql(u8, val1.shellModelId, val2.shellModelId);
}
};
const BranchData = packed struct(u6) {
enabledConnections: u6,
@ -425,7 +441,7 @@ pub const RotationModes = struct {
};
fn init() void {
branchModels = .init(main.globalAllocator.allocator);
branchModels = .initContext(main.globalAllocator.allocator, undefined);
}
fn deinit() void {
@ -460,7 +476,7 @@ pub const RotationModes = struct {
cross: void,
};
fn rotateQuad(originalCorners: [4]Vec2f, pattern: Pattern, min: f32, max: f32, side: Neighbor) main.models.QuadInfo {
fn rotateQuad(originalCorners: [4]Vec2f, pattern: Pattern, min: f32, max: f32, side: Neighbor, textureSlotOffset: u32) main.models.QuadInfo {
var corners: [4]Vec2f = originalCorners;
switch(pattern) {
@ -498,13 +514,13 @@ pub const RotationModes = struct {
},
.cornerUV = originalCorners,
.normal = @floatFromInt(side.relPos()),
.textureSlot = @intFromEnum(pattern),
.textureSlot = textureSlotOffset + @intFromEnum(pattern),
};
return res;
}
fn addQuads(pattern: Pattern, side: Neighbor, radius: f32, out: *main.List(main.models.QuadInfo)) void {
fn addQuads(pattern: Pattern, side: Neighbor, radius: f32, out: *main.List(main.models.QuadInfo), textureSlotOffset: u32) void {
const min: f32 = (8.0 - radius)/16.0;
const max: f32 = (8.0 + radius)/16.0;
switch(pattern) {
@ -514,7 +530,7 @@ pub const RotationModes = struct {
.{min, max},
.{max, min},
.{max, max},
}, pattern, min, max, side));
}, pattern, min, max, side, textureSlotOffset));
},
.halfLine => {
out.append(rotateQuad(.{
@ -522,7 +538,7 @@ pub const RotationModes = struct {
.{min, max},
.{max, 0.0},
.{max, max},
}, pattern, min, max, side));
}, pattern, min, max, side, textureSlotOffset));
},
.line => {
out.append(rotateQuad(.{
@ -530,7 +546,7 @@ pub const RotationModes = struct {
.{min, 1.0},
.{max, 0.0},
.{max, 1.0},
}, pattern, min, max, side));
}, pattern, min, max, side, textureSlotOffset));
},
.bend => {
out.append(rotateQuad(.{
@ -538,7 +554,7 @@ pub const RotationModes = struct {
.{0.0, max},
.{max, 0.0},
.{max, max},
}, pattern, min, max, side));
}, pattern, min, max, side, textureSlotOffset));
},
.intersection => {
out.append(rotateQuad(.{
@ -546,7 +562,7 @@ pub const RotationModes = struct {
.{0.0, max},
.{1.0, 0.0},
.{1.0, max},
}, pattern, min, max, side));
}, pattern, min, max, side, textureSlotOffset));
},
.cross => {
out.append(rotateQuad(.{
@ -554,7 +570,7 @@ pub const RotationModes = struct {
.{0.0, 1.0},
.{1.0, 0.0},
.{1.0, 1.0},
}, pattern, min, max, side));
}, pattern, min, max, side, textureSlotOffset));
},
}
}
@ -638,20 +654,33 @@ pub const RotationModes = struct {
};
}
pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex {
const radius = zon.get(f32, "radius", 4);
if(branchModels.get(@bitCast(radius))) |modelIndex| return modelIndex;
pub fn createBlockModel(_: Block, modeData: *u16, zon: ZonElement) ModelIndex {
var radius = zon.get(f32, "radius", 4);
const radiusForComparisons = std.math.lossyCast(u16, @round(radius*65536.0/16.0));
radius = @as(f32, @floatFromInt(radiusForComparisons))*16.0/65536.0;
modeData.* = radiusForComparisons;
const shellModelId = zon.get([]const u8, "shellModel", "");
const textureSlotOffset = zon.get(u32, "textureSlotOffset", 0);
if(branchModels.get(.{.radius = radiusForComparisons, .shellModelId = shellModelId, .textureSlotOffset = textureSlotOffset})) |modelIndex| return modelIndex;
var shellQuads = main.List(main.models.QuadInfo).init(main.stackAllocator);
defer shellQuads.deinit();
if(shellModelId.len != 0) {
const shellModel = main.models.getModelIndex(shellModelId).model();
shellModel.getRawFaces(&shellQuads);
}
var modelIndex: ModelIndex = undefined;
for(0..64) |i| {
var quads = main.List(main.models.QuadInfo).init(main.stackAllocator);
defer quads.deinit();
quads.appendSlice(shellQuads.items);
for(Neighbor.iterable) |neighbor| {
const pattern = getPattern(BranchData.init(@intCast(i)), neighbor);
if(pattern) |pat| {
addQuads(pat, neighbor, radius, &quads);
addQuads(pat, neighbor, radius, &quads, textureSlotOffset);
}
}
@ -661,7 +690,7 @@ pub const RotationModes = struct {
}
}
branchModels.put(@bitCast(radius), modelIndex) catch unreachable;
branchModels.put(.{.radius = radiusForComparisons, .shellModelId = shellModelId, .textureSlotOffset = textureSlotOffset}, modelIndex) catch unreachable;
return modelIndex;
}
@ -707,16 +736,15 @@ pub const RotationModes = struct {
neighborBlock: Block,
blockPlacing: bool,
) bool {
const blockBaseModelIndex = blocks.meshes.modelIndexStart(currentBlock.*);
const neighborBaseModelIndex = blocks.meshes.modelIndexStart(neighborBlock);
const canConnectToNeighbor = currentBlock.mode() == neighborBlock.mode() and currentBlock.modeData() == neighborBlock.modeData();
if(blockPlacing or blockBaseModelIndex == neighborBaseModelIndex or neighborBlock.solid()) {
if(blockPlacing or canConnectToNeighbor 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 (blockBaseModelIndex == neighborBaseModelIndex or neighborModel.isNeighborOccluded[neighbor.?.reverse().toInt()]));
const targetVal = ((neighborBlock.solid() and (!neighborBlock.viewThrough() or canConnectToNeighbor)) and (canConnectToNeighbor or neighborModel.isNeighborOccluded[neighbor.?.reverse().toInt()]));
currentData.setConnection(neighbor.?, targetVal);
const result: u16 = currentData.enabledConnections;
@ -729,14 +757,13 @@ pub const RotationModes = struct {
}
pub fn updateData(block: *Block, neighbor: Neighbor, neighborBlock: Block) bool {
const blockBaseModel = blocks.meshes.modelIndexStart(block.*);
const neighborBaseModel = blocks.meshes.modelIndexStart(neighborBlock);
const canConnectToNeighbor = block.mode() == neighborBlock.mode() and block.modeData() == neighborBlock.modeData();
var currentData = BranchData.init(block.data);
// Handle joining with other branches. While placed, branches extend in a
// opposite direction than they were placed from, effectively connecting
// to the block they were placed at.
if(blockBaseModel == neighborBaseModel) {
if(canConnectToNeighbor) {
const neighborData = BranchData.init(neighborBlock.data);
currentData.setConnection(neighbor, neighborData.isConnected(neighbor.reverse()));
} else if(!neighborBlock.solid()) {