Skip to content

Reimplement negative shape cullface 重新实现负形剔除 - #4333

Open
QiuShui1012 wants to merge 4 commits into
Anvil-Dev:dev/26.1/1.6from
QiuShui1012:render/26.1/1.6
Open

Reimplement negative shape cullface 重新实现负形剔除#4333
QiuShui1012 wants to merge 4 commits into
Anvil-Dev:dev/26.1/1.6from
QiuShui1012:render/26.1/1.6

Conversation

@QiuShui1012

Copy link
Copy Markdown
Collaborator
  • 修改了约束仓角落的超限纹理,使其与其它超限方块同步
  • 同步了中子辐照器的美术资源

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /opt/data/workspace/AnvilCraft && python3 -c "
import json,glob
for f in glob.glob('src/main/resources/assets/anvilcraft/models/block/*.json'):
    try:
        json.load(open(f))
    except Except...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /tmp && python3 -c "
import re
diff = open('/tmp/pr4333.diff').read()
files = re.split(r'(?m)^diff --git ', diff)[1:]
bad = 0
for f in files:
    header = f.split('\n',1)[0]
    if 'models/block/' ...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #4333

操作: synchronize
范围: 33 个文件(9 Java, 9 模型 JSON, 11 PNG, 4 Gradle/mixins 配置)/ 1896 行 diff(11 新增,0 删除)

核心变更: 重写负形剔除机制 —— 弃用旧的「模型 cullface 烘焙期剔除 + 自定义 OCCLUSION_CACHE 遮挡逻辑」,改为运行时方案:模型烘焙时收集边界矩形(0.025/15.975 平面上的 quad)→ 包装 DelegateBlockStateModel → 通过 skipRendering 拦截做全覆盖剔除 + quad 级切片做部分覆盖剔除。


🔴 关键(需确认后合并)

  • client/event/NegativeShapeModelEventListener.java — 整个机制依赖 part.getQuads(null) 能取到边界 quadcollectEdgeRectanglesNegativeShapePart.edgeQuads() 都只从 getQuads(null) 收集/切片。若 26.1 的 getQuads(null) 只返回无方向 quad(普通 JSON face 烘焙后都带 Direction),则 EDGE_RECTANGLES 恒为空 → shouldSkipFace 恒 false → 剔除静默失效。请确认 flipped element(from > to 的 outline 盒)烘焙出的 quad 方向确实是 null/unculled。另需确认 getQuads(side) 不会同时返回未切片的原 edge quad(否则与切片副本重复渲染)。建议在无 Sodium 环境实测两个相邻负形方块的拼接处。

  • mixin/BlockStateBaseMixin.java — 对同族方块对无条件短路 skipRenderingshouldSkipFace 返回 false(无边界矩形/部分覆盖)时仍强制 cir.setReturnValue(false)绕过了原版基于遮挡形状的剔除。例如 InfiniteCollectorBlock 的 outline 顶面在 Y=3.975(非边界平面,无边界矩形)——堆叠两个 InfiniteCollector 时,内部水平面不再被剔除(旧 BlockMixin 的 shape 联合逻辑会剔除)。建议:仅当 shouldSkipFace() 为 true 时短路,false 时回退原版逻辑(不 setReturnValue)。若强制 false 是有意为之(避免原版全量剔除挡住切片),请在代码中注明原因。

⚠️ 警告

  • mixin/compat/SodiumBlockOcclusionCacheMixin.java — 非同族邻居强制 setReturnValue(true),与原版路径不一致。原版路径(BlockMixin!skipRendering)对不透明邻居可能剔除,Sodium 路径却无条件渲染该面 → 产生被深度隐藏的浪费 quad(overdraw)。另外 Sodium 0.9.1 的 AbstractBlockRenderContext 类名/shouldDrawSide 签名请与 sodium-neoforge-api 核对:类名错则 hasSodium=false 静默跳过 mixin(兼容失效),方法/字段签名错则带 Sodium 启动即崩溃。
  • NegativeShapeModelEventListener.javaEDGE_RECTANGLES 静态 HashMap 无同步。模型烘焙(主线程)写入,区块重建线程/Sodium 网格线程读取;烘焙完成后无并发写,实践中安全,但 F3+T 资源重载时 clear()+重填与渲染线程存在理论竞态。建议改为在重载屏障后再发布(或至少注释说明线程假设)。
  • sliceQuad 回退逻辑rebuildQuad 返回 null(顶点不在矩形四角/非轴对齐 quad)时保留完整原 quad,部分覆盖场景下 rim 会完整渲染(轻微视觉瑕疵)。当前模型均为轴对齐 quad,触发概率低,但值得知晓。
  • BlockMixin.javashouldRenderFace 语义切换。对非同族邻居从旧的 shape 遮挡实现(含空 shape 短路)切换为 !skipRendering;若 26.1 中 shouldRenderFaceskipRendering 的空 shape 语义不一致,空心方块贴空气时可能误剔除。render 分支若已实测应无问题,建议确认。

💡 建议

  • anvilcraft.mixins.json 全列表 4→6 空格重排,33 项纯缩进噪音混在功能变更里,可拆成独立格式化提交。
  • Sodium 0.8.10→0.9.1 + 仓库从 Modrinth 切到 maven.caffeinemc.net:确认 CI/构建环境能访问新仓库(net.caffeinemc:sodium-neoforge-api/mod 坐标正确性依赖该源)。
  • 模型 JSON 的 10 处缺失尾换行全部修复(均为旧侧标记),不错。

🟢 看起来不错

  • 模型烘焙包装幂等:instanceof NegativeShapeBlockStateModel 检查 + 用 unwrapped originalModel() 收集矩形,重载不会双重包装。
  • OCCLUSION_CACHE ThreadLocal shadow 与自定义缓存逻辑干净移除,BlockMixin 简化为 HEAD 注入 !skipRendering
  • FaceRectangle 减法/切片/UV 双线性插值实现严谨;shouldSkipFace(全覆盖剔除)与 collectCoverage(部分切片)矩形来源一致(相邻方块对侧边界矩形),两条路径语义自洽。
  • 两个新 mixin 都注册在 client 列表(BlockStateBaseMixincompat.SodiumBlockOcclusionCacheMixin),无专用服务器加载风险;Sodium mixin 受插件 hasSodium 门控,逻辑正确。
  • 纹理引用全部可解析(transcendium_block_outlinenegative_matter_block_outline 已存在,8 个新 anvilon PNG 随 PR 提供);anvilon_irradiator_*#2/#3 覆盖与父模型变量一一对应。

📋 声称验证表

声称 状态 对应文件
约束仓角落超限纹理与其它超限方块同步 confined_neutronium_ingot.jsonconfined_time_anvilon.jsonconfinement_chamber.json 等角落元素 #0#1(transcendium_block_outline)
中子辐照器美术资源同步 neutron_irradiator.json#4→negative_matter_block_outline)、anvilon_irradiator_* 4 模型 + 8 个新 PNG
重新实现负形剔除 NegativeShapeModelEventListener.java + BlockMixin 重写 + BlockStateBaseMixin + Sodium mixin + negative_matter_block.json 移除 6 处 cullface

🧪 测试建议(手动渲染验证)

场景 验证点 优先级
两个同型负形方块相邻(如负物质块×2) 共享面 rim 完整消失、无 z-fighting 🔴
同族异型相邻(如约束仓×约束铁砧,布局不同) 部分覆盖处 quad 切片正确、UV 无拉伸 🔴
堆叠两个 InfiniteCollector 内部水平面是否被误渲染(BlockStateBaseMixin 短路问题) 🟡
负形方块贴不透明方块,Sodium 开/关对比 两路径渲染一致性、无多余 overdraw 🟡
F3+T 资源重载后 剔除是否仍生效(EDGE_RECTANGLES 重建) 🟡
Iris+Sodium 组合启动 mixin 应用无崩溃 🟡

结论: COMMENT — 架构设计清晰、声称全部落实,未发现可证实的关键缺陷;但「getQuads(null) 语义依赖」「skipRendering 无条件短路绕过原版剔除」「Sodium 与原版路径不一致」三点建议作者确认/补充说明后合入。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/baked-model-wrapper-quad-filter-review.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

curl -sL "https://raw.githubusercontent.com/Anvil-Dev/AnvilCraft/render/26.1/1.6/src/main/resources/assets/anvilcraft/models/block/negative_matter_block.json" -o /tmp/negative_matter_block_new.json; w...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /opt/data/workspace/AnvilCraft && python3 -c "
import json
d = json.load(open('src/main/resources/assets/anvilcraft/models/block/negative_matter_block.json'))
for e in d['elements']:
    print('fro...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /opt/data/workspace/AnvilCraft && python3 -c "
import json
d = json.load(open('src/main/resources/anvilcraft.mixins.json'))
print('client:', [m for m in d['client'] if 'odium' in m or 'BlockState' ...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /tmp && grep "^diff --git.*\.json" /tmp/pr4333.diff | sed 's|.* b/||' | while read f; do base=$(basename $f); case "$f" in *models/block*) src="/opt/data/workspace/AnvilCraft/src/main/resources/ass...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

所有关键验证完成。汇总分析结果,输出审查意见:

代码审查摘要 — PR #4333

操作: synchronize(分支同步:render/26.1/1.6 → dev/26.1/1.6)
范围: 33 个文件(9 Java、10 模型 JSON、11 PNG 纹理、3 构建文件)/ 1912 行 diff

变更机制解读

负形剔除从旧的「VoxelShape 布尔运算 + OCCLUSION_CACHE ThreadLocal」方案重构为基于模型的边缘矩形系统

  1. 烘焙时NegativeShapeModelEventListener.onModelBake):对每个 INegativeShapeBlock 状态收集位于模型边界(0.025/15.975 平面)的四边形为 FaceRectangle,存入静态 EDGE_RECTANGLES,并把模型包装为 NegativeShapeBlockStateModel
  2. 渲染时collectParts(level, pos, ...) 查询 6 方向邻居,将同族邻居的覆盖矩形传入 NegativeShapePart,对边缘四边形做矩形差集切片subtract 4 条带切分 + UV 双线性插值重建)
  3. 剔除判定BlockStateBaseMixin HEAD 注入 skipRenderingshouldSkipFace(完全覆盖才剔除);BlockMixin HEAD 注入 shouldRenderFace!skipRendering;Sodium 侧 SodiumBlockOcclusionCacheMixin 注入 shouldDrawSide
  4. 新增 3 个方块接入:ConfinementChamberBlock / SimpleConfinementAnvilonBlock / InfiniteCollectorBlockITranscendiumBlockNeutronIrradiatorBlockINegativeMatterBlock

已交叉验证:所有接入方块的模型确实使用 0.025/15.975 内缩约定(含 infinite_collector.jsonneutron_irradiator.json),与检测常量 MIN_MODEL_EDGE=0.025、容差 0.0001 完全匹配,不会与相邻不透明方块产生共面 z-fighting。

🔴 关键

未发现必须阻塞合并的问题。

⚠️ 警告

  • NegativeShapePart.getQuads(null) 无条件剥离边缘四边形(NegativeShapeModelEventListener.java:302-316)— side == null 路径把模型边界四边形全部丢弃。区块渲染走逐面查询不受影响,但只查询 null 的消费方(物品渲染、破坏粒子)会丢失全部外缘面,方块物品可能显示为残缺外壳。已确认 mod 自身的渲染代码(FeatureRendererSupportBaseFluidTankItemRenderer)都走 collectParts,但 MC 26.1 默认 BlockItem 的物品模型路径无法在本环境核实。建议合入前在游戏内检查这些方块的物品栏/手持模型;若缺失,可将剥离条件改为 coverage 非空时才剥离(EMPTY_COVERAGE 时保留完整边缘面)。
  • EDGE_RECTANGLES 静态 HashMap 线程安全(:131, :139, :199)— 模型烘焙(客户端主线程)时 clear()+put(),区块重建工作线程并发读。资源重载期间理论上存在并发读写风险。建议改为 volatile + 不可变快照整体替换(如 Map<BlockState, ...> 重建后原子交换引用),成本极低。
  • getQuads(side) 双重取列表(:272-278)— 每个面查询都同时调用 originalPart.getQuads(side)originalPart.getQuads(null) 再过滤,相当于每次渲染每个 part 生成两遍四边形列表。辐照器模型有 150+ element,逐面 × 逐 part 的开销在区块重建时可感知。可考虑烘焙时按方向预缓存边缘四边形。

💡 建议

  • "scope": 0 字段(neutron_irradiator.json 的 4 个 group)— BlockBench 动画导出残留。MC 模型加载器会忽略未知键、无功能影响,但建议确认不是误带动画数据,后续手工编辑易混淆。
  • NegativeShapePart 回退行为(:349)— rebuildQuad 返回 null 时回退到完整原始四边形(宁可过度绘制也不丢面),方向正确,可留注释说明这是有意为之。
  • 依赖变更确认:Sodium 0.8.10→0.9.1(Modrinth 全量 jar → CaffeineMC 官方 maven 的 api/mod 分离)与新 mixin 目标类 AbstractBlockRenderContext 匹配,hasSodium 插件门控与 mixin 类名一致 ✅;Ageratum 移除冗余 implementation、启用 jarJar 为有意行为变更;JEI forge→neoforge 为纯重命名。

🟢 看起来不错

  • 矩形差集切分(4 条带 + 迭代求余)几何上完全正确;shouldSkipFace(全覆盖剔除)与模型级切片(部分覆盖)两层职责清晰、语义一致
  • negative_matter_block.json 移除旧的错位 cullface 反向剔除 hack,正确迁移到新系统
  • 纹理同步验证通过:confined_energy/mass/space_anvilon.json 均 parent 到 confined_time_anvilon.json,本 PR 对后者加的 #1(transcendium_block_outline)和 light_emission: 15自动继承,4 个约束仓角落方块纹理同步完整,与声称一致
  • 所有引用的纹理(transcendium_block_outlinenegative_matter_block_outline)在目标分支存在,11 张新 PNG 对应 4 种辐照器 side/top + 辐照器本体三面,blockstate 变体映射正确
  • AnvilCraftMixinPlugin 字段统一改为静态限定访问,hasSodium 检测类与 mixin 目标类一致;BlockMixin 旧的 @Shadow OCCLUSION_CACHE@Local 捕获移除,注入点从 INVOKE 改为 HEAD,显著简化
  • 10 个 JSON 文件补齐了缺失的 EOF 换行

📋 声称验证表

声称 状态 对应文件
重新实现负形剔除 NegativeShapeModelEventListener(新)、BlockMixin 重写、BlockStateBaseMixin(新)、SodiumBlockOcclusionCacheMixin(新)
约束仓角落超限纹理与其它超限方块同步 confinement_chamber.json、confined_time_anvilon.json(子模型继承)、3 方块接入 ITranscendiumBlock
中子辐照器美术资源同步 anvilon_irradiator_{energy,mass,space,time}.json + 11 张新 PNG、neutron_irradiator.json 纹理 #4 指向 negative_matter_block_outline

结论: COMMENT — 实现方案清晰、几何逻辑正确、纹理与注册链验证全部通过,未发现阻塞性问题。建议合入前重点验证 ⚠️ 第 1 条(方块物品渲染是否缺失外缘面),并顺手处理 EDGE_RECTANGLES 的并发安全(低风险)。

🧪 测试建议

被测目标 推荐场景 优先级
方块物品模型(confinement_chamber / negative_matter_block / neutron_irradiator) 物品栏/手持/掉落物形态检查外缘面是否完整 🔴
相邻同族方块剔除 2×2×2 堆叠约束仓、中子辐照器并排,检查共享面剔除与部分覆盖切片 🔴
与不透明方块相邻 负形方块紧贴普通方块,确认无 z-fighting/闪烁 🟡
Sodium 0.9.1 环境 安装 Sodium 后重载资源包 + 断点重建,确认 shouldDrawSide 注入生效、无 mixin 崩溃 🟡
资源重载 F3+T 连续重载数次,确认无并发异常(EDGE_RECTANGLES) 🟢

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/baked-model-wrapper-quad-filter-review.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants