模型

模型

模型是决定方块或物品外观形状和纹理的 JSON 文件,由不同尺寸的长方体元素组成,并为各面分配纹理。

物品按注册名称分配模型。例如,examplemod:example_item 使用 assets/examplemod/models/item/example_item.json。方块则先分配方块状态文件,流程稍复杂,详见下文。

规范

另见 Minecraft Wiki 的模型条目。

模型根对象可包含以下可选属性:

  • loader:NeoForge 添加,指定自定义模型加载器。
  • parent:指定父模型,使用相对于 models 文件夹的资源位置。先继承父模型的全部属性,再由当前模型属性覆盖。常用父模型包括:
    • minecraft:block/block:所有方块模型的共同父模型。
    • minecraft:block/cube:使用 1×1×1 立方体的模型。
    • minecraft:block/cube_all:六面纹理相同,如圆石、木板。
    • minecraft:block/cube_bottom_top:四个侧面相同,顶面和底面各有纹理,如砂岩、錾制石英块。
    • minecraft:block/cube_column:侧面纹理与顶底纹理分开,如原木、石英柱和紫珀柱。
    • minecraft:block/cross:两个使用相同纹理的平面,分别顺时针和逆时针旋转 45°,俯视形成 X,用于草、树苗、花等植物。
    • minecraft:item/generated:经典二维平面物品的父模型,多数物品使用。四边形由纹理生成,因此忽略 elements。
    • minecraft:item/handheld:看起来真正握在玩家手中的二维物品,主要用于工具。继承 item/generated,同样忽略 elements。
    • minecraft:builtin/entity:除 particle 外不指定纹理。使用它时,BakedModel#isCustomRenderer() 返回 true,以使用 BlockEntityWithoutLevelRenderer。
    • 方块物品通常以对应方块模型为父模型,例如圆石物品使用 minecraft:block/cobblestone,但并非绝对。
  • ambientocclusion:是否启用环境光遮蔽,仅对方块模型有效,默认为 true。模型明暗异常时可尝试关闭。
  • render_type:见渲染类型。
  • gui_light:可为 "front"(正面光照,适合二维模型)或 "side"(侧面光照,适合三维模型,尤其是方块)。默认 side,仅对物品有效。
  • textures:将名称(纹理变量)映射到纹理位置的子对象。变量可用于元素;也可在元素中引用但暂不赋值,让子模型指定。
    • 方块模型还应指定 particle 纹理,用于落到方块上、跑过方块或破坏方块时的粒子。
    • 物品模型可使用 layer0、layer1 等图层纹理,编号大的绘制在编号小的上方。仅当父模型为 item/generated 时有效,最多 5 层(layer0 至 layer4)。
  • elements:长方体元素列表。
  • overrides:覆盖模型列表,仅对物品有效。
  • display:保存不同视角显示选项的子对象,键名见链接文章。仅对物品有效,但常写在方块模型中供物品继承。每个视角都是可选子对象,可按以下顺序应用:
    • translation:平移,以 [x, y, z] 指定。
    • rotation:旋转,以 [x, y, z] 指定。
    • scale:缩放,以 [x, y, z] 指定。
    • right_rotation:NeoForge 添加,缩放后的第二次旋转,以 [x, y, z] 指定。
  • transform:见根变换。

建议 不清楚具体写法时,可以参考具有相似功能的原版模型。

渲染类型

NeoForge 的可选 render_type 字段用于指定模型渲染类型。未设置时(所有原版模型均如此),游戏回退到 ItemBlockRenderTypes 中硬编码的类型;如果那里也没有,则使用 minecraft:solid。默认类型有:

  • minecraft:solid:完全不透明的方块,如石头。
  • minecraft:cutout:每个像素只可能完全不透明或完全透明,如玻璃。
  • minecraft:cutout_mipped:cutout 的变体,远处缩小纹理以减少视觉瑕疵(mipmap)。不为物品应用 mipmap,因为通常不需要且可能产生瑕疵,例如树叶使用此类型。
  • minecraft:cutout_mipped_all:同时为物品应用 mipmap 的变体。
  • minecraft:translucent:像素可以半透明,如染色玻璃。
  • minecraft:tripwire:需要渲染到天气目标的特殊方块,即绊线。

选择类型也涉及性能:solid 快于 cutout,cutout 快于 translucent。因此应选择满足需求且限制最严格的类型。

也可以在 Mod 总线的 RegisterNamedRenderTypesEvent 事件中调用 #register 添加类型,参数为:

  • 名称:自动加上 Mod ID 前缀,例如 my_cutout 成为 examplemod:my_cutout。
  • 区块渲染类型:RenderType.chunkBufferLayers() 返回列表中的任意类型。
  • 实体渲染类型:必须使用 DefaultVertexFormat.NEW_ENTITY 顶点格式。
  • 可选的极佳画质类型:同样要求 NEW_ENTITY 格式,在画质为 Fabulous! 时替代普通实体类型;省略则回退到普通类型。若使用透明效果,通常建议指定。

元素

元素是长方体的 JSON 表示,包含:

  • from:起始角坐标 [x, y, z],单位是 1/16 方块。例如 [0, 0, 0] 是左下角,[8, 8, 8] 是中心,[16, 16, 16] 是右上角。
  • to:结束角坐标,单位同上。

建议 Minecraft 将 from、to 限制在 [-16, 32],但强烈不建议超出 [0, 16],否则会出现光照或剔除问题。

  • neoforge_data:见额外面数据。
  • faces:最多包含 north、south、east、west、up、down 六个面。每个面的数据包括:
    • uv:[u1, v1, u2, v2],分别为左上与右下 UV 坐标。
    • texture:以 # 开头的纹理变量,例如 #wood 引用 wood。技术上可省略,但会使用缺失纹理。
    • rotation:可选,顺时针旋转纹理 90、180 或 270 度。
    • cullface:可选,若指定方向有完整方块接触此面,则跳过该面。方向为六个方向名之一。
    • tintindex:可选,颜色处理器使用的染色索引,默认 -1,表示不染色。
    • neoforge_data:见额外面数据。

此外还可指定:

  • shade:仅对方块有效,是否应用与方向相关的明暗,默认为 true。
  • rotation:旋转子对象,包含:
    • angle:角度,-45 至 45,步长 22.5 度。
    • axis:旋转轴,目前不能同时绕多轴旋转。
    • origin:可选旋转中心 [x, y, z],为绝对坐标而非相对立方体位置,默认为 [0, 0, 0]。

额外面数据

可选 neoforge_data 可以应用于整个元素,也可以应用于单个面。两者同时存在时,面级数据覆盖元素级数据。属性包括:

  • color:用指定 ARGB 颜色着色,可为字符串或十进制整数(JSON 不支持十六进制字面量),默认 0xFFFFFFFF。常量颜色可用它替代颜色处理器。
  • block_light:覆盖此面的方块光值,默认 0。
  • sky_light:覆盖此面的天空光值,默认 0。
  • ambient_occlusion:启用或禁用此面的环境光遮蔽,默认采用模型设置。

使用 neoforge:item_layers 加载器时,还能为 item/generated 的所有几何设置额外面数据。下例将第 1 层染红并以最高亮度显示:

{
    "loader": "neoforge:item_layers",
    "parent": "minecraft:item/generated",
    "textures": {
        "layer0": "minecraft:item/stick",
        "layer1": "minecraft:item/glowstone_dust"
    },
    "neoforge_data": {
        "layers": {
            "1": {
                "color": "0xFFFF0000",
                "block_light": 15,
                "sky_light": 15,
                "ambient_occlusion": false
            }
        }
    }
}

覆盖模型

物品覆盖模型依据一个浮点值(覆盖值)分配不同模型,例如弓与弩依据拉弦时间改变纹理。覆盖模型同时需要 JSON 和代码两侧的配置。

模型可指定一个或多个覆盖项,覆盖值达到或超过阈值时启用。例如,弓使用 pulling(1 表示拉弦,0 表示未拉弦)与 pull(拉弦程度)。低于 65% 时只要求 pulling 为 1;达到 65% 时要求 pull 至少 0.65;达到 90% 时要求至少 0.9。如果多项同时符合,使用列表最后一项,因此顺序必须正确:

{
    // other stuff here
    "overrides": [
        {
            // pulling = 1
            "predicate": {
                "pulling": 1
            },
            "model": "item/bow_pulling_0"
        },
        {
            // pulling = 1, pull >= 0.65
            "predicate": {
                "pulling": 1,
                "pull": 0.65
            },
            "model": "item/bow_pulling_1"
        },
        // pulling = 1, pull >= 0.9
        {
            "predicate": {
                "pulling": 1,
                "pull": 0.9
            },
            "model": "item/bow_pulling_2"
        }
    ]
}

代码部分比较简单。假设要添加 examplemod:property 属性,在客户端事件处理器中使用:

@SubscribeEvent // on the mod event bus only on the physical client
public static void onClientSetup(FMLClientSetupEvent event) {
    event.enqueueWork(() -> { // ItemProperties#register is not threadsafe, so we need to call it on the main thread
        ItemProperties.register(
            // The item to apply the property to.
            ExampleItems.EXAMPLE_ITEM,
            // The id of the property.
            ResourceLocation.fromNamespaceAndPath("examplemod", "property"),
            // A reference to a method that calculates the override value.
            // Parameters are the used item stack, the level context, the player using the item,
            // and a random seed you can use.
            (stack, level, player, seed) -> someMethodThatReturnsAFloat()
        );
    });
}

说明 原版仅允许 0 至 1 的浮点值,NeoForge 将其扩展为任意浮点值。

根变换

NeoForge 添加的模型顶层 transform 属性,可在应用方块状态文件旋转或物品 display 变换之前,为全部几何应用变换。

有两种写法。第一种是 matrix,以嵌套 JSON 数组指定 3×4 变换矩阵(行主序,省略最后一行)。矩阵依次组合平移、左旋转、缩放、右旋转和变换原点。例如:

{
    // ...
    "transform": {
        "matrix": [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 0, 0, 0]
        ]
    }
}

第二种是 JSON 对象,包含以下任意属性组合,并按列出顺序应用:

  • translation:相对平移,三维向量 [x, y, z],默认 [0, 0, 0]。
  • rotation 或 left_rotation:缩放前绕平移后原点旋转,默认不旋转。可以写为:
    • 单轴与角度的对象,例如 {"x": 90}。
    • 多个单轴对象的数组,按顺序应用,例如 [{"x": 90}, {"y": 45}, {"x": -22.5}]。
    • 分别指定三轴角度的数组,例如 [90, 45, -22.5]。
    • 直接表示四元数的四元素数组,例如 [0.38268346, 0, 0, 0.9238795](绕 X 轴 45 度)。
  • scale:相对于平移后原点缩放,三维向量,默认 [1, 1, 1]。
  • post_rotation 或 right_rotation:缩放后的旋转,默认无旋转,格式同 rotation。
  • origin:旋转和缩放的原点,最后也会将变换移到这里。可写三维向量,或内置值 "corner"([0,0,0])、"center"([0.5,0.5,0.5])、"opposing-corner"([1,1,1],默认)。

方块状态文件

另见 Minecraft Wiki 的方块状态文件条目。

游戏通过状态文件为不同方块状态分配模型。每个注册方块必须恰好有一个状态文件。模型可以通过 variants 或 multipart 指定,两者互斥。

variants 中每项对应一个状态,大多数方块使用此方式:

  • 键是不含方块名的状态字符串,例如不含水上半台阶为 type=top,waterlogged=false,无属性方块为 ""。未使用属性可以省略,例如 waterlogged 不影响模型时,两个取值可合并为 type=top;空字符串因此适用于任意方块。
  • 值为单个模型对象或模型数组,数组中会随机选一个。模型对象包含:
    • model:相对于命名空间 models 文件夹的路径,例如 minecraft:block/cobblestone。
    • x、y:对应轴上的旋转,仅允许 90 度步长,可选,默认 0。
    • uvlock:旋转时是否锁定 UV,可选,默认 false。
    • weight:仅对数组有用,决定随机选择权重,可选,默认 1。

multipart 则依据状态属性组合元素,主要用于栅栏和墙,以布尔属性启用四向部件。每个元素包含 when 与 apply:

  • when 指定状态字符串,或必须满足的属性列表。列表可命名为 OR 或 AND,对内容执行对应逻辑。单个状态与列表项都可用 | 分隔多个值,例如 facing=east|facing=west。
  • apply 指定模型对象或模型数组,规则与 variants 相同。

染色

草、树叶等方块会依据位置或属性改变纹理颜色。模型元素可在面上指定染色索引,交给颜色处理器处理。代码通过两个事件分别注册方块和物品颜色处理器,方式相似。先看方块:

@SubscribeEvent // on the mod event bus only on the physical client
public static void registerBlockColorHandlers(RegisterColorHandlersEvent.Block event) {
    // Parameters are the block's state, the level the block is in, the block's position, and the tint index.
    // The level and position may be null.
    event.register((state, level, pos, tintIndex) -> {
        // Replace with your own calculation. See the BlockColors class for vanilla references.
        // Colors are in ARGB format. Generally, if the tint index is -1, it means that no tinting
        // should take place and a default value should be used instead.
        return 0xFFFFFFFF;
    },
    // A varargs of blocks to apply the tinting to
    EXAMPLE_BLOCK.value(), ...);
}

物品处理器基本相同,主要区别是名称和 lambda 参数:

@SubscribeEvent // on the mod event bus only on the physical client
public static void registerItemColorHandlers(RegisterColorHandlersEvent.Item event) {
    // Parameters are the item stack and the tint index.
    event.register((stack, tintIndex) -> {
        // Like above, replace with your own calculation. Vanilla values are in the ItemColors class.
        // Also like above, tint index -1 means no tint and should use a default value instead.
        return 0xFFFFFFFF;
    },
    // A varargs of items to apply the tinting to
    EXAMPLE_ITEM.value(), ...);
}

item/generated 为各层指定染色索引:layer0 为 0,layer1 为 1,以此类推。方块物品也属于物品,需要物品颜色处理器才能染色。

注册额外模型

没有关联到方块或物品,但用于方块实体渲染器等其他场景的模型,可通过 ModelEvent.RegisterAdditional 注册:

@SubscribeEvent // on the mod event bus only on the physical client
public static void registerAdditional(ModelEvent.RegisterAdditional event) {
    event.register(new ModelResourceLocation(
        // The id of the model
        ResourceLocation.fromNamespaceAndPath("examplemod", "block/example_unused_model"),
        // The string representing what variant of the model this is for
        // In normal vanilla, this would be one of three values:
        // - Blocks: The stringified block state
        // - Items: 'inventory' as it is the inventory model
        // - Standalone: 'standalone' as this does not refer to any other model
        "variant_type=true"
    ));

    // An inventory model example
    event.register(ModelResourceLocation.inventory(
        ResourceLocation.fromNamespaceAndPath("examplemod", "item/example_unused_inventory_model")
    ));

    // A standalone model example
    event.register(ModelResourceLocation.standalone(
        ResourceLocation.fromNamespaceAndPath("examplemod", "block/example_unused_standalone_model")
    ));
}

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