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.base.Function; 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.IRetexturableModel; 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.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; public class ModelConfigurableSides implements IBakedModel { private static final String MODEL_PREFIX = "confSides_"; 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.getResourcePath().contains(RESOURCE_LOCATION); } @Override public IModel loadModel(ResourceLocation modelLocation) throws IOException { String resourcePath = modelLocation.getResourcePath(); 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 IRetexturableModel { 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 IModelState getDefaultState() { return null; } @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(); } } }