烘焙模型
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。