烘焙模型

烘焙模型

BakedModel 是带纹理形状在代码中的表示。它可以有多种来源,例如默认模型加载器调用 UnbakedModel#bake,或自定义模型加载器调用 IUnbakedGeometry#bake。某些方块实体渲染器也会使用烘焙模型。模型的复杂度没有限制。

模型保存在 ModelManager 中,可通过 Minecraft.getInstance().modelManager 访问,再调用 ModelManager#getModel,使用 ResourceLocation 或 ModelResourceLocation 获取特定模型。Mod 基本上都会复用之前自动加载和烘焙的模型。

BakedModel 的方法

getQuads

烘焙模型最重要的方法是 getQuads,它返回可发送给 GPU 的 BakedQuad 列表。四边形类似于建模软件(以及大多数其他游戏)中的三角形,但 Minecraft 以方形为主,因此开发者选择四边形(4 个顶点)而非三角形(3 个顶点)进行渲染。该方法有五个参数:

  • BlockState:正在渲染的方块状态。可为 null,表示正在渲染物品。
  • Direction:进行遮挡剔除时所针对的面方向。可为 null,此时应返回无法被遮挡的四边形。
  • RandomSource:可用于随机化的客户端随机源。
  • ModelData:额外模型数据,可能包含渲染所需的方块实体数据,由 BakedModel#getModelData 提供。
  • RenderType:渲染方块所用的渲染类型。可为 null,表示返回该模型所有渲染类型的四边形;否则为 BakedModel#getRenderTypes(见下文)返回的类型之一。

模型应充分缓存。虽然区块只在方块改变时重建,但该方法仍应尽量快速,并尽可能缓存计算结果,因为它在每个区块段内会被调用很多次(每种渲染类型最多 7 次 × 模型所用渲染类型数量 × 每个区块段 4096 个方块)。此外,方块实体渲染器或实体渲染器还可能在每帧调用多次。

applyTransform 与 getTransforms

applyTransform 允许在对模型应用视角变换时执行自定义逻辑,甚至返回完全不同的模型。NeoForge 添加它以替代原版 getTransforms():后者只能自定义变换内容,不能改变应用方式。不过,applyTransform 默认仍调用 getTransforms,如果只需自定义变换,重写后者即可。applyTransform 有三个参数:

  • ItemDisplayContext:模型要变换到的显示视角。
  • PoseStack:渲染所用姿态栈。
  • boolean:是否使用左手渲染的修正值,而非默认右手值。渲染左手时为 true(副手,或设置中启用左手模式后的主手)。

提示 applyTransform 与 getTransforms 仅适用于物品模型。

其他方法

BakedModel 中还可以重写或查询以下方法:

签名 作用
TriState useAmbientOcclusion() 是否使用环境光遮蔽。实际相关变体接受 BlockState、RenderType 与 ModelData,返回 TriState,既能强制禁用也能强制启用 AO。另有仅接受 BlockState 和无参数的两个布尔返回值重载,均已弃用并计划移除,应使用前者。
boolean isGui3d() 模型在 GUI 槽位中呈三维还是平面。
boolean usesBlockLight() 为 true 时采用三维光照,为 false 时采用来自正面的平面光照。
boolean isCustomRenderer() 为 true 时跳过常规渲染,改调用关联的 BlockEntityWithoutLevelRenderer 的 renderByItem;否则使用默认渲染器。
ItemOverrides getOverrides() 返回关联的 ItemOverrides,仅与物品模型有关。
ModelData getModelData(BlockAndTintGetter, BlockPos, BlockState, ModelData) 返回模型所用数据。传入的已有数据来自 BlockEntity#getModelData(),若无方块实体则为 ModelData.EMPTY。可用于需要模型数据但没有方块实体的方块,例如连接纹理方块。
TextureAtlasSprite getParticleIcon(ModelData) 返回模型所用粒子精灵,可依据模型数据选择不同精灵。由 NeoForge 添加,替代原版无参数的 getParticleIcon()。
ChunkRenderTypeSet getRenderTypes(BlockState, RandomSource, ModelData) 返回方块模型的渲染类型集合。ChunkRenderTypeSet 是以集合为底层、有序的 Iterable<RenderType>。默认回退到模型 JSON 指定的类型。仅用于方块,物品用下一重载。
List<RenderType> getRenderTypes(ItemStack, boolean) 返回物品模型的渲染类型列表。默认回退到常规的模型关联类型查找,结果始终只有一个元素。仅用于物品,方块用上一重载。

显示视角

Minecraft 渲染引擎为物品识别 8 种视角(算上代码回退值则为 9 种)。它们用于模型 JSON 的 display 区块,在代码中由 ItemDisplayContext 枚举表示。

枚举值 JSON 键 用途
THIRD_PERSON_RIGHT_HAND "thirdperson_righthand" 第三人称右手(F5 或其他玩家)。
THIRD_PERSON_LEFT_HAND "thirdperson_lefthand" 第三人称左手。
FIRST_PERSON_RIGHT_HAND "firstperson_righthand" 第一人称右手。
FIRST_PERSON_LEFT_HAND "firstperson_lefthand" 第一人称左手。
HEAD "head" 玩家头部盔甲槽(往往需通过命令实现)。
GUI "gui" 物品栏与快捷栏。
GROUND "ground" 掉落物;旋转由掉落物渲染器处理,而非模型。
FIXED "fixed" 物品展示框。
NONE "none" 代码回退,不应在 JSON 中使用。

ItemOverrides

ItemOverrides 让烘焙模型可以读取 ItemStack 状态,并通过 #resolve 返回新模型。其五个参数为:原始 BakedModel、正在渲染的 ItemStack、渲染所在的 ClientLevel、模型附着的 LivingEntity,以及随机化种子 int。

ClientLevel 只能用于查询,不能修改世界,并且可为 null。LivingEntity 也可为 null,例如从方块实体渲染器渲染时。

ItemOverrides 还以 BakedOverride 保存覆盖选项,后者是模型 overrides 区块在代码中的表示。烘焙模型可依据其内容返回不同模型。通过 ItemOverrides#getOverrides() 可以获取全部选项。

BakedModelWrapper

BakedModelWrapper 可修改已有烘焙模型。它是 BakedModel 的子类,构造函数接受另一个原始模型,默认将所有方法调用转发给它。因此,实现时只需重写目标方法:

// The generic parameter may optionally be a more specific subclass of BakedModel.
// If it is, the constructor parameter must match that type.
public class MyBakedModelWrapper extends BakedModelWrapper<BakedModel> {
    // Pass the original model to super.
    public MyBakedModelWrapper(BakedModel originalModel) {
        super(originalModel);
    }
    
    // Override whatever methods you want here. You may also access originalModel if needed.
}

编写包装类后,需要将其应用到目标模型。在客户端的 ModelEvent.ModifyBakingResult 事件处理器中完成:

@SubscribeEvent // on the mod event bus only on the physical client
public static void modifyBakingResult(ModelEvent.ModifyBakingResult event) {
    // For block models
    event.getModels().computeIfPresent(
        // The model resource location of the model to modify. Get it from
        // BlockModelShaper#stateToModelLocation with the blockstate to be affected as a parameter.
        BlockModelShaper.stateToModelLocation(MyBlocksClass.EXAMPLE_BLOCK.defaultBlockState()),
        // A BiFunction with the location and the original models as parameters, returning the new model.
        (location, model) -> new MyBakedModelWrapper(model);
    );
    // For item models
    event.getModels().computeIfPresent(
        // The model resource location of the model to modify.
        new ModelResourceLocation(
            ResourceLocation.fromNamespaceAndPath("examplemod", "example_item"),
            "inventory"
        ),
        // A BiFunction with the location and the original models as parameters, returning the new model.
        (location, model) -> new MyBakedModelWrapper(model);
    );
}

警告 条件允许时,通常建议优先使用自定义模型加载器,而非在 ModelEvent.ModifyBakingResult 中包装模型。自定义加载器需要时也可以使用 BakedModelWrapper。


来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交 4a680cdd4d15。