blusunrize.immersiveengineering.client.models.ModelConfigurableSides.java Source code

Java tutorial

Introduction

Here is the source code for blusunrize.immersiveengineering.client.models.ModelConfigurableSides.java

Source

/*
 * BluSunrize
 * Copyright (c) 2017
 *
 * This code is licensed under "Blu's License of Common Sense"
 * Details can be found in the license file in the root folder of this project
 */

package blusunrize.immersiveengineering.client.models;

import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.ApiUtils;
import blusunrize.immersiveengineering.api.IEEnums;
import blusunrize.immersiveengineering.api.IEEnums.SideConfig;
import blusunrize.immersiveengineering.api.IEProperties;
import blusunrize.immersiveengineering.client.ClientUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.property.IExtendedBlockState;
import org.lwjgl.util.vector.Vector3f;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Function;

public class ModelConfigurableSides implements IBakedModel {
    private static final String MODEL_PREFIX = "conf_sides_";
    private static final String RESOURCE_LOCATION = "models/block/smartmodel/" + MODEL_PREFIX;
    //Holy shit, this type-chaining is messy. But I wanted to use lambdas!
    private static HashMap<String, ITextureNamer> TYPES = new HashMap();

    static {
        TYPES.put("all6_", new ITextureNamer() {
        });//every side seperately
        TYPES.put("s_", new ITextureNamer() {//all sides, same texture
            @Override
            public String nameFromSide(EnumFacing side, SideConfig cfg) {
                return "side";
            }
        });
        TYPES.put("hud_", new ITextureNamer() {//horizontal, up, down
            @Override
            public String nameFromSide(EnumFacing side, SideConfig cfg) {
                return side.ordinal() < 2 ? side.getName() : "side";
            }
        });
        TYPES.put("hv_", new ITextureNamer() {//horizontal, vertical
            @Override
            public String nameFromSide(EnumFacing side, SideConfig cfg) {
                return side.ordinal() < 2 ? "up" : "side";
            }
        });
        TYPES.put("ud_", new ITextureNamer() {//up, down, sides not configureable
            @Override
            public String nameFromSide(EnumFacing side, SideConfig cfg) {
                return side.ordinal() < 2 ? side.getName() : "side";
            }

            @Override
            public String nameFromCfg(EnumFacing side, SideConfig cfg) {
                return side.ordinal() < 2 ? cfg.getTextureName() : null;
            }
        });
        TYPES.put("v_", new ITextureNamer() {//vertical, sides not configureable
            @Override
            public String nameFromSide(EnumFacing side, SideConfig cfg) {
                return side.ordinal() < 2 ? "up" : "side";
            }

            @Override
            public String nameFromCfg(EnumFacing side, SideConfig cfg) {
                return side.ordinal() < 2 ? cfg.getTextureName() : null;
            }
        });
    }

    public static HashMap<String, List<BakedQuad>> modelCache = new HashMap<>();

    final String name;
    public TextureAtlasSprite[][] textures;

    public ModelConfigurableSides(String name, TextureAtlasSprite[][] textures) {
        this.name = name;
        this.textures = textures;
    }

    @Override
    public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) {
        TextureAtlasSprite[] tex = new TextureAtlasSprite[6];
        for (int i = 0; i < tex.length; i++)
            tex[i] = this.textures[i][0];
        char[] keyArray = "000000".toCharArray();
        if (state instanceof IExtendedBlockState) {
            IExtendedBlockState extended = (IExtendedBlockState) state;
            for (int i = 0; i < IEProperties.SIDECONFIG.length; i++)
                if (extended.getUnlistedNames().contains(IEProperties.SIDECONFIG[i])) {
                    IEEnums.SideConfig config = extended.getValue(IEProperties.SIDECONFIG[i]);
                    if (config != null) {
                        int c = config.ordinal();
                        tex[i] = this.textures[i][c];
                        keyArray[i] = Character.forDigit(c, 10);
                    }
                }
        }
        String key = name + String.copyValueOf(keyArray);
        if (!modelCache.containsKey(key))
            modelCache.put(key, bakeQuads(tex));
        return modelCache.get(key);
    }

    private static List<BakedQuad> bakeQuads(TextureAtlasSprite[] sprites) {
        List<BakedQuad> quads = Lists.newArrayListWithExpectedSize(6);
        float[] colour = { 1, 1, 1, 1 };
        Vector3f[] vertices = { new Vector3f(0, 0, 0), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1),
                new Vector3f(1, 0, 0) };
        quads.add(ClientUtils.createBakedQuad(DefaultVertexFormats.ITEM, vertices, EnumFacing.DOWN, sprites[0],
                new double[] { 0, 16, 16, 0 }, colour, true));
        vertices = new Vector3f[] { new Vector3f(0, 1, 0), new Vector3f(0, 1, 1), new Vector3f(1, 1, 1),
                new Vector3f(1, 1, 0) };
        quads.add(ClientUtils.createBakedQuad(DefaultVertexFormats.ITEM, vertices, EnumFacing.UP, sprites[1],
                new double[] { 0, 0, 16, 16 }, colour, false));

        vertices = new Vector3f[] { new Vector3f(1, 0, 0), new Vector3f(1, 1, 0), new Vector3f(0, 1, 0),
                new Vector3f(0, 0, 0) };
        quads.add(ClientUtils.createBakedQuad(DefaultVertexFormats.ITEM, vertices, EnumFacing.NORTH, sprites[2],
                new double[] { 0, 16, 16, 0 }, colour, true));
        vertices = new Vector3f[] { new Vector3f(1, 0, 1), new Vector3f(1, 1, 1), new Vector3f(0, 1, 1),
                new Vector3f(0, 0, 1) };
        quads.add(ClientUtils.createBakedQuad(DefaultVertexFormats.ITEM, vertices, EnumFacing.SOUTH, sprites[3],
                new double[] { 16, 16, 0, 0 }, colour, false));

        vertices = new Vector3f[] { new Vector3f(0, 0, 0), new Vector3f(0, 1, 0), new Vector3f(0, 1, 1),
                new Vector3f(0, 0, 1) };
        quads.add(ClientUtils.createBakedQuad(DefaultVertexFormats.ITEM, vertices, EnumFacing.WEST, sprites[4],
                new double[] { 0, 16, 16, 0 }, colour, true));
        vertices = new Vector3f[] { new Vector3f(1, 0, 0), new Vector3f(1, 1, 0), new Vector3f(1, 1, 1),
                new Vector3f(1, 0, 1) };
        quads.add(ClientUtils.createBakedQuad(DefaultVertexFormats.ITEM, vertices, EnumFacing.EAST, sprites[5],
                new double[] { 16, 16, 0, 0 }, colour, false));
        return quads;
    }

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

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

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

    @Override
    public TextureAtlasSprite getParticleTexture() {
        return this.textures[0][0];
    }

    static final ItemCameraTransforms defaultTransforms = new ItemCameraTransforms(
            new ItemTransformVec3f(new Vector3f(75, 45, 0), new Vector3f(0, .25f, 0),
                    new Vector3f(0.375f, 0.375f, 0.375f)), //thirdperson left
            new ItemTransformVec3f(new Vector3f(75, 45, 0), new Vector3f(0, .15625f, 0),
                    new Vector3f(0.375f, 0.375f, 0.375f)), //thirdperson left

            new ItemTransformVec3f(new Vector3f(0, 45, 0), new Vector3f(0, 0, 0), new Vector3f(.4f, .4f, .4f)), //firstperson left
            new ItemTransformVec3f(new Vector3f(0, 225, 0), new Vector3f(0, 0, 0), new Vector3f(.4f, .4f, .4f)), //firstperson right

            new ItemTransformVec3f(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 1, 1)), //head
            new ItemTransformVec3f(new Vector3f(30, 225, 0), new Vector3f(0, 0, 0),
                    new Vector3f(.625f, .625f, .625f)), //gui
            new ItemTransformVec3f(new Vector3f(0, 0, 0), new Vector3f(0, .1875f, 0),
                    new Vector3f(.25f, .25f, .25f)), //ground
            new ItemTransformVec3f(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(.5f, .5f, .5f))); //fixed

    @Override
    public ItemCameraTransforms getItemCameraTransforms() {
        return defaultTransforms;
    }

    @Override
    public ItemOverrideList getOverrides() {
        return ItemOverrideList.NONE;
    }

    public static class Loader implements ICustomModelLoader {
        @Override
        public void onResourceManagerReload(IResourceManager resourceManager) {
            modelCache.clear();
        }

        @Override
        public boolean accepts(ResourceLocation modelLocation) {
            return modelLocation.getPath().contains(RESOURCE_LOCATION);
        }

        @Override
        public IModel loadModel(ResourceLocation modelLocation) {
            String resourcePath = modelLocation.getPath();
            int pos = resourcePath.indexOf(MODEL_PREFIX);
            if (pos >= 0) {
                pos += MODEL_PREFIX.length();
                String sub = resourcePath.substring(pos);
                String name = sub;
                String type = null;
                ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
                for (Entry<String, ITextureNamer> e : TYPES.entrySet())
                    if (sub.startsWith(e.getKey())) {
                        type = e.getKey();
                        name = sub.substring(type.length());
                        for (EnumFacing f : EnumFacing.VALUES)
                            for (SideConfig cfg : SideConfig.values()) {
                                String key = f.getName() + "_" + cfg.getTextureName();
                                String tex = name + "_" + e.getValue().getTextureName(f, cfg);
                                builder.put(key, new ResourceLocation(ImmersiveEngineering.MODID, "blocks/" + tex));
                            }
                    }
                return new ConfigSidesModelBase(name, type, builder.build());
            }
            return ModelLoaderRegistry.getMissingModel();
        }
    }

    private static class ConfigSidesModelBase implements IModel {
        final String name;
        final String type;
        ImmutableMap<String, ResourceLocation> textures;

        public ConfigSidesModelBase(String name, String type, ImmutableMap<String, ResourceLocation> textures) {
            this.name = name;
            this.type = type;
            this.textures = textures;
        }

        @Override
        public Collection<ResourceLocation> getDependencies() {
            return ImmutableList.of();
        }

        @Override
        public Collection<ResourceLocation> getTextures() {
            return textures.values();
        }

        @Override
        public IBakedModel bake(IModelState state, VertexFormat format,
                Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) {
            TextureAtlasSprite[][] tex = new TextureAtlasSprite[6][3];
            for (EnumFacing f : EnumFacing.VALUES)
                for (SideConfig cfg : SideConfig.values()) {
                    ResourceLocation rl = textures.get(f.getName() + "_" + cfg.getTextureName());
                    if (rl != null)
                        tex[f.ordinal()][cfg.ordinal()] = ApiUtils
                                .getRegisterSprite(ClientUtils.mc().getTextureMapBlocks(), rl);
                }
            return new ModelConfigurableSides(name, tex);
        }

        @Override
        public IModel retexture(ImmutableMap<String, String> textures) {
            String newName = this.name;
            ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
            for (EnumFacing f : EnumFacing.VALUES)
                for (SideConfig cfg : SideConfig.values()) {
                    String key = f.getName() + "_" + cfg.getTextureName();
                    ResourceLocation rl = this.textures.get(key);
                    if (textures.containsKey(key))
                        rl = new ResourceLocation(textures.get(key));
                    else if (textures.containsKey(f.getName())) {
                        ITextureNamer namer = TYPES.get(type);
                        rl = new ResourceLocation(textures.get(f.getName()));
                        if (namer != null) {
                            String c = namer.nameFromCfg(f, cfg);
                            if (c != null)
                                rl = new ResourceLocation(textures.get(f.getName()) + "_" + c);
                        }
                    } else if (textures.containsKey("name")) {
                        ITextureNamer namer = TYPES.get(type);
                        newName = textures.get("name");
                        if (namer != null)
                            rl = new ResourceLocation(newName + "_" + namer.getTextureName(f, cfg));
                    }
                    builder.put(key, rl);
                }
            return new ConfigSidesModelBase(newName, type, builder.build());
        }
    }

    interface ITextureNamer {
        default String getTextureName(EnumFacing side, SideConfig cfg) {
            String s = nameFromSide(side, cfg);
            String c = nameFromCfg(side, cfg);
            if (s != null && c != null)
                return s + "_" + c;
            else if (s != null)
                return s;
            else if (c != null)
                return c;
            return "";
        }

        default String nameFromSide(EnumFacing side, SideConfig cfg) {
            return side.getName();
        }

        default String nameFromCfg(EnumFacing side, SideConfig cfg) {
            return cfg.getTextureName();
        }
    }
}