Baked model issue

unlisted ⁨3⁩ ⁨files⁩ 2022-07-29 09:27:56 UTC

ClientTools

Raw
package net.nicgamer.automatek.varia;

import com.mojang.blaze3d.vertex.VertexFormatElement;
import com.mojang.math.Transformation;
import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;

public class ClientTools {

    private static void putVertex(BakedQuadBuilder builder, Vector3f normal, Vector4f vector, float u, float v, TextureAtlasSprite sprite) {

        var elements = builder.getVertexFormat().getElements().asList();
        for (int j = 0; j < elements.size(); j++) {
            var e = elements.get(j);
            switch (e.getUsage()) {
                case POSITION -> builder.put(j, vector.x(), vector.y(), vector.z(), 1.0f);
                case COLOR -> builder.put(j, 1.0f, 1.0f, 1.0f, 1.0f);
                case UV -> putVertexUV(builder, u, v, sprite, j, e);
                case NORMAL -> builder.put(j, normal.x(), normal.y(), normal.z());
                default -> builder.put(j);
            }
        }
    }

    private static void putVertexUV(BakedQuadBuilder builder, float u, float v, TextureAtlasSprite sprite, int j, VertexFormatElement e) {
        switch (e.getIndex()) {
            case 0 -> builder.put(j, sprite.getU(u), sprite.getV(v));
            case 2 -> builder.put(j, (short) 0, (short) 0);
                default -> builder.put(j);
        }
    }

    public static Vector3f v(float x, float y, float z) {
        return new Vector3f(x, y, z);
    }

    public static BakedQuad createQuad(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f v4, Transformation rotation, TextureAtlasSprite sprite) {
        Vector3f normal = v3.copy();
        normal.sub(v2);
        Vector3f temp = v1.copy();
        temp.sub(v2);
        normal.cross(temp);
        normal.normalize();

        int tw = sprite.getWidth();
        int th = sprite.getHeight();

        rotation = rotation.blockCenterToCorner();
        rotation.transformNormal(normal);

        Vector4f vv1 = new Vector4f(v1); rotation.transformPosition(vv1);
        Vector4f vv2 = new Vector4f(v2); rotation.transformPosition(vv2);
        Vector4f vv3 = new Vector4f(v3); rotation.transformPosition(vv3);
        Vector4f vv4 = new Vector4f(v4); rotation.transformPosition(vv4);

        var builder = new BakedQuadBuilder(sprite);
        builder.setQuadOrientation(Direction.getNearest(normal.x(), normal.y(), normal.z()));
        putVertex(builder, normal, vv1, 0, 0, sprite);
        putVertex(builder, normal, vv2, 0, th, sprite);
        putVertex(builder, normal, vv3, tw, th, sprite);
        putVertex(builder, normal, vv4, tw, 0, sprite);
        return builder.build();
    }
}

ConnectingBlockBakedModel

Raw
package net.nicgamer.automatek.utilities;

import com.mojang.math.Transformation;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.data.IDynamicBakedModel;
import net.minecraftforge.client.model.data.IModelData;
import net.nicgamer.automatek.blocks.ConnectingBlockModelLoader;
import net.nicgamer.automatek.blocks.ModelKey;
import net.nicgamer.automatek.varia.ClientTools;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Function;

import static java.lang.Boolean.TRUE;
import static net.nicgamer.automatek.varia.ClientTools.v;

public class ConnectingBlockBakedModel implements IDynamicBakedModel {

    private final ModelState modelState;
    private final Function<Material, TextureAtlasSprite> spriteGetter;
    private final Map<ModelKey, List<BakedQuad>> quadCache = new HashMap<>();
    private final ItemOverrides overrides;
    private final ItemTransforms itemTransforms;

    /**
     * @param modelState     represents the transformation (orientation) of our model. This is generated from the FACING property that our blockstate uses
     * @param spriteGetter   gives a way to convert materials to actual sprites on the main atlas
     * @param overrides      this is used for using this baked model when it is rendered in an inventory (as an item)
     * @param itemTransforms these represent the transforms to use for the item model
     */
    public ConnectingBlockBakedModel(ModelState modelState, Function<Material, TextureAtlasSprite> spriteGetter,
                                     ItemOverrides overrides, ItemTransforms itemTransforms) {
        this.modelState = modelState;
        this.spriteGetter = spriteGetter;
        this.overrides = overrides;
        this.itemTransforms = itemTransforms;
        generateQuadCache();
    }

    @Override
    public boolean usesBlockLight() {
        return false;
    }

    /**
     * Whenever a chunk where our block is in needs to be rerendered this method is called to return the quads (polygons)
     * for our model. Typically this will be called seven times: one time for every direction and one time in general.
     * If you have a block that is solid at one of the six sides it can be a good idea to render that face only for that
     * direction. That way Minecraft knows that it can get rid of that face when another solid block is adjacent to that.
     * All faces or quads that are generated for side == null are not going to be culled away like that
     *
     * @param state     the blockstate for our block
     * @param side      the six directions or null for quads that are not at a specific direction
     * @param rand      random generator that you can use to add variations to your model (usually for textures)
     * @param extraData this represents the data that is given to use from our block entity
     * @return a list of quads
     */
    @Nonnull
    @Override
    public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {

        // Are we on the solid render type and are we rendering for side == null
        RenderType layer = MinecraftForgeClient.getRenderType();
        if (side != null || (layer != null && !layer.equals(RenderType.solid()))) {
            return Collections.emptyList();
        }

        // Get the data from our block entity
        boolean northConnected = TRUE == extraData.getData(ConnectingBlockBE.NORTH_CONNECTED);
        boolean eastConnected = TRUE == extraData.getData(ConnectingBlockBE.EAST_CONNECTED);
        boolean southConnected = TRUE == extraData.getData(ConnectingBlockBE.SOUTH_CONNECTED);
        boolean westConnected = TRUE == extraData.getData(ConnectingBlockBE.WEST_CONNECTED);
        boolean upConnected = TRUE == extraData.getData(ConnectingBlockBE.UP_CONNECTED);
        boolean downConnected = TRUE == extraData.getData(ConnectingBlockBE.DOWN_CONNECTED);

        // ModelKey represents a unique configuration. We can use this to get our cached quads
        ModelKey key = new ModelKey(northConnected, eastConnected, southConnected, westConnected, upConnected, downConnected, modelState);

        var quads = new ArrayList<>(quadCache.get(key));
        return quads;
    }

    private void generateQuadCache() {
        boolean northConnected;
        boolean eastConnected;
        boolean southConnected;
        boolean westConnected;
        boolean upConnected;
        boolean downConnected;
        for (int i = 0; i < 2; i++) {
            northConnected = i != 0;
            for (int j = 0; j < 2; j++) {
                eastConnected = j != 0;
                for (int k = 0; k < 2; k++) {
                    southConnected = k != 0;
                    for (int l = 0; l < 2; l++) {
                        westConnected = l != 0;
                        for (int m = 0; m < 2; m++) {
                            upConnected = m != 0;
                            for (int n = 0; n < 2; n++) {
                                downConnected = n != 0;
                                quadCache.put(new ModelKey(northConnected, eastConnected, southConnected, westConnected, upConnected, downConnected, modelState), generateQuads(northConnected, eastConnected, southConnected, westConnected, upConnected, downConnected));
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Generate the quads for a given configuration. This is done in the constructor in order to populate
     * our quad cache.
     */
    @NotNull
    private List<BakedQuad> generateQuads(boolean northConnected, boolean eastConnected, boolean southConnected, boolean westConnected, boolean upConnected, boolean downConnected) {
        var quads = new ArrayList<BakedQuad>();
        float l = 0;
        float r = 1;

        Transformation rotation = modelState.getRotation();

        TextureAtlasSprite O = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_ZERO_CONNECTIONS);
        TextureAtlasSprite U = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_UP);
        TextureAtlasSprite R = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_RIGHT);
        TextureAtlasSprite D = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_DOWN);
        TextureAtlasSprite L = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_LEFT);
        TextureAtlasSprite LR = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_LR);
        TextureAtlasSprite DU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_UD);
        TextureAtlasSprite RU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_UR);
        TextureAtlasSprite DR = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_RD);
        TextureAtlasSprite DL = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_DL);
        TextureAtlasSprite LU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_LU);
        TextureAtlasSprite DRU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_URD);
        TextureAtlasSprite DLR = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_RDL);
        TextureAtlasSprite DLU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_DLU);
        TextureAtlasSprite LRU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_LUR);
        TextureAtlasSprite DLRU = spriteGetter.apply(ConnectingBlockModelLoader.MATERIAL_CONNECTING_BLOCK_FOUR_CONNECTION);

        blockMaterials.put("O", O);
        blockMaterials.put("D", D);
        blockMaterials.put("L", L);
        blockMaterials.put("R", R);
        blockMaterials.put("U", U);
        blockMaterials.put("DL", DL);
        blockMaterials.put("DR", DR);
        blockMaterials.put("DU", DU);
        blockMaterials.put("LR", LR);
        blockMaterials.put("LU", LU);
        blockMaterials.put("RU", RU);
        blockMaterials.put("DLR", DLR);
        blockMaterials.put("DLU", DLU);
        blockMaterials.put("DRU", DRU);
        blockMaterials.put("LRU", LRU);
        blockMaterials.put("DLRU", DLRU);

        var sideTextures = texturesForFaces(northConnected, eastConnected, southConnected, westConnected, upConnected, downConnected);

        // The base
        quads.add(ClientTools.createQuad(v(r, r, r), v(r, r, l), v(l, r, l), v(l, r, r), rotation, sideTextures[4]));      // Top
        quads.add(ClientTools.createQuad(v(l, l, l), v(r, l, l), v(r, l, r), v(l, l, r), rotation, sideTextures[5]));      // Bottom
        quads.add(ClientTools.createQuad(v(l, r, r), v(l, l, r), v(l, l, l), v(l, r, l), rotation, sideTextures[3]));      // West
        quads.add(ClientTools.createQuad(v(r, r, l), v(r, l, l), v(r, l, r), v(r, r, r), rotation, sideTextures[1]));      // East
        quads.add(ClientTools.createQuad(v(r, r, r), v(r, l, r), v(l, l, l), v(l, r, l), rotation, sideTextures[0]));      // North
        quads.add(ClientTools.createQuad(v(l, r, l), v(l, l, l), v(r, l, r), v(r, r, r), rotation, sideTextures[2]));      // South

        return quads;
    }

    @Override
    public boolean useAmbientOcclusion() {
        return true;
    }

    @Override
    public boolean isGui3d() {
        return false;
    }

    @Override
    public boolean isCustomRenderer() {
        return false;
    }

    @Override
    public TextureAtlasSprite getParticleIcon() {
        return blockMaterials.get("O");
    }

    @Override
    public ItemOverrides getOverrides() {
        return overrides;
    }

    @Override
    public ItemTransforms getTransforms() {
        return itemTransforms;
    }

    private final Map<String, TextureAtlasSprite> blockMaterials = new HashMap<>();

    private TextureAtlasSprite[] texturesForFaces(boolean northConnected, boolean eastConnected, boolean southConnected, boolean westConnected, boolean upConnected, boolean downConnected) {
        boolean[] connectedSidesBooleans = new boolean[]{northConnected, eastConnected, southConnected, westConnected, upConnected, downConnected};
        TextureAtlasSprite[] sprites = new TextureAtlasSprite[6];
        Character[][] connectedSides = new Character[6][4];   //[North, East, South, West, Up, Down] [U, R, D, L]
        int[] amountOccurrenceSide = new int[]{0, 0, 0, 0, 0, 0};
        char[][] mapper = new char[][]{{0, 'L', 0, 'R', 'U', 'D'}, {'R', 0, 'L', 0, 'U', 'D'}, {0, 'R', 0, 'L', 'U', 'D'}, {'L', 0, 'R', 0, 'U', 'D'}, {'U', 'R', 'D', 'L', 0, 0}, {'U', 'L', 'D', 'R', 0, 0}};
        int[] activeSide = new int[4];
        for (int i = 0; i < 6; i++) {
            switch (i) {
                case 4, 5 -> {
                    activeSide[0] = 0;
                    activeSide[1] = 1;
                    activeSide[2] = 2;
                    activeSide[3] = 3;
                }
                default -> {
                    activeSide[2] = 4;
                    activeSide[3] = 5;
                    switch (i) {
                        case 0, 2 -> {
                            activeSide[0] = 1;
                            activeSide[1] = 3;
                        }
                        case 1, 3 -> {
                            activeSide[0] = 0;
                            activeSide[1] = 2;
                        }
                    }
                }
            }
            if (connectedSidesBooleans[i]) {
                connectedSides[activeSide[0]][amountOccurrenceSide[activeSide[0]]] = mapper[activeSide[0]][i];
                connectedSides[activeSide[1]][amountOccurrenceSide[activeSide[1]]] = mapper[activeSide[1]][i];
                connectedSides[activeSide[2]][amountOccurrenceSide[activeSide[2]]] = mapper[activeSide[2]][i];
                connectedSides[activeSide[3]][amountOccurrenceSide[activeSide[3]]] = mapper[activeSide[3]][i];
            } else {
                connectedSides[activeSide[0]][amountOccurrenceSide[activeSide[0]]] = 0;
                connectedSides[activeSide[1]][amountOccurrenceSide[activeSide[1]]] = 0;
                connectedSides[activeSide[2]][amountOccurrenceSide[activeSide[2]]] = 0;
                connectedSides[activeSide[3]][amountOccurrenceSide[activeSide[3]]] = 0;
            }
            amountOccurrenceSide[activeSide[0]]++;
            amountOccurrenceSide[activeSide[1]]++;
            amountOccurrenceSide[activeSide[2]]++;
            amountOccurrenceSide[activeSide[3]]++;
        }
        String[] keys = new String[6];
        for (int j = 0; j < 6; j++) {
            String key = "";
            var character = Arrays.stream(connectedSides[j]).sorted().map(value -> value == 0 ? "" : value).toArray();
            for (int k = 0; k < 4; k++) {
                key = key.concat(character[k].toString());
            }
            keys[j] = key;
        }
        for (int l = 0; l < sprites.length; l++) {
            if (!keys[l].equals("")) {
                sprites[l] = blockMaterials.get(keys[l]);
            } else {
                sprites[l] = blockMaterials.get("O");
            }
        }
        return sprites;
    }
}

ConnectingBlockModelLoader

Raw
package net.nicgamer.automatek.blocks;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.IModelConfiguration;
import net.minecraftforge.client.model.IModelLoader;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.nicgamer.automatek.AutomatekMod;
import net.nicgamer.automatek.utilities.ConnectingBlockBakedModel;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

public class ConnectingBlockModelLoader implements IModelLoader<ConnectingBlockModelLoader.ConnectingBlockModelGeometry> {

    public static final ResourceLocation CONNECTING_BLOCK_LOADER = new ResourceLocation(AutomatekMod.MOD_ID, "connecting_block_loader");

    public static final ResourceLocation CONNECTING_BLOCK_ZERO_CONNECTIONS = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/zero_sides_connected");
    public static final ResourceLocation CONNECTING_BLOCK_ONE_CONNECTION_UP = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/one_side_connected_u");
    public static final ResourceLocation CONNECTING_BLOCK_ONE_CONNECTION_RIGHT = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/one_side_connected_r");
    public static final ResourceLocation CONNECTING_BLOCK_ONE_CONNECTION_LEFT = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/one_side_connected_l");
    public static final ResourceLocation CONNECTING_BLOCK_ONE_CONNECTION_DOWN = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/one_side_connected_d");
    public static final ResourceLocation CONNECTING_BLOCK_TWO_CURVE_CONNECTION_UR = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/two_sides_connected_curve_ur");
    public static final ResourceLocation CONNECTING_BLOCK_TWO_CURVE_CONNECTION_RD = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/two_sides_connected_curve_rd");
    public static final ResourceLocation CONNECTING_BLOCK_TWO_CURVE_CONNECTION_DL = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/two_sides_connected_curve_dl");
    public static final ResourceLocation CONNECTING_BLOCK_TWO_CURVE_CONNECTION_LU = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/two_sides_connected_curve_lu");
    public static final ResourceLocation CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_UD = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/two_sides_connected_straight_ud");
    public static final ResourceLocation CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_LR = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/two_sides_connected_straight_lr");
    public static final ResourceLocation CONNECTING_BLOCK_THREE_CONNECTION_URD = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/three_sides_connected_urd");
    public static final ResourceLocation CONNECTING_BLOCK_THREE_CONNECTION_RDL = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/three_sides_connected_rdl");
    public static final ResourceLocation CONNECTING_BLOCK_THREE_CONNECTION_DLU = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/three_sides_connected_dlu");
    public static final ResourceLocation CONNECTING_BLOCK_THREE_CONNECTION_LUR = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/three_sides_connected_lur");
    public static final ResourceLocation CONNECTING_BLOCK_FOUR_CONNECTION = new ResourceLocation(AutomatekMod.MOD_ID, "block/frames/transparent");

    public static final Material MATERIAL_CONNECTING_BLOCK_ZERO_CONNECTIONS = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_ZERO_CONNECTIONS);
    public static final Material MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_UP = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_ONE_CONNECTION_UP);
    public static final Material MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_RIGHT = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_ONE_CONNECTION_RIGHT);
    public static final Material MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_LEFT = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_ONE_CONNECTION_LEFT);
    public static final Material MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_DOWN = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_ONE_CONNECTION_DOWN);
    public static final Material MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_UR = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_TWO_CURVE_CONNECTION_UR);
    public static final Material MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_RD = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_TWO_CURVE_CONNECTION_RD);
    public static final Material MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_DL = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_TWO_CURVE_CONNECTION_DL);
    public static final Material MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_LU = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_TWO_CURVE_CONNECTION_LU);
    public static final Material MATERIAL_CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_UD = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_UD);
    public static final Material MATERIAL_CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_LR = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_LR);
    public static final Material MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_URD = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_THREE_CONNECTION_URD);
    public static final Material MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_RDL = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_THREE_CONNECTION_RDL);
    public static final Material MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_DLU = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_THREE_CONNECTION_DLU);
    public static final Material MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_LUR = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_THREE_CONNECTION_LUR);
    public static final Material MATERIAL_CONNECTING_BLOCK_FOUR_CONNECTION = ForgeHooksClient.getBlockMaterial(CONNECTING_BLOCK_FOUR_CONNECTION);

    @Override
    public void onResourceManagerReload(ResourceManager resourceManager) {
    }

    @Override
    public ConnectingBlockModelGeometry read(JsonDeserializationContext deserializationContext, JsonObject modelContents) {
        return new ConnectingBlockModelGeometry();
    }


    public static class ConnectingBlockModelGeometry implements IModelGeometry<ConnectingBlockModelGeometry> {

        @Override
        public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState modelTransform, ItemOverrides overrides, ResourceLocation modelLocation) {
            return new ConnectingBlockBakedModel(modelTransform, spriteGetter, overrides, owner.getCameraTransforms());
        }

        @Override
        public Collection<Material> getTextures(IModelConfiguration owner, Function<ResourceLocation, UnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors) {
            return List.of(MATERIAL_CONNECTING_BLOCK_ZERO_CONNECTIONS, MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_UP, MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_RIGHT, MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_LEFT, MATERIAL_CONNECTING_BLOCK_ONE_CONNECTION_DOWN,
                    MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_UR, MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_RD, MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_DL, MATERIAL_CONNECTING_BLOCK_TWO_CURVE_CONNECTION_LU,
                    MATERIAL_CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_UD, MATERIAL_CONNECTING_BLOCK_TWO_STRAIGHT_CONNECTION_LR, MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_URD, MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_RDL,
                    MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_DLU, MATERIAL_CONNECTING_BLOCK_THREE_CONNECTION_LUR, MATERIAL_CONNECTING_BLOCK_FOUR_CONNECTION);
        }
    }
}