package slimeknights.tconstruct.library.client.model; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import gnu.trove.map.hash.TIntObjectHashMap; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.resources.IResourceManager; 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.TRSRTransformation; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.LoaderState; import org.apache.commons.io.FilenameUtils; import java.io.IOException; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import slimeknights.tconstruct.library.TinkerRegistry; import slimeknights.tconstruct.library.client.CustomTextureCreator; import slimeknights.tconstruct.library.client.model.format.AmmoPosition; import slimeknights.tconstruct.library.client.model.format.ToolModelOverride; import slimeknights.tconstruct.library.tools.IToolPart; import slimeknights.tconstruct.library.tools.ToolCore; public class ToolModelLoader implements ICustomModelLoader { public static String EXTENSION = ".tcon"; // used to create only actually needed textures in the texturegenerator instead of ALL materials for all parts private static final Map<ResourceLocation, ToolCore> modelItemMap = Maps.newHashMap(); public static void addPartMapping(ResourceLocation resourceLocation, ToolCore tool) { modelItemMap.put(resourceLocation, tool); } @Override public boolean accepts(ResourceLocation modelLocation) { return modelLocation.getResourcePath().endsWith(EXTENSION); // tinkertoolmodel extension. Foo.tcon.json } @Override public IModel loadModel(ResourceLocation modelLocation) { if(!Loader.instance().hasReachedState(LoaderState.POSTINITIALIZATION)) { return DummyModel.INSTANCE; } try { // Modelblock is used since our format is compatible to the vanilla format // and we don't have to write our own json deserializer // it also provides us with the textures Map<String, String> textures = ModelHelper.loadTexturesFromJson(modelLocation); ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms = ModelHelper.loadTransformFromJson(modelLocation); ImmutableList<ToolModelOverride> overrides = ModelHelper.loadToolModelOverridesFromJson(modelLocation); AmmoPosition ammoPosition = ModelHelper.loadAmmoPositionFromJson(modelLocation); Float[] rotations = ModelHelper.loadLayerRotations(modelLocation); if(rotations.length > 0 && textures.size() != rotations.length) { TinkerRegistry.log.error("Toolmodel {} has invalid layerrotation entry: Size should be {} but is {}; Skipping rotations.", modelLocation, textures.size(), rotations.length); rotations = new Float[0]; } ImmutableList.Builder<ResourceLocation> defaultTextureListBuilder = ImmutableList.builder(); List<MaterialModel> parts = Lists.newArrayList(); List<MaterialModel> brokenParts = Lists.newArrayList(); ToolCore toolCore = modelItemMap.get(MaterialModelLoader.getReducedPath(modelLocation)); for(Map.Entry<String, String> entry : textures.entrySet()) { String name = entry.getKey(); try { int i; List<MaterialModel> listToAdd; if(name.startsWith("layer")) { i = Integer.valueOf(name.substring(5)); listToAdd = parts; } else if(name.startsWith("broken")) { i = Integer.valueOf(name.substring(6)); listToAdd = brokenParts; } // invalid entry, ignore else { TinkerRegistry.log.warn("Toolmodel {} has invalid texture entry {}; Skipping layer.", modelLocation, name); continue; } ResourceLocation location = new ResourceLocation(entry.getValue()); MaterialModel partModel = new MaterialModel(ImmutableList.of(location)); while(listToAdd.size() <= i) { listToAdd.add(null); } listToAdd.set(i, partModel); defaultTextureListBuilder.add(location); registerCustomTextures(i, location, toolCore); } catch(NumberFormatException e) { TinkerRegistry.log.error("Toolmodel {} has invalid texture entry {}; Skipping layer.", modelLocation, name); } } // create overrides for(ToolModelOverride override : overrides) { for(Map.Entry<String, String> entry : override.textures.entrySet()) { String name = entry.getKey(); try { int i; TIntObjectHashMap<MaterialModel> mapToAdd; if(name.startsWith("layer")) { i = Integer.valueOf(name.substring(5)); mapToAdd = override.partModelReplacement; } else if(name.startsWith("broken")) { i = Integer.valueOf(name.substring(6)); mapToAdd = override.brokenPartModelReplacement; } // invalid entry, ignore else { TinkerRegistry.log.warn("Toolmodel {} has invalid texture override entry {}; Skipping layer.", modelLocation, name); continue; } ResourceLocation location = new ResourceLocation(entry.getValue()); MaterialModel partModel = new MaterialModel(ImmutableList.of(location)); mapToAdd.put(i, partModel); registerCustomTextures(i, location, toolCore); } catch(NumberFormatException e) { TinkerRegistry.log.error("Toolmodel {} has invalid texture entry {}; Skipping layer.", modelLocation, name); } } } String toolName = FilenameUtils.getBaseName(modelLocation.getResourcePath()); IModel mods; ModifierModel modifiers = null; try { mods = ModelLoaderRegistry.getModel(ModifierModelLoader.getLocationForToolModifiers(toolName)); if(mods == null || !(mods instanceof ModifierModel)) { TinkerRegistry.log.trace( "Toolmodel {} does not have any modifiers associated with it. Be sure that the Tools internal name, the Toolmodels filename and the name used inside the Modifier Model Definition match!", modelLocation); } else { modifiers = (ModifierModel) mods; for(ToolModelOverride toolModelOverride : overrides) { if(toolModelOverride.modifierSuffix != null) { String modifierName = toolName + toolModelOverride.modifierSuffix; IModel extraModel = ModelLoaderRegistry.getModel(ModifierModelLoader.getLocationForToolModifiers(modifierName)); if(extraModel instanceof ModifierModel) { ModifierModel overriddenModifierModel = new ModifierModel(); // fill in non-overridden modifiers for(Map.Entry<String, String> entry : modifiers.getModels().entrySet()) { overriddenModifierModel.addModelForModifier(entry.getKey(), entry.getValue()); } // overwrite overridden modifiers for(Map.Entry<String, String> entry : ((ModifierModel) extraModel).getModels().entrySet()) { overriddenModifierModel.addModelForModifier(entry.getKey(), entry.getValue()); } toolModelOverride.overrideModifierModel = overriddenModifierModel; } } } } } catch(Exception e) { TinkerRegistry.log.error(e); modifiers = null; } return new ToolModel(defaultTextureListBuilder.build(), parts, brokenParts, rotations, modifiers, transforms, overrides, ammoPosition); } catch(IOException e) { TinkerRegistry.log.error("Could not load multimodel {}", modelLocation.toString()); } return ModelLoaderRegistry.getMissingModel(); } private void registerCustomTextures(int i, ResourceLocation resourceLocation, ToolCore toolCore) { if(toolCore == null) { CustomTextureCreator.registerTexture(resourceLocation); } else { for(IToolPart part : toolCore.getRequiredComponents().get(i).getPossibleParts()) { CustomTextureCreator.registerTextureForPart(resourceLocation, part); } } } @Override public void onResourceManagerReload(@Nonnull IResourceManager resourceManager) { } }