From bbc107de96a438496ae85223ca1b2a3d8478228f Mon Sep 17 00:00:00 2001 From: Fry Date: Thu, 19 Jan 2017 13:57:37 +0400 Subject: [PATCH] Added the variant name to the missing model. (#3328) --- .../client/model/FancyMissingModel.java | 223 ++++++++++++++++++ .../client/model/ModelLoader.java | 5 +- .../client/model/ModelLoaderRegistry.java | 12 +- .../client/model/SimpleModelFontRenderer.java | 179 ++++++++++++++ .../common/ForgeModContainer.java | 8 + .../resources/assets/forge/lang/en_US.lang | 3 +- 6 files changed, 425 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/minecraftforge/client/model/FancyMissingModel.java create mode 100644 src/main/java/net/minecraftforge/client/model/SimpleModelFontRenderer.java diff --git a/src/main/java/net/minecraftforge/client/model/FancyMissingModel.java b/src/main/java/net/minecraftforge/client/model/FancyMissingModel.java new file mode 100644 index 0000000000..435d551277 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/FancyMissingModel.java @@ -0,0 +1,223 @@ +package net.minecraftforge.client.model; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemOverrideList; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.ForgeModContainer; +import net.minecraftforge.common.model.IModelState; +import net.minecraftforge.common.model.TRSRTransformation; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import javax.vecmath.Matrix4f; +import javax.vecmath.Quat4f; +import javax.vecmath.Vector3f; +import java.util.Collection; +import java.util.List; + +final class FancyMissingModel implements IModel +{ + private static final ResourceLocation font = new ResourceLocation("minecraft", "textures/font/ascii.png"); + private static final ResourceLocation font2 = new ResourceLocation("minecraft", "font/ascii"); + private static final TRSRTransformation smallTransformation = TRSRTransformation.blockCenterToCorner(new TRSRTransformation(null, null, new Vector3f(.25f, .25f, .25f), null)); + private static final LoadingCache fontCache = CacheBuilder.newBuilder().maximumSize(3).build(new CacheLoader() + { + public SimpleModelFontRenderer load(VertexFormat format) throws Exception + { + Matrix4f m = new Matrix4f(); + m.m20 = 1f / 128f; + m.m01 = m.m12 = -m.m20; + m.m33 = 1; + m.setTranslation(new Vector3f(1, 1 + 1f / 0x100, 0)); + return new SimpleModelFontRenderer( + Minecraft.getMinecraft().gameSettings, + font, + Minecraft.getMinecraft().getTextureManager(), + false, + m, + format + ) { + @Override + protected float renderUnicodeChar(char c, boolean italic) + { + return super.renderDefaultChar(126, italic); + } + }; + } + }); + + private final IModel missingModel; + private final String message; + + public FancyMissingModel(IModel missingModel, String message) + { + this.missingModel = missingModel; + this.message = message; + } + + @Override + public Collection getDependencies() + { + return ImmutableList.of(); + } + + @Override + public Collection getTextures() + { + return ImmutableList.of(font2); + } + + @Override + public IBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + IBakedModel bigMissing = missingModel.bake(state, format, bakedTextureGetter); + IModelState smallState = new ModelStateComposition(state, smallTransformation); + IBakedModel smallMissing = missingModel.bake(smallState, format, bakedTextureGetter); + return new BakedModel(bigMissing, smallMissing, fontCache.getUnchecked(format), message, bakedTextureGetter.apply(font2)); + } + + @Override + public IModelState getDefaultState() + { + return TRSRTransformation.identity(); + } + + private static final class BakedModel implements IPerspectiveAwareModel + { + private final SimpleModelFontRenderer fontRenderer; + private final String message; + private final TextureAtlasSprite fontTexture; + private final IBakedModel missingModel; + private final IBakedModel otherModel; + private final boolean big; + private ImmutableList quads; + + public BakedModel(IBakedModel bigMissing, IBakedModel smallMissing, SimpleModelFontRenderer fontRenderer, String message, TextureAtlasSprite fontTexture) + { + this.missingModel = bigMissing; + otherModel = new BakedModel(smallMissing, fontRenderer, message, fontTexture, this); + this.big = true; + this.fontRenderer = fontRenderer; + this.message = message; + this.fontTexture = fontTexture; + } + + public BakedModel(IBakedModel smallMissing, SimpleModelFontRenderer fontRenderer, String message, TextureAtlasSprite fontTexture, BakedModel big) + { + this.missingModel = smallMissing; + otherModel = big; + this.big = false; + this.fontRenderer = fontRenderer; + this.message = message; + this.fontTexture = fontTexture; + } + + @Override + public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) + { + if (side == null) + { + if (quads == null) + { + fontRenderer.setSprite(fontTexture); + fontRenderer.setFillBlanks(true); + String[] lines = message.split("\\r?\\n"); + List splitLines = Lists.newArrayList(); + for (int y = 0; y < lines.length; y++) + { + splitLines.addAll(fontRenderer.listFormattedStringToWidth(lines[y], 0x80)); + } + for (int y = 0; y < splitLines.size(); y++) + { + fontRenderer.drawString(splitLines.get(y), 0, (int)((y - splitLines.size() / 2f) * fontRenderer.FONT_HEIGHT) + 0x40, 0xFF00FFFF); + } + ImmutableList.Builder builder = ImmutableList.builder(); + builder.addAll(missingModel.getQuads(state, side, rand)); + builder.addAll(fontRenderer.build()); + quads = builder.build(); + } + return quads; + } + return missingModel.getQuads(state, side, rand); + } + + @Override + public boolean isAmbientOcclusion() { return true; } + + @Override + public boolean isGui3d() { return false; } + + @Override + public boolean isBuiltInRenderer() { return false; } + + @Override + public TextureAtlasSprite getParticleTexture() { return fontTexture; } + + @Override + public ItemCameraTransforms getItemCameraTransforms() { return ItemCameraTransforms.DEFAULT; } + + @Override + public ItemOverrideList getOverrides() { return ItemOverrideList.NONE; } + + @Override + public Pair handlePerspective(ItemCameraTransforms.TransformType cameraTransformType) + { + TRSRTransformation transform = TRSRTransformation.identity(); + boolean big = true; + switch (cameraTransformType) + { + + case THIRD_PERSON_LEFT_HAND: + break; + case THIRD_PERSON_RIGHT_HAND: + break; + case FIRST_PERSON_LEFT_HAND: + transform = new TRSRTransformation(new Vector3f(-0.62f, 0.5f, -.5f), new Quat4f(1, -1, -1, 1), null, null); + big = false; + break; + case FIRST_PERSON_RIGHT_HAND: + transform = new TRSRTransformation(new Vector3f(-0.5f, 0.5f, -.5f), new Quat4f(1, 1, 1, 1), null, null); + big = false; + break; + case HEAD: + break; + case GUI: + if (ForgeModContainer.zoomInMissingModelTextInGui) + { + transform = new TRSRTransformation(null, new Quat4f(1, 1, 1, 1), new Vector3f(4, 4, 4), null); + big = false; + } + else + { + transform = new TRSRTransformation(null, new Quat4f(1, 1, 1, 1), null, null); + big = true; + } + break; + case FIXED: + transform = new TRSRTransformation(null, new Quat4f(-1, -1, 1, 1), null, null); + break; + default: + break; + } + if (big != this.big) + { + return Pair.of(otherModel, transform.getMatrix()); + } + return Pair.of(this, transform.getMatrix()); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index 5a773e3037..ab33c3d813 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -262,7 +262,7 @@ public final class ModelLoader extends ModelBakery catch(Exception e) { storeException(location, e); - model = getMissingModel(); + model = ModelLoaderRegistry.getMissingModel(location, e); } stateModels.put(location, model); } @@ -348,11 +348,12 @@ public final class ModelLoader extends ModelBakery exception = new ItemLoadingException("Could not load item model either from the normal location " + file + " or from the blockstate", normalException, blockstateException); } } - stateModels.put(memory, model); if(exception != null) { storeException(memory, exception); + model = ModelLoaderRegistry.getMissingModel(memory, exception); } + stateModels.put(memory, model); } } ProgressManager.pop(itemBar); diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java index 5b147d1fd1..14ceec4dde 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java @@ -189,7 +189,7 @@ public class ModelLoaderRegistry } catch(Exception e) { - return getMissingModel(); + return getMissingModel(location, e); } } @@ -205,7 +205,7 @@ public class ModelLoaderRegistry catch(Exception e) { FMLLog.getLogger().error(error, e); - return getMissingModel(); + return getMissingModel(location, e); } } @@ -218,6 +218,14 @@ public class ModelLoaderRegistry return ModelLoader.VanillaLoader.INSTANCE.getLoader().getMissingModel(); } + static IModel getMissingModel(ResourceLocation location, Throwable cause) + { + //IModel model = new FancyMissingModel(ExceptionUtils.getStackTrace(cause).replaceAll("\\t", " ")); + IModel model = new FancyMissingModel(getMissingModel(), location.toString()); + textures.addAll(model.getTextures()); + return model; + } + public static void clearModelCache(IResourceManager manager) { ModelLoaderRegistry.manager = manager; diff --git a/src/main/java/net/minecraftforge/client/model/SimpleModelFontRenderer.java b/src/main/java/net/minecraftforge/client/model/SimpleModelFontRenderer.java new file mode 100644 index 0000000000..31e2c2dd5c --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/SimpleModelFontRenderer.java @@ -0,0 +1,179 @@ +package net.minecraftforge.client.model; + +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4f; +import javax.vecmath.Vector3f; +import javax.vecmath.Vector4f; + +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; + +import com.google.common.collect.ImmutableList; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; + +public abstract class SimpleModelFontRenderer extends FontRenderer { + + private float r, g, b, a; + private final Matrix4f matrix; + private ImmutableList.Builder builder = ImmutableList.builder(); + private final VertexFormat format; + private final Vector3f normal = new Vector3f(0, 0, 1); + private final EnumFacing orientation; + private boolean fillBlanks = false; + + private TextureAtlasSprite sprite; + + public SimpleModelFontRenderer(GameSettings settings, ResourceLocation font, TextureManager manager, boolean isUnicode, Matrix4f matrix, VertexFormat format) + { + super(settings, font, manager, isUnicode); + manager.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + this.matrix = new Matrix4f(matrix); + Matrix3f nm = new Matrix3f(); + this.matrix.getRotationScale(nm); + nm.invert(); + nm.transpose(); + this.format = format; + nm.transform(normal); + normal.normalize(); + orientation = EnumFacing.getFacingFromVector(normal.x, normal.y, normal.z); + } + + public void setSprite(TextureAtlasSprite sprite) + { + this.sprite = sprite; + super.onResourceManagerReload(null); + } + + public void setFillBlanks(boolean fillBlanks) + { + this.fillBlanks = fillBlanks; + } + + @Override + protected float renderDefaultChar(int pos, boolean italic) + { + float x = (pos % 16) / 16f; + float y = (pos / 16) / 16f; + float sh = italic ? 1f : 0f; + float w = charWidth[pos] - 1.01f; + float h = FONT_HEIGHT - 1.01f; + float wt = w / 128f; + float ht = h / 128f; + + UnpackedBakedQuad.Builder quadBuilder = new UnpackedBakedQuad.Builder(format); + quadBuilder.setQuadOrientation(orientation); + + addVertex(quadBuilder, posX + sh, posY, x, y); + addVertex(quadBuilder, posX - sh, posY + h, x, y + ht); + addVertex(quadBuilder, posX + w + sh, posY + h, x + wt, y + ht); + addVertex(quadBuilder, posX + w - sh, posY, x + wt, y); + builder.add(quadBuilder.build()); + + if(fillBlanks) + { + float cuv = 15f / 16f; + + quadBuilder = new UnpackedBakedQuad.Builder(format); + quadBuilder.setQuadOrientation(orientation); + + addVertex(quadBuilder, posX + w + sh, posY, cuv, cuv); + addVertex(quadBuilder, posX + w - sh, posY + h, cuv, cuv); + addVertex(quadBuilder, posX + charWidth[pos] + sh, posY + h, cuv, cuv); + addVertex(quadBuilder, posX + charWidth[pos] - sh, posY, cuv, cuv); + builder.add(quadBuilder.build()); + + quadBuilder = new UnpackedBakedQuad.Builder(format); + quadBuilder.setQuadOrientation(orientation); + + addVertex(quadBuilder, posX + sh, posY + h, cuv, cuv); + addVertex(quadBuilder, posX - sh, posY + FONT_HEIGHT, cuv, cuv); + addVertex(quadBuilder, posX + charWidth[pos] + sh, posY + FONT_HEIGHT, cuv, cuv); + addVertex(quadBuilder, posX + charWidth[pos] - sh, posY + h, cuv, cuv); + builder.add(quadBuilder.build()); + } + return charWidth[pos]; + } + + private final Vector4f vec = new Vector4f(); + + private void addVertex(UnpackedBakedQuad.Builder quadBuilder, float x, float y, float u, float v) + { + vec.x = x; + vec.y = y; + vec.z = 0; + vec.w = 1; + matrix.transform(vec); + for(int e = 0; e < format.getElementCount(); e++) + { + switch(format.getElement(e).getUsage()) + { + case POSITION: + quadBuilder.put(e, vec.x, vec.y, vec.z, vec.w); + break; + case UV: + quadBuilder.put(e, sprite.getInterpolatedU(u * 16), sprite.getInterpolatedV(v * 16), 0, 1); + break; + case COLOR: + quadBuilder.put(e, r, g, b, a); + break; + case NORMAL: + //quadBuilder.put(e, normal.x, normal.y, normal.z, 1); + quadBuilder.put(e, 0, 0, 1, 1); + break; + default: + quadBuilder.put(e); + break; + } + } + } + + @Override + public void onResourceManagerReload(IResourceManager resourceManager) + { + super.onResourceManagerReload(resourceManager); + String p = locationFontTexture.getResourcePath(); + if(p.startsWith("textures/")) p = p.substring("textures/".length(), p.length()); + if(p.endsWith(".png")) p = p.substring(0, p.length() - ".png".length()); + String f = locationFontTexture.getResourceDomain() + ":" + p; + sprite = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(f); + } + + @Override + protected abstract float renderUnicodeChar(char c, boolean italic); + + @Override + protected void doDraw(float shift) + { + posX += (int)shift; + } + + @Override + protected void setColor(float r, float g, float b, float a) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + @Override public void enableAlpha() + { + } + + public ImmutableList build() + { + ImmutableList ret = builder.build(); + builder = ImmutableList.builder(); + return ret; + } +} diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index 2f3b0820cc..257389dd69 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -112,6 +112,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC public static boolean disableVersionCheck = false; public static boolean forgeLightPipelineEnabled = true; public static boolean replaceVanillaBucketModel = true; + public static boolean zoomInMissingModelTextInGui = false; public static long java8Reminder = 0; public static boolean disableStairSlabCulling = false; // Also known as the "DontCullStairsBecauseIUseACrappyTexturePackThatBreaksBasicBlockShapesSoICantTrustBasicBlockCulling" flag public static boolean alwaysSetupTerrainOffThread = false; // In RenderGlobal.setupTerrain, always force the chunk render updates to be queued to the thread @@ -283,12 +284,19 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC // Client-Side only properties propOrder = new ArrayList(); + prop = config.get(Configuration.CATEGORY_CLIENT, "replaceVanillaBucketModel", Boolean.FALSE, "Replace the vanilla bucket models with Forges own dynamic bucket model. Unifies bucket visuals if a mod uses the Forge bucket model."); prop.setLanguageKey("forge.configgui.replaceBuckets").setRequiresMcRestart(true); replaceVanillaBucketModel = prop.getBoolean(Boolean.FALSE); propOrder.add(prop.getName()); + prop = config.get(Configuration.CATEGORY_CLIENT, "zoomInMissingModelTextInGui", Boolean.TRUE, + "Toggle off to make missing model text in the gui fit inside the slot."); + zoomInMissingModelTextInGui = prop.getBoolean(Boolean.FALSE); + prop.setLanguageKey("forge.configgui.zoomInMissingModelTextInGui"); + propOrder.add(prop.getName()); + prop = config.get(Configuration.CATEGORY_CLIENT, "java8Reminder", java8Reminder, "The timestamp of the last reminder to update to Java 8 in number of milliseconds since January 1, 1970, 00:00:00 GMT. Nag will show only once every 24 hours. To disable it set this to some really high number."); java8Reminder = prop.getLong(java8Reminder); diff --git a/src/main/resources/assets/forge/lang/en_US.lang b/src/main/resources/assets/forge/lang/en_US.lang index 1509ec681a..3b40d1ce89 100644 --- a/src/main/resources/assets/forge/lang/en_US.lang +++ b/src/main/resources/assets/forge/lang/en_US.lang @@ -49,6 +49,7 @@ forge.configgui.replaceBuckets=Use Forge's bucket model forge.configgui.forgeLightPipelineEnabled=Forge Light Pipeline Enabled forge.configgui.java8Reminder=Java 8 Reminder timestamp forge.configgui.disableStairSlabCulling=Disable Stair/Slab culling. +forge.configgui.zoomInMissingModelTextInGui=Zoom in Missing model text in the GUI forge.configgui.disableStairSlabCulling.tooltip=Enable this if you see through blocks touching stairs/slabs with your resource pack. forge.configgui.alwaysSetupTerrainOffThread=Force threaded chunk rendering forge.configgui.alwaysSetupTerrainOffThread.tooltip=Enable forge to queue all chunk updates to the Chunk Update thread. May increase FPS significantly, but may also cause weird rendering lag. Not recommended for computers without a significant number of cores available. @@ -186,4 +187,4 @@ fml.button.continue=Continue fml.button.open.mods.folder=Open Mods Folder fml.button.open.file=Open %s -forge.container.enchant.limitedEnchantability=Limited Enchantability \ No newline at end of file +forge.container.enchant.limitedEnchantability=Limited Enchantability