Kick more things into an api

This commit is contained in:
quat1024 2023-09-03 17:33:34 -04:00
parent daa6c52db6
commit fd1222f2b6
13 changed files with 395 additions and 165 deletions

View File

@ -2,8 +2,10 @@ Versions before 2.1.2 have been backfilled; I gotta be more on top of changelogs
# next version (unreleased) # next version (unreleased)
* Start sketching out a proper API, accessible through `TemplatesClientApi.getInstance()`.
* Not everything has been moved into the API package yet for ABI reasons.
* Code cleanups that hopefully didn't break ABI compat
* Remove some unused stuff from the jar * Remove some unused stuff from the jar
* Code cleanups, hopefully without breaking ABI compat (i don't have an ABI checker in the pipeline tho)
* Fix a bug where templates that look like blocks with randomized models, such as stone, could reroll their blockstate on every resource load. * Fix a bug where templates that look like blocks with randomized models, such as stone, could reroll their blockstate on every resource load.
* Forgot to specify a random seed. * Forgot to specify a random seed.
* All templated blocks still use the *same* model, so templated stone will still not be randomly rotated/flipped, but at least it's now the *same* same model. * All templated blocks still use the *same* model, so templated stone will still not be randomly rotated/flipped, but at least it's now the *same* same model.

View File

@ -54,25 +54,25 @@ The last piece of information is important because Templates tries hard to retai
Pick a model implementation that suits your needs: Pick a model implementation that suits your needs:
### `UnbakedAutoRetexturedModel` ### Auto retexturing
* the quad: Sourced from a JSON model. * the quad: Sourced from a JSON model.
* whether you want to retexture it: "Yes". All quads will be retextured. * whether you want to retexture it: "Yes". All quads will be retextured.
* what face of the block: Automatically determined by facing direction. * what face of the block: Automatically determined by facing direction.
To construct, pass the ID of the JSON model you want to source quads from. Construct with `TemplatesClientApi.getInstance().auto`. Pass the ID of the model you want to source quads from.
There's no way to configure this, so if you want to skip retexturing a face, try the next model implementation instead. There's no way to configure this, so if you want to skip retexturing a face, try the next model implementation instead.
**TODO**: this does not work well with `multipart` models with differently-rotated parts, like the fence model (consisting of 1 fence post and 1 fence-side model that gets rotated around to fill all 4 sides) **TODO**: this does not work well with `multipart` models with differently-rotated parts, like the fence model (consisting of 1 fence post and 1 fence-side model that gets rotated around to fill all 4 sides)
### `UnbakedJsonRetexturedModel` ### Special texture-based retexturing
* the quad: Sourced from a JSON model. * the quad: Sourced from a JSON model.
* whether you want to retexture it: Determined from the texture applied to the quad. * whether you want to retexture it: Determined from the texture applied to the quad.
* what face of the block: Determined via the texture applied to the quad. * what face of the block: Determined via the texture applied to the quad.
To construct, pass the ID of a JSON model to retexture. All quads textured with `templates:templates_special/east` will be textured with the east side of the theme, all quads textured with `templates:templates_special/up` will be retextured with the top side of the theme, etc. Quads textured with any other texture will be passed through unaltered. Construct with `TemplatesClientApi.getInstance().json`. Pass the ID of the model you want to source quads from. All quads textured with the *special textures* `templates:templates_special/east` will be textured with the east side of the theme, all quads textured with `templates:templates_special/up` will be retextured with the top side of the theme, etc. Quads textured with any other texture will be passed through unaltered.
<details><summary>Regarding texture variables:</summary> <details><summary>Regarding texture variables:</summary>
@ -97,31 +97,31 @@ Sadly, many models don't specify *completely* separate textures for all six side
(This one works better with multipart models.) (This one works better with multipart models.)
### `UnbakedMeshRetexturedModel` ### Mesh retexturing
* the quad: Sourced from a `Mesh`. * the quad: Sourced from a `Mesh`.
* whether you want to retexture it: Quads with a nonzero `tag`. * whether you want to retexture it: Quads with a nonzero `tag`.
* what face of the block: Determined from the `tag`. * what face of the block: Determined from the `tag`.
To construct, pass a `Supplier<Mesh>`. To mark a face "retexture this with the EAST side of the block", call `.tag(Direction.EAST.ordinal() + 1)` on it; same for the other directions. (So, the valid tags are 1, 2, 3, 4, 5, and 6, corresponding to down, up, north, south, west, east.) Give these faces UV coordinates ranging from 0 to 1. Construct with `TemplatesClientApi.getInstance().mesh`, passing a `Supplier<Mesh>`. To mark a face "retexture this with the EAST side of the block", call `.tag(Direction.EAST.ordinal() + 1)` on it; same for the other directions. (So, the valid tags are 1, 2, 3, 4, 5, and 6, corresponding to down, up, north, south, west, east.) Give these faces UV coordinates ranging from 0 to 1.
A `.tag` of 0 (the default) will be passed through unchanged. This is a little useless since you still need to provide UV coordinates, so instead of passing a `Supplier<Mesh>` you can also pass a `Function<Function<SpriteIdentifier, Sprite>, Mesh>`; you will be provided with a `Function<SpriteIdentifier, Sprite>` that you can query for sprite information, including their UVs. A `.tag` of 0 (the default) will be passed through unchanged. This is a little useless since you still need to provide UV coordinates, so instead of passing a `Supplier<Mesh>` you can also pass a `Function<Function<SpriteIdentifier, Sprite>, Mesh>`; you will be provided with a `Function<SpriteIdentifier, Sprite>` that you can query for sprite information, including their UVs.
(To construct this type, you will also need to pass the identifier of a "base model", which can be a regular JSON model. Miscellaneous `BakedModel` properties like rotations, AO, `isSideLit`, etc will be sourced from it. See Template's `models/block/slope_base`. You may need to set `"gui_light": "front"` to avoid a flat look in the ui.) (To construct this type, you will also need to pass the identifier of a "base model", which can be a regular JSON model. Miscellaneous `BakedModel` properties like rotations, AO, `isSideLit`, etc will be sourced from it. See Template's `models/block/slope_base`. You may need to set `"gui_light": "front"` to avoid a flat look in the ui.)
### A secret fourth thing ### A secret, fourth thing
Templates doesn't actually care about the block model you pass in. It won't *work* unless you reimplement the retexturing, but if you have your needs I won't stop you. Templates doesn't actually care about the implementation of the UnbakedModel you pass in. It won't *work* unless you reimplement the retexturing, but if you have your needs I won't stop you.
All the models are supposed to be extensible (if i left a stray `private` let me know). All the `UnbakedModels` are backed by the same abstract class called `RetexturingBakedModel` which actually does the retexturing; feel free to extend it. All the models are supposed to be extensible (if i left a stray `private` let me know). All the `UnbakedModels` are backed by the same abstract class called `RetexturingBakedModel` which actually does the retexturing; feel free to extend it.
## Registering your model ## Registering your model
After you've decided on and constructed your special model, you should tell Templates about it. Pick an ID that's different from the base model. (If your base model is `mymod:block/awesome_template`, a good name might be `mymod:awesome_template_special`). Register your special model under that ID using `TemplatesClient.provider.addTemplateModel`. After you've decided on and constructed your special model, you should tell Templates about it. Pick an ID that's different from the base model. (If your base model is `mymod:block/awesome_template`, a good name might be `mymod:awesome_template_special`). Register your special model under that ID using `TemplatesClientApi.getInstance().addTemplateModel`.
To assign the block model, using a vanilla blockstate file, simply point your block at that model ID as normal. (See this mod's `blockstates` folder.) You may also use the `x`, `y`, and `uvlock` properties. To assign the block model, using a vanilla blockstate file, simply point your block at that model ID as normal. (See this mod's `blockstates` folder.) You may also use the `x`, `y`, and `uvlock` properties.
To assign the item model, since items don't have the "blockstate file" level of indirection, call `TemplatesClient.provider.assignItemModel`, passing your special model's ID and the items it should be assigned to. Or if you'd rather use a vanilla json model (that won't be retextured) just make one the vanilla way. To assign the item model, since items don't have the "blockstate file" level of indirection, call `TemplatesClientApi.getInstance().assignItemModel`, passing your special model's ID and the items it should be assigned to. Or if you'd rather use a vanilla json model (that won't be retextured) just make one the vanilla way.
# Most important attribution in the whole wide world # Most important attribution in the whole wide world

View File

@ -1,17 +1,12 @@
package io.github.cottonmc.templates; package io.github.cottonmc.templates;
import io.github.cottonmc.templates.api.TemplatesClientApi;
import io.github.cottonmc.templates.model.SlopeBaseMesh; import io.github.cottonmc.templates.model.SlopeBaseMesh;
import io.github.cottonmc.templates.model.UnbakedAutoRetexturedModel;
import io.github.cottonmc.templates.model.UnbakedJsonRetexturedModel;
import io.github.cottonmc.templates.model.UnbakedMeshRetexturedModel;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
@ -19,15 +14,106 @@ import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceType; import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.ChunkSectionPos; import net.minecraft.util.math.ChunkSectionPos;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.ApiStatus;
public class TemplatesClient implements ClientModInitializer { public class TemplatesClient implements ClientModInitializer {
//2.2 note: Yes, this wasn't final before, but it should have been. @ApiStatus.Internal //2.2 - Please use the new TemplatesClientApi.getInstance() method.
//This field is considered public api by the way, feel free to use it instead of making your own copy.
public static final TemplatesModelProvider provider = new TemplatesModelProvider(); public static final TemplatesModelProvider provider = new TemplatesModelProvider();
@ApiStatus.Internal //Please use TemplatesClientApi.getInstance() instead.
public static final TemplatesClientApiImpl API_IMPL = new TemplatesClientApiImpl(provider);
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
privateInit(); //<- Stuff you shouldn't replicate in any addon mods ;)
//all templates mustn't be on the SOLID layer because they are not opaque!
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), Templates.INTERNAL_TEMPLATES.toArray(new Block[0]));
//now, assign special item models
TemplatesClientApi api = TemplatesClientApi.getInstance();
api.addTemplateModel(Templates.id("button_special") , api.auto(new Identifier("block/button")));
api.addTemplateModel(Templates.id("button_pressed_special") , api.auto(new Identifier("block/button_pressed")));
api.addTemplateModel(Templates.id("one_candle_special") , api.auto(new Identifier("block/template_candle")));
api.addTemplateModel(Templates.id("two_candles_special") , api.auto(new Identifier("block/template_two_candles")));
api.addTemplateModel(Templates.id("three_candles_special") , api.auto(new Identifier("block/template_three_candles")));
api.addTemplateModel(Templates.id("four_candles_special") , api.auto(new Identifier("block/template_four_candles")));
api.addTemplateModel(Templates.id("carpet_special") , api.auto(new Identifier("block/carpet")));
api.addTemplateModel(Templates.id("cube_special") , api.auto(new Identifier("block/cube")));
api.addTemplateModel(Templates.id("door_bottom_left_special") , api.auto(new Identifier("block/door_bottom_left")));
api.addTemplateModel(Templates.id("door_bottom_right_special") , api.auto(new Identifier("block/door_bottom_right")));
api.addTemplateModel(Templates.id("door_top_left_special") , api.auto(new Identifier("block/door_top_left")));
api.addTemplateModel(Templates.id("door_top_right_special") , api.auto(new Identifier("block/door_top_right")));
api.addTemplateModel(Templates.id("door_bottom_left_open_special"), api.auto(new Identifier("block/door_bottom_left_open")));
api.addTemplateModel(Templates.id("door_bottom_right_open_special"), api.auto(new Identifier("block/door_bottom_right_open"))); //This is why we dont format code as tables kids
api.addTemplateModel(Templates.id("door_top_left_open_special") , api.auto(new Identifier("block/door_top_left_open")));
api.addTemplateModel(Templates.id("door_top_right_open_special") , api.auto(new Identifier("block/door_top_right_open")));
api.addTemplateModel(Templates.id("fence_post_special") , api.auto(new Identifier("block/fence_post")));
api.addTemplateModel(Templates.id("fence_gate_special") , api.auto(new Identifier("block/template_fence_gate")));
api.addTemplateModel(Templates.id("fence_gate_open_special") , api.auto(new Identifier("block/template_fence_gate_open")));
api.addTemplateModel(Templates.id("fence_gate_wall_special") , api.auto(new Identifier("block/template_fence_gate_wall")));
api.addTemplateModel(Templates.id("fence_gate_wall_open_special") , api.auto(new Identifier("block/template_fence_gate_wall_open")));
api.addTemplateModel(Templates.id("glass_pane_post_special") , api.auto(new Identifier("block/glass_pane_post")));
api.addTemplateModel(Templates.id("glass_pane_noside_special") , api.auto(new Identifier("block/glass_pane_noside")));
api.addTemplateModel(Templates.id("glass_pane_noside_alt_special"), api.auto(new Identifier("block/glass_pane_noside_alt")));
api.addTemplateModel(Templates.id("pressure_plate_up_special") , api.auto(new Identifier("block/pressure_plate_up")));
api.addTemplateModel(Templates.id("pressure_plate_down_special") , api.auto(new Identifier("block/pressure_plate_down")));
api.addTemplateModel(Templates.id("slab_bottom_special") , api.auto(new Identifier("block/slab")));
api.addTemplateModel(Templates.id("slab_top_special") , api.auto(new Identifier("block/slab_top")));
api.addTemplateModel(Templates.id("stairs_special") , api.auto(new Identifier("block/stairs")));
api.addTemplateModel(Templates.id("inner_stairs_special") , api.auto(new Identifier("block/inner_stairs")));
api.addTemplateModel(Templates.id("outer_stairs_special") , api.auto(new Identifier("block/outer_stairs")));
api.addTemplateModel(Templates.id("trapdoor_bottom_special") , api.auto(new Identifier("block/template_trapdoor_bottom")));
api.addTemplateModel(Templates.id("trapdoor_top_special") , api.auto(new Identifier("block/template_trapdoor_top")));
api.addTemplateModel(Templates.id("vertical_slab_special") , api.auto(Templates.id("block/vertical_slab"))); //my model not vanilla
api.addTemplateModel(Templates.id("wall_post_special") , api.auto(new Identifier("block/template_wall_post")));
//vanilla style models (using "special-sprite replacement" method)
api.addTemplateModel(Templates.id("lever_special") , api.json(Templates.id("block/lever")));
api.addTemplateModel(Templates.id("trapdoor_open_special") , api.json(Templates.id("block/trapdoor_open")));
api.addTemplateModel(Templates.id("lever_on_special") , api.json(Templates.id("block/lever_on")));
//these next five only exist because AutoRetexturedModels don't seem to rotate their textures the right way when rotated from a multipart blockstate
api.addTemplateModel(Templates.id("fence_side_special") , api.json(Templates.id("block/fence_side")));
api.addTemplateModel(Templates.id("glass_pane_side_special") , api.json(Templates.id("block/glass_pane_side")));
api.addTemplateModel(Templates.id("glass_pane_side_alt_special") , api.json(Templates.id("block/glass_pane_side_alt")));
api.addTemplateModel(Templates.id("wall_side_special") , api.json(Templates.id("block/wall_side")));
api.addTemplateModel(Templates.id("wall_side_tall_special") , api.json(Templates.id("block/wall_side_tall")));
//mesh models
api.addTemplateModel(Templates.id("slope_special") , api.mesh(Templates.id("block/slope_base"), SlopeBaseMesh::makeUpright).disableAo());
api.addTemplateModel(Templates.id("slope_side_special") , api.mesh(Templates.id("block/slope_base"), SlopeBaseMesh::makeSide).disableAo());
api.addTemplateModel(Templates.id("tiny_slope_special") , api.mesh(Templates.id("block/tiny_slope_base"), SlopeBaseMesh::makeTinyUpright).disableAo());
api.addTemplateModel(Templates.id("tiny_slope_side_special") , api.mesh(Templates.id("block/tiny_slope_base"), SlopeBaseMesh::makeTinySide).disableAo());
//item only models
api.addTemplateModel(Templates.id("button_inventory_special") , api.auto(new Identifier("block/button_inventory")));
api.addTemplateModel(Templates.id("fence_inventory_special") , api.auto(new Identifier("block/fence_inventory")));
api.addTemplateModel(Templates.id("fence_post_inventory_special") , api.auto(Templates.id("block/fence_post_inventory")));
api.addTemplateModel(Templates.id("wall_inventory_special") , api.auto(new Identifier("block/wall_inventory")));
//item model assignments (in lieu of models/item/___.json)
api.assignItemModel(Templates.id("button_inventory_special") , Templates.BUTTON);
api.assignItemModel(Templates.id("carpet_special") , Templates.CARPET);
api.assignItemModel(Templates.id("cube_special") , Templates.CUBE);
api.assignItemModel(Templates.id("fence_inventory_special") , Templates.FENCE);
api.assignItemModel(Templates.id("fence_gate_special") , Templates.FENCE_GATE);
api.assignItemModel(Templates.id("trapdoor_bottom_special") , Templates.IRON_TRAPDOOR);
api.assignItemModel(Templates.id("fence_post_inventory_special") , Templates.POST);
api.assignItemModel(Templates.id("pressure_plate_up_special") , Templates.PRESSURE_PLATE);
api.assignItemModel(Templates.id("slab_bottom_special") , Templates.SLAB);
api.assignItemModel(Templates.id("stairs_special") , Templates.STAIRS);
api.assignItemModel(Templates.id("trapdoor_bottom_special") , Templates.TRAPDOOR);
api.assignItemModel(Templates.id("vertical_slab_special") , Templates.VERTICAL_SLAB);
api.assignItemModel(Templates.id("wall_inventory_special") , Templates.WALL);
api.assignItemModel(Templates.id("slope_special") , Templates.SLOPE);
api.assignItemModel(Templates.id("tiny_slope_special") , Templates.TINY_SLOPE);
//TODO: i could stick some kind of entrypoint here for signalling other mods that it's ok to register now?
// Dont think it rly matters though, everything's all kept in nice hash maps
}
private void privateInit() {
//set up some magic to force chunk rerenders when you change a template (see TemplateEntity) //set up some magic to force chunk rerenders when you change a template (see TemplateEntity)
Templates.chunkRerenderProxy = (world, pos) -> { Templates.chunkRerenderProxy = (world, pos) -> {
if(world == MinecraftClient.getInstance().world) { if(world == MinecraftClient.getInstance().world) {
@ -47,101 +133,5 @@ public class TemplatesClient implements ClientModInitializer {
@Override public Identifier getFabricId() { return Templates.id("dump-caches"); } @Override public Identifier getFabricId() { return Templates.id("dump-caches"); }
@Override public void reload(ResourceManager blah) { provider.dumpCache(); } @Override public void reload(ResourceManager blah) { provider.dumpCache(); }
}); });
//all templates mustn't be on the SOLID layer because they are not opaque by default (!)
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), Templates.INTERNAL_TEMPLATES.toArray(new Block[0]));
//and a big wall of fancy models
provider.addTemplateModel(Templates.id("button_special") , new UnbakedAutoRetexturedModel(new Identifier("block/button")));
provider.addTemplateModel(Templates.id("button_pressed_special") , new UnbakedAutoRetexturedModel(new Identifier("block/button_pressed")));
provider.addTemplateModel(Templates.id("one_candle_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_candle")));
provider.addTemplateModel(Templates.id("two_candles_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_two_candles")));
provider.addTemplateModel(Templates.id("three_candles_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_three_candles")));
provider.addTemplateModel(Templates.id("four_candles_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_four_candles")));
provider.addTemplateModel(Templates.id("carpet_special") , new UnbakedAutoRetexturedModel(new Identifier("block/carpet")));
provider.addTemplateModel(Templates.id("cube_special") , new UnbakedAutoRetexturedModel(new Identifier("block/cube")));
provider.addTemplateModel(Templates.id("door_bottom_left_special") , new UnbakedAutoRetexturedModel(new Identifier("block/door_bottom_left")));
provider.addTemplateModel(Templates.id("door_bottom_right_special") , new UnbakedAutoRetexturedModel(new Identifier("block/door_bottom_right")));
provider.addTemplateModel(Templates.id("door_top_left_special") , new UnbakedAutoRetexturedModel(new Identifier("block/door_top_left")));
provider.addTemplateModel(Templates.id("door_top_right_special") , new UnbakedAutoRetexturedModel(new Identifier("block/door_top_right")));
provider.addTemplateModel(Templates.id("door_bottom_left_open_special"), new UnbakedAutoRetexturedModel(new Identifier("block/door_bottom_left_open")));
provider.addTemplateModel(Templates.id("door_bottom_right_open_special"), new UnbakedAutoRetexturedModel(new Identifier("block/door_bottom_right_open"))); //This is why we dont format code as tables kids
provider.addTemplateModel(Templates.id("door_top_left_open_special") , new UnbakedAutoRetexturedModel(new Identifier("block/door_top_left_open")));
provider.addTemplateModel(Templates.id("door_top_right_open_special") , new UnbakedAutoRetexturedModel(new Identifier("block/door_top_right_open")));
provider.addTemplateModel(Templates.id("fence_post_special") , new UnbakedAutoRetexturedModel(new Identifier("block/fence_post")));
provider.addTemplateModel(Templates.id("fence_gate_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_fence_gate")));
provider.addTemplateModel(Templates.id("fence_gate_open_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_fence_gate_open")));
provider.addTemplateModel(Templates.id("fence_gate_wall_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_fence_gate_wall")));
provider.addTemplateModel(Templates.id("fence_gate_wall_open_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_fence_gate_wall_open")));
provider.addTemplateModel(Templates.id("glass_pane_post_special") , new UnbakedAutoRetexturedModel(new Identifier("block/glass_pane_post")));
provider.addTemplateModel(Templates.id("glass_pane_noside_special") , new UnbakedAutoRetexturedModel(new Identifier("block/glass_pane_noside")));
provider.addTemplateModel(Templates.id("glass_pane_noside_alt_special"), new UnbakedAutoRetexturedModel(new Identifier("block/glass_pane_noside_alt")));
provider.addTemplateModel(Templates.id("pressure_plate_up_special") , new UnbakedAutoRetexturedModel(new Identifier("block/pressure_plate_up")));
provider.addTemplateModel(Templates.id("pressure_plate_down_special") , new UnbakedAutoRetexturedModel(new Identifier("block/pressure_plate_down")));
provider.addTemplateModel(Templates.id("slab_bottom_special") , new UnbakedAutoRetexturedModel(new Identifier("block/slab")));
provider.addTemplateModel(Templates.id("slab_top_special") , new UnbakedAutoRetexturedModel(new Identifier("block/slab_top")));
provider.addTemplateModel(Templates.id("stairs_special") , new UnbakedAutoRetexturedModel(new Identifier("block/stairs")));
provider.addTemplateModel(Templates.id("inner_stairs_special") , new UnbakedAutoRetexturedModel(new Identifier("block/inner_stairs")));
provider.addTemplateModel(Templates.id("outer_stairs_special") , new UnbakedAutoRetexturedModel(new Identifier("block/outer_stairs")));
provider.addTemplateModel(Templates.id("trapdoor_bottom_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_trapdoor_bottom")));
provider.addTemplateModel(Templates.id("trapdoor_top_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_trapdoor_top")));
provider.addTemplateModel(Templates.id("vertical_slab_special") , new UnbakedAutoRetexturedModel(Templates.id("block/vertical_slab"))); //my model not vanilla
provider.addTemplateModel(Templates.id("wall_post_special") , new UnbakedAutoRetexturedModel(new Identifier("block/template_wall_post")));
//vanilla style models (using "special-sprite replacement" method)
provider.addTemplateModel(Templates.id("lever_special") , new UnbakedJsonRetexturedModel(Templates.id("block/lever")));
provider.addTemplateModel(Templates.id("trapdoor_open_special") , new UnbakedJsonRetexturedModel(Templates.id("block/trapdoor_open")));
provider.addTemplateModel(Templates.id("lever_on_special") , new UnbakedJsonRetexturedModel(Templates.id("block/lever_on")));
//these only exist because AutoRetexturedModels don't seem to rotate their textures the right way when rotated from a multipart blockstate
provider.addTemplateModel(Templates.id("fence_side_special") , new UnbakedJsonRetexturedModel(Templates.id("block/fence_side")));
provider.addTemplateModel(Templates.id("glass_pane_side_special") , new UnbakedJsonRetexturedModel(Templates.id("block/glass_pane_side")));
provider.addTemplateModel(Templates.id("glass_pane_side_alt_special") , new UnbakedAutoRetexturedModel(Templates.id("block/glass_pane_side_alt")));
provider.addTemplateModel(Templates.id("wall_side_special") , new UnbakedJsonRetexturedModel(Templates.id("block/wall_side")));
provider.addTemplateModel(Templates.id("wall_side_tall_special") , new UnbakedJsonRetexturedModel(Templates.id("block/wall_side_tall")));
//mesh models
provider.addTemplateModel(Templates.id("slope_special") , new UnbakedMeshRetexturedModel(Templates.id("block/slope_base"), SlopeBaseMesh::makeUpright).disableAo());
provider.addTemplateModel(Templates.id("slope_side_special") , new UnbakedMeshRetexturedModel(Templates.id("block/slope_base"), SlopeBaseMesh::makeSide).disableAo());
provider.addTemplateModel(Templates.id("tiny_slope_special") , new UnbakedMeshRetexturedModel(Templates.id("block/tiny_slope_base"), SlopeBaseMesh::makeTinyUpright).disableAo());
provider.addTemplateModel(Templates.id("tiny_slope_side_special") , new UnbakedMeshRetexturedModel(Templates.id("block/tiny_slope_base"), SlopeBaseMesh::makeTinySide).disableAo());
//item only models
provider.addTemplateModel(Templates.id("button_inventory_special") , new UnbakedAutoRetexturedModel(new Identifier("block/button_inventory")));
provider.addTemplateModel(Templates.id("fence_inventory_special") , new UnbakedAutoRetexturedModel(new Identifier("block/fence_inventory")));
provider.addTemplateModel(Templates.id("fence_post_inventory_special") , new UnbakedAutoRetexturedModel(Templates.id("block/fence_post_inventory")));
provider.addTemplateModel(Templates.id("wall_inventory_special") , new UnbakedAutoRetexturedModel(new Identifier("block/wall_inventory")));
//item model assignments (in lieu of models/item/___.json)
provider.assignItemModel(Templates.id("button_inventory_special") , Templates.BUTTON);
provider.assignItemModel(Templates.id("carpet_special") , Templates.CARPET);
provider.assignItemModel(Templates.id("cube_special") , Templates.CUBE);
provider.assignItemModel(Templates.id("fence_inventory_special") , Templates.FENCE);
provider.assignItemModel(Templates.id("fence_gate_special") , Templates.FENCE_GATE);
provider.assignItemModel(Templates.id("trapdoor_bottom_special") , Templates.IRON_TRAPDOOR);
provider.assignItemModel(Templates.id("fence_post_inventory_special") , Templates.POST);
provider.assignItemModel(Templates.id("pressure_plate_up_special") , Templates.PRESSURE_PLATE);
provider.assignItemModel(Templates.id("slab_bottom_special") , Templates.SLAB);
provider.assignItemModel(Templates.id("stairs_special") , Templates.STAIRS);
provider.assignItemModel(Templates.id("trapdoor_bottom_special") , Templates.TRAPDOOR);
provider.assignItemModel(Templates.id("vertical_slab_special") , Templates.VERTICAL_SLAB);
provider.assignItemModel(Templates.id("wall_inventory_special") , Templates.WALL);
provider.assignItemModel(Templates.id("slope_special") , Templates.SLOPE);
provider.assignItemModel(Templates.id("tiny_slope_special") , Templates.TINY_SLOPE);
}
public static @NotNull Renderer getFabricRenderer() {
Renderer obj = RendererAccess.INSTANCE.getRenderer();
if(obj != null) return obj;
//Welp, not much more we can do, this mod heavily relies on frapi
String msg = "A Fabric Rendering API implementation is required to use Templates 2!";
if(!FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo"))
msg += "\nI noticed you don't have Indigo installed, which is a part of the complete Fabric API package.";
if(FabricLoader.getInstance().isModLoaded("sodium"))
msg += "\nI noticed you have Sodium installed - consider also installing Indium to provide a compatible renderer implementation.";
throw new NullPointerException(msg);
} }
} }

View File

@ -0,0 +1,84 @@
package io.github.cottonmc.templates;
import io.github.cottonmc.templates.api.TemplatesClientApi;
import io.github.cottonmc.templates.model.TemplateAppearanceManager;
import io.github.cottonmc.templates.model.UnbakedAutoRetexturedModel;
import io.github.cottonmc.templates.model.UnbakedJsonRetexturedModel;
import io.github.cottonmc.templates.model.UnbakedMeshRetexturedModel;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.item.ItemConvertible;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
public class TemplatesClientApiImpl implements TemplatesClientApi {
public TemplatesClientApiImpl(TemplatesModelProvider prov) {
this.prov = prov;
}
private final TemplatesModelProvider prov;
@Override
public TweakableUnbakedModel auto(Identifier parent) {
return new UnbakedAutoRetexturedModel(parent);
}
@Override
public TweakableUnbakedModel json(Identifier parent) {
return new UnbakedJsonRetexturedModel(parent);
}
@Override
public TweakableUnbakedModel mesh(Identifier parent, Function<Function<SpriteIdentifier, Sprite>, Mesh> baseMeshFactory) {
return new UnbakedMeshRetexturedModel(parent, baseMeshFactory);
}
@Override
public void addTemplateModel(Identifier id, UnbakedModel unbaked) {
prov.addTemplateModel(id, unbaked);
}
@Override
public void assignItemModel(Identifier templateModelId, ModelIdentifier... modelIds) {
prov.assignItemModel(templateModelId, modelIds);
}
@Override
public void assignItemModel(Identifier templateModelId, Identifier... itemIds) {
prov.assignItemModel(templateModelId, itemIds);
}
@Override
public void assignItemModel(Identifier templateModelId, ItemConvertible... itemConvs) {
prov.assignItemModel(templateModelId, itemConvs);
}
@Override
public TemplateAppearanceManager getOrCreateTemplateApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
return prov.getOrCreateTemplateApperanceManager(spriteLookup);
}
@Override
public @NotNull Renderer getFabricRenderer() {
Renderer obj = RendererAccess.INSTANCE.getRenderer();
if(obj != null) return obj;
//Welp, not much more we can do, this mod heavily relies on frapi
String msg = "A Fabric Rendering API implementation is required to use Templates 2!";
if(!FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo"))
msg += "\nI noticed you don't have Indigo installed, which is a part of the complete Fabric API package.";
if(FabricLoader.getInstance().isModLoaded("sodium"))
msg += "\nI noticed you have Sodium installed - consider also installing Indium to provide a compatible renderer implementation.";
throw new NullPointerException(msg);
}
}

View File

@ -18,7 +18,6 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
@SuppressWarnings("unused") //this class is part of the api
public class TemplatesModelProvider implements ModelResourceProvider, ModelVariantProvider { public class TemplatesModelProvider implements ModelResourceProvider, ModelVariantProvider {
private final Map<Identifier, UnbakedModel> models = new HashMap<>(); private final Map<Identifier, UnbakedModel> models = new HashMap<>();
private final Map<ModelIdentifier, Identifier> itemAssignments = new HashMap<>(); private final Map<ModelIdentifier, Identifier> itemAssignments = new HashMap<>();
@ -74,10 +73,8 @@ public class TemplatesModelProvider implements ModelResourceProvider, ModelVaria
appearanceManager = null; //volatile write appearanceManager = null; //volatile write
} }
/// "public api" public void addTemplateModel(Identifier id, UnbakedModel unbaked) {
models.put(id, unbaked);
public void addTemplateModel(Identifier id, UnbakedModel modelFactory) {
models.put(id, modelFactory);
} }
public void assignItemModel(Identifier templateModelId, ModelIdentifier... modelIds) { public void assignItemModel(Identifier templateModelId, ModelIdentifier... modelIds) {

View File

@ -0,0 +1,116 @@
package io.github.cottonmc.templates.api;
import io.github.cottonmc.templates.TemplatesClient;
import io.github.cottonmc.templates.model.TemplateAppearanceManager;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.item.ItemConvertible;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
import java.util.function.Supplier;
@ApiStatus.AvailableSince("2.2")
@SuppressWarnings({"unused", "UnusedReturnValue"}) //this is all public api
public interface TemplatesClientApi {
/**
* Obtain the current API instance.
*/
static TemplatesClientApi getInstance() {
return TemplatesClient.API_IMPL;
}
/// CONSTRUCTING UNBAKED MODELS ///
/**
* - the quad: Sourced from the ID you pass in. It can be a json model.
* - whether you want to retexture it: "Yes". All quads will be retextured.
* - what face of the block: Automatically determined by facing direction.
*/
TweakableUnbakedModel auto(Identifier parent);
/**
* - the quad: Sourced from the ID you pass in. It can be a json model.
* - whether you want to retexture it: Determined from the texture applied to the quad.
* - what face of the block: Determined via the texture applied to the quad.
*/
TweakableUnbakedModel json(Identifier parent);
/**
* - the quad: Sourced from a `Mesh`.
* - whether you want to retexture it: Quads with a nonzero `tag`.
* - what face of the block: Determined from the `tag`.
* <p>
* This form doesn't give the ability to look up sprites, so it's hard to make a sensible quad that won't be retextured.
*/
default TweakableUnbakedModel mesh(Identifier parent, Supplier<Mesh> baseMeshFactory) {
return mesh(parent, __ -> baseMeshFactory.get());
}
/**
* - the quad: Sourced from a `Mesh`.
* - whether you want to retexture it: Quads with a nonzero `tag`.
* - what face of the block: Determined from the `tag`.
* <p>
* You can use the provided Function<SpriteIdentifier, Sprite> to look up sprite UVs and put them on faces with a 0 tag.
* These faces will not get retextured.
*/
TweakableUnbakedModel mesh(Identifier parent, Function<Function<SpriteIdentifier, Sprite>, Mesh> baseMeshFactory);
/**
* Get the TemplateAppearanceManager instance. To retexture a template, there has to be some way of determining what texture should
* go on the top, what texture should go on the north side, and the TemplateAppearanceManager is in charge of gleaning this information
* from the target blockmodels. It also caches this information.
* <p>
* There is one TemplateApperanceManager per resource-load. Please obtain a new one every model bake.
*
* @param spriteLookup Something you'll find as part of UnbakedModel#bake.
*/
TemplateAppearanceManager getOrCreateTemplateApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup);
/// REGISTERING UNBAKED MODELS ///
/**
* Register an UnbakedModel to be loaded behind a particular ID.
* Astute viewers will note that this is, *currently*, a thin wrapper around the fabric ModelResourceProvider system.
*/
void addTemplateModel(Identifier id, UnbakedModel unbaked);
/**
* When the game loads this ModelIdentifier, it will instead load the UnbakedModel corresponding to the id passed to addTemplateModel.
* Astute viewers will note that this is, *currently*, a thin wrapper around the fabric ModelVariantProvider system.
*/
void assignItemModel(Identifier templateModelId, ModelIdentifier... modelIds);
/**
* Calls assignItemModel(Identifier, ModelIdentifier) with "#inventory" appended.
* In practice: you can pass an item's ID.
*/
void assignItemModel(Identifier templateModelId, Identifier... itemIds);
/**
* Calls assignItemModel(Identifier, Identifier) by first converting the argument to an item, then taking its ID.
* In practice: you can pass a Block (or Item), and the model will be assigned to the block's item form.
*/
void assignItemModel(Identifier templateModelId, ItemConvertible... itemConvs);
/// OTHER STUFF LOL ///
/**
* Simple wrapper around fabric's RenderAccess.INSTANCE.getRenderer() that throws a slightly more informative error if one is not
* present. Note that NullPointerException is not a checked exception.
*/
@NotNull Renderer getFabricRenderer() throws NullPointerException;
interface TweakableUnbakedModel extends UnbakedModel {
TweakableUnbakedModel disableAo();
TweakableUnbakedModel itemModelState(BlockState state);
}
}

View File

@ -1,6 +1,6 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient; import io.github.cottonmc.templates.api.TemplatesClientApi;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
@ -16,7 +16,7 @@ import java.util.Map;
public class MeshTransformUtil { public class MeshTransformUtil {
public static Mesh pretransformMesh(Mesh mesh, RenderContext.QuadTransform transform) { public static Mesh pretransformMesh(Mesh mesh, RenderContext.QuadTransform transform) {
MeshBuilder builder = TemplatesClient.getFabricRenderer().meshBuilder(); MeshBuilder builder = TemplatesClientApi.getInstance().getFabricRenderer().meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
mesh.forEach(quad -> { mesh.forEach(quad -> {

View File

@ -1,6 +1,6 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient; import io.github.cottonmc.templates.api.TemplatesClientApi;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
@ -20,7 +20,7 @@ public class SlopeBaseMesh {
public static final int TAG_BOTTOM = Direction.DOWN.ordinal() + 1; public static final int TAG_BOTTOM = Direction.DOWN.ordinal() + 1;
public static Mesh makeUpright() { public static Mesh makeUpright() {
Renderer renderer = TemplatesClient.getFabricRenderer(); Renderer renderer = TemplatesClientApi.getInstance().getFabricRenderer();
MeshBuilder builder = renderer.meshBuilder(); MeshBuilder builder = renderer.meshBuilder();
QuadEmitter qu = builder.getEmitter(); QuadEmitter qu = builder.getEmitter();
qu.tag(TAG_SLOPE) qu.tag(TAG_SLOPE)
@ -63,7 +63,7 @@ public class SlopeBaseMesh {
//looks weird since i wrote a janky script to massage a .bbmodel, some manual fixups applied //looks weird since i wrote a janky script to massage a .bbmodel, some manual fixups applied
public static Mesh makeTinyUpright() { public static Mesh makeTinyUpright() {
Renderer renderer = TemplatesClient.getFabricRenderer(); Renderer renderer = TemplatesClientApi.getInstance().getFabricRenderer();
MeshBuilder builder = renderer.meshBuilder(); MeshBuilder builder = renderer.meshBuilder();
QuadEmitter qu = builder.getEmitter(); QuadEmitter qu = builder.getEmitter();
qu.tag(TAG_LEFT) qu.tag(TAG_LEFT)

View File

@ -5,6 +5,7 @@ import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
//TODO: move to the api package
public interface TemplateAppearance { public interface TemplateAppearance {
@NotNull Sprite getParticleSprite(); //TODO: plug this in (particle mixins don't use it atm) @NotNull Sprite getParticleSprite(); //TODO: plug this in (particle mixins don't use it atm)

View File

@ -1,6 +1,7 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient; import io.github.cottonmc.templates.api.TemplatesClientApi;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
@ -30,9 +31,10 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
//TODO: extract an API for the api package
public class TemplateAppearanceManager { public class TemplateAppearanceManager {
public TemplateAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) { public TemplateAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
MaterialFinder finder = TemplatesClient.getFabricRenderer().materialFinder(); MaterialFinder finder = TemplatesClientApi.getInstance().getFabricRenderer().materialFinder();
for(BlendMode blend : BlendMode.values()) { for(BlendMode blend : BlendMode.values()) {
finder.clear().disableDiffuse(false).blendMode(blend); finder.clear().disableDiffuse(false).blendMode(blend);
@ -86,8 +88,9 @@ public class TemplateAppearanceManager {
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state); BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
//Only for parsing vanilla quads: //Only for parsing vanilla quads:
QuadEmitter emitter = TemplatesClient.getFabricRenderer().meshBuilder().getEmitter(); Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
RenderMaterial defaultMat = TemplatesClient.getFabricRenderer().materialFinder().clear().find(); QuadEmitter emitter = r.meshBuilder().getEmitter();
RenderMaterial defaultMat = r.materialFinder().clear().find();
Sprite[] sprites = new Sprite[7]; Sprite[] sprites = new Sprite[7];
int[] bakeFlags = new int[6]; int[] bakeFlags = new int[6];

View File

@ -1,6 +1,6 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient; import io.github.cottonmc.templates.api.TemplatesClientApi;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
@ -26,25 +26,31 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Function; import java.util.function.Function;
public class UnbakedAutoRetexturedModel implements UnbakedModel { public class UnbakedAutoRetexturedModel implements UnbakedModel, TemplatesClientApi.TweakableUnbakedModel {
public UnbakedAutoRetexturedModel(Identifier parent) { public UnbakedAutoRetexturedModel(Identifier parent) {
this(parent, Blocks.AIR.getDefaultState());
}
public UnbakedAutoRetexturedModel(Identifier parent, BlockState itemModelState) {
this.parent = parent; this.parent = parent;
this.itemModelState = itemModelState;
} }
protected final Identifier parent; protected final Identifier parent;
protected final BlockState itemModelState; protected BlockState itemModelState = Blocks.AIR.getDefaultState();
protected boolean ao = true; protected boolean ao = true;
/// user configuration
@Override
public UnbakedAutoRetexturedModel disableAo() { public UnbakedAutoRetexturedModel disableAo() {
ao = false; ao = false;
return this; return this;
} }
@Override
public TemplatesClientApi.TweakableUnbakedModel itemModelState(BlockState state) {
this.itemModelState = state;
return this;
}
/// actual unbakedmodel stuff
@Override @Override
public Collection<Identifier> getModelDependencies() { public Collection<Identifier> getModelDependencies() {
return Collections.singletonList(parent); return Collections.singletonList(parent);
@ -58,15 +64,15 @@ public class UnbakedAutoRetexturedModel implements UnbakedModel {
@Nullable @Nullable
@Override @Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) { public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
return new RetexturingBakedModel( return new RetexturingBakedModel(
baker.bake(parent, modelBakeSettings), baker.bake(parent, modelBakeSettings),
TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup), TemplatesClientApi.getInstance().getOrCreateTemplateApperanceManager(spriteLookup),
modelBakeSettings, modelBakeSettings,
itemModelState, itemModelState,
ao ao
) { ) {
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
@Override @Override
protected Mesh getBaseMesh(BlockState state) { protected Mesh getBaseMesh(BlockState state) {
//Convert models to retexturable Meshes lazily, the first time we encounter each blockstate //Convert models to retexturable Meshes lazily, the first time we encounter each blockstate
@ -74,7 +80,7 @@ public class UnbakedAutoRetexturedModel implements UnbakedModel {
} }
private Mesh convertModel(BlockState state) { private Mesh convertModel(BlockState state) {
Renderer r = TemplatesClient.getFabricRenderer(); Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
MeshBuilder builder = r.meshBuilder(); MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
RenderMaterial mat = tam.getCachedMaterial(state, false); RenderMaterial mat = tam.getCachedMaterial(state, false);
@ -94,4 +100,11 @@ public class UnbakedAutoRetexturedModel implements UnbakedModel {
} }
}; };
} }
//ABI compat.
@Deprecated(forRemoval = true) //2.2 - use TemplatesClientApi.getInstance().auto, and use the builder to set this field
public UnbakedAutoRetexturedModel(Identifier parent, BlockState itemModelState) {
this(parent);
itemModelState(itemModelState);
}
} }

View File

@ -1,14 +1,13 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.Templates; import io.github.cottonmc.templates.Templates;
import io.github.cottonmc.templates.TemplatesClient; import io.github.cottonmc.templates.api.TemplatesClientApi;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.Baker; import net.minecraft.client.render.model.Baker;
@ -29,25 +28,31 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Function; import java.util.function.Function;
public class UnbakedJsonRetexturedModel implements UnbakedModel { public class UnbakedJsonRetexturedModel implements UnbakedModel, TemplatesClientApi.TweakableUnbakedModel {
public UnbakedJsonRetexturedModel(Identifier parent) { public UnbakedJsonRetexturedModel(Identifier parent) {
this(parent, Blocks.AIR.getDefaultState());
}
public UnbakedJsonRetexturedModel(Identifier parent, BlockState itemModelState) {
this.parent = parent; this.parent = parent;
this.itemModelState = itemModelState;
} }
protected final Identifier parent; protected final Identifier parent;
protected final BlockState itemModelState; protected BlockState itemModelState;
protected boolean ao = true; protected boolean ao = true;
/// user configuration
@Override
public UnbakedJsonRetexturedModel disableAo() { public UnbakedJsonRetexturedModel disableAo() {
ao = false; ao = false;
return this; return this;
} }
@Override
public TemplatesClientApi.TweakableUnbakedModel itemModelState(BlockState state) {
this.itemModelState = state;
return this;
}
/// actual unbakedmodel stuff
@Override @Override
public Collection<Identifier> getModelDependencies() { public Collection<Identifier> getModelDependencies() {
return Collections.singletonList(parent); return Collections.singletonList(parent);
@ -69,15 +74,15 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !"); specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
} }
ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
return new RetexturingBakedModel( return new RetexturingBakedModel(
baker.bake(parent, modelBakeSettings), baker.bake(parent, modelBakeSettings),
TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup), TemplatesClientApi.getInstance().getOrCreateTemplateApperanceManager(spriteLookup),
modelBakeSettings, modelBakeSettings,
itemModelState, itemModelState,
ao ao
) { ) {
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
@Override @Override
protected Mesh getBaseMesh(BlockState state) { protected Mesh getBaseMesh(BlockState state) {
//Convert models to retexturable Meshes lazily, the first time we encounter each blockstate //Convert models to retexturable Meshes lazily, the first time we encounter each blockstate
@ -85,7 +90,7 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
} }
private Mesh convertModel(BlockState state) { private Mesh convertModel(BlockState state) {
Renderer r = TemplatesClient.getFabricRenderer(); Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
MeshBuilder builder = r.meshBuilder(); MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
RenderMaterial mat = tam.getCachedMaterial(state, false); RenderMaterial mat = tam.getCachedMaterial(state, false);
@ -113,4 +118,11 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
} }
}; };
} }
//ABI compat
@Deprecated(forRemoval = true) //2.2 - use TemplatesClientApi.getInstance().json, and use the builder to set this field
public UnbakedJsonRetexturedModel(Identifier parent, BlockState itemModelState) {
this(parent);
itemModelState(itemModelState);
}
} }

View File

@ -1,6 +1,6 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import io.github.cottonmc.templates.TemplatesClient; import io.github.cottonmc.templates.api.TemplatesClientApi;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
@ -17,11 +17,7 @@ import java.util.Collections;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public class UnbakedMeshRetexturedModel implements UnbakedModel { public class UnbakedMeshRetexturedModel implements UnbakedModel, TemplatesClientApi.TweakableUnbakedModel {
public UnbakedMeshRetexturedModel(Identifier parent, Supplier<Mesh> baseMeshFactory) {
this(parent, __ -> baseMeshFactory.get());
}
public UnbakedMeshRetexturedModel(Identifier parent, Function<Function<SpriteIdentifier, Sprite>, Mesh> baseMeshFactory) { public UnbakedMeshRetexturedModel(Identifier parent, Function<Function<SpriteIdentifier, Sprite>, Mesh> baseMeshFactory) {
this.parent = parent; this.parent = parent;
this.baseMeshFactory = baseMeshFactory; this.baseMeshFactory = baseMeshFactory;
@ -29,13 +25,23 @@ public class UnbakedMeshRetexturedModel implements UnbakedModel {
protected final Identifier parent; protected final Identifier parent;
protected final Function<Function<SpriteIdentifier, Sprite>, Mesh> baseMeshFactory; protected final Function<Function<SpriteIdentifier, Sprite>, Mesh> baseMeshFactory;
protected boolean ao = true; protected boolean ao = true;
/// user configuration
@Override
public UnbakedMeshRetexturedModel disableAo() { public UnbakedMeshRetexturedModel disableAo() {
ao = false; ao = false;
return this; return this;
} }
@Override
public TemplatesClientApi.TweakableUnbakedModel itemModelState(BlockState state) {
throw new UnsupportedOperationException("UnbakedMeshRetexturedModel does not need an item model state set; it uses meshes");
}
/// actual unbakedmodel stuff
@Override @Override
public Collection<Identifier> getModelDependencies() { public Collection<Identifier> getModelDependencies() {
return Collections.singletonList(parent); return Collections.singletonList(parent);
@ -52,7 +58,7 @@ public class UnbakedMeshRetexturedModel implements UnbakedModel {
return new RetexturingBakedModel( return new RetexturingBakedModel(
baker.bake(parent, modelBakeSettings), baker.bake(parent, modelBakeSettings),
TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup), TemplatesClientApi.getInstance().getOrCreateTemplateApperanceManager(spriteLookup),
modelBakeSettings, modelBakeSettings,
Blocks.AIR.getDefaultState(), Blocks.AIR.getDefaultState(),
ao ao
@ -63,4 +69,10 @@ public class UnbakedMeshRetexturedModel implements UnbakedModel {
} }
}; };
} }
//ABI compat
@Deprecated(forRemoval = true) //2.2 - use TemplatesClientApi.getInstance().mesh
public UnbakedMeshRetexturedModel(Identifier parent, Supplier<Mesh> baseMeshFactory) {
this(parent, __ -> baseMeshFactory.get());
}
} }