1.1. IModel

IModel 代表了一个模型的最初形态。这个模型还没有经过处理(即所谓的“bake”(烘培)过程),其具体细节(UV 锁、平滑光照/ambient occlusion、GUI 中是否有 3D 效果、使用的纹理、IModelState/仿射变换状态等等)都有可能发生变化。

public class ABrandNewModel implements IModel {

    private final ResourceLocation modelLocation;

    public ABrandNewModel(ResourceLocation location) {
        this.modelLocation = location;
    }

    @Override
    public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) {
        return aBakedModel;
    }
}

而产生 IBakedModel 的过程便是在这里。在这个方法中可以拿到最终使用的 IModelState、模型使用的顶点格式(VertexFormat)和一个用于获取 TextureAtlasSprite 的高阶函数对象。(当然…… Java 语言中没有 First-class function,这里说高阶函数实际上并不严谨。)

1.1.1. retexture(更换纹理)

public IModel retexture(ImmutableMap<String, String> textures) {
    return this;
}

覆盖这个方法后,在模型 bake 的过程中可以通过 Forge BlockState V1 中单个 variant 下的 textures 字段覆盖现有纹理,该方法传入的 ImmutableMap 实际上也正是 textures 字段代表的 JSON Object 的 Map 形态。典型的使用案例是 Forge 的 ModelDynBucket,用于 Forge 提供的 Universal Bucket 的物品模型。

1.1.2. 自定义数据

@Override
public IModel process(ImmutableMap<String, String> customData) {
    return this;
}

覆盖这个方法后,在模型 bake 的过程中可以读取到一系列额外的数据;这些数据需要通过 Forge BlockState V1 中单个 variant 下追加一名为 custom 的 JSON Object 字段来提供。典型的使用案例是 Forge 提供的流体模型(关于流体的内容会在第二十六章中提到),它通过这个自定义数据来获知目标流体的具体名称:

{
    "forge_marker": 1,
    "variants": {
        "my_fluid": {
            "model": "forge:fluid",
            "custom": {
                "fluid": "my_fluid"
            }
        }
    }
}