base mod created

This commit is contained in:
Mohammad-Ali Minaie
2018-10-08 09:07:47 -04:00
parent 0a7700c356
commit b86dedad2f
7848 changed files with 584664 additions and 1 deletions

View File

@@ -0,0 +1,50 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.advancements.critereon;
import com.google.gson.JsonObject;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.util.ResourceLocation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class ItemPredicates
{
private static final Map<ResourceLocation, Function<JsonObject, ItemPredicate>> predicates = new HashMap<>();
static
{
register(new ResourceLocation("forge:ore_dict"), OredictItemPredicate::new);
}
public static void register(ResourceLocation rl, Function<JsonObject, ItemPredicate> jsonToPredicate)
{
predicates.put(rl, jsonToPredicate);
}
public static Map<ResourceLocation, Function<JsonObject, ItemPredicate>> getPredicates()
{
return Collections.unmodifiableMap(predicates);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.advancements.critereon;
import com.google.gson.JsonObject;
import net.minecraft.util.JsonUtils;
import org.apache.commons.lang3.ArrayUtils;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
/**
* An {@link ItemPredicate} that matches oredicts.
*/
public class OredictItemPredicate extends ItemPredicate
{
private final String ore;
public OredictItemPredicate(String ore)
{
this.ore = ore;
}
public OredictItemPredicate(JsonObject jsonObject) { this(JsonUtils.getString(jsonObject, "ore")); }
@Override
public boolean test(ItemStack stack)
{
return !stack.isEmpty() && ArrayUtils.contains(OreDictionary.getOreIDs(stack), OreDictionary.getOreID(ore));
}
}

View File

@@ -0,0 +1,65 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.classloading;
import java.io.File;
import java.util.Map;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import javax.annotation.Nullable;
public class FMLForgePlugin implements IFMLLoadingPlugin
{
public static boolean RUNTIME_DEOBF = false;
public static File forgeLocation;
@Override
public String[] getASMTransformerClass()
{
return new String[0];
}
@Override
public String getModContainerClass()
{
return "net.minecraftforge.common.ForgeModContainer";
}
@Override
@Nullable
public String getSetupClass()
{
return null;
}
@Override
public void injectData(Map<String, Object> data)
{
RUNTIME_DEOBF = (Boolean)data.get("runtimeDeobfuscationEnabled");
forgeLocation = (File)data.get("coremodLocation");
}
@Override
public String getAccessTransformerClass()
{
return null;
}
}

View File

@@ -0,0 +1,172 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiChat;
import net.minecraft.command.CommandException;
import net.minecraft.command.CommandHandler;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.CommandEvent;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.common.FMLLog;
import static net.minecraft.util.text.TextFormatting.*;
/**
* The class that handles client-side chat commands. You should register any
* commands that you want handled on the client with this command handler.
*
* If there is a command with the same name registered both on the server and
* client, the client takes precedence!
*
*/
public class ClientCommandHandler extends CommandHandler
{
public static final ClientCommandHandler instance = new ClientCommandHandler();
public String[] latestAutoComplete = null;
/**
* @return 1 if successfully executed, -1 if no permission or wrong usage,
* 0 if it doesn't exist or it was canceled (it's sent to the server)
*/
/**
* Attempt to execute a command. This method should return the number of times that the command was executed. If the
* command does not exist or if the player does not have permission, 0 will be returned. A number greater than 1 can
* be returned if a player selector is used.
*/
@Override
public int executeCommand(ICommandSender sender, String message)
{
message = message.trim();
boolean usedSlash = message.startsWith("/");
if (usedSlash)
{
message = message.substring(1);
}
String[] temp = message.split(" ");
String[] args = new String[temp.length - 1];
String commandName = temp[0];
System.arraycopy(temp, 1, args, 0, args.length);
ICommand icommand = getCommands().get(commandName);
try
{
if (icommand == null || (!usedSlash && icommand instanceof IClientCommand && !((IClientCommand)icommand).allowUsageWithoutPrefix(sender, message)))
{
return 0;
}
if (icommand.checkPermission(this.getServer(), sender))
{
CommandEvent event = new CommandEvent(icommand, sender, args);
if (MinecraftForge.EVENT_BUS.post(event))
{
if (event.getException() != null)
{
throw event.getException();
}
return 0;
}
this.tryExecute(sender, args, icommand, message);
return 1;
}
else
{
sender.sendMessage(format(RED, "commands.generic.permission"));
}
}
catch (WrongUsageException wue)
{
sender.sendMessage(format(RED, "commands.generic.usage", format(RED, wue.getMessage(), wue.getErrorObjects())));
}
catch (CommandException ce)
{
sender.sendMessage(format(RED, ce.getMessage(), ce.getErrorObjects()));
}
catch (Throwable t)
{
sender.sendMessage(format(RED, "commands.generic.exception"));
FMLLog.log.error("Command '{}' threw an exception:", message, t);
}
return -1;
}
//Couple of helpers because the mcp names are stupid and long...
private TextComponentTranslation format(TextFormatting color, String str, Object... args)
{
TextComponentTranslation ret = new TextComponentTranslation(str, args);
ret.getStyle().setColor(color);
return ret;
}
public void autoComplete(String leftOfCursor)
{
latestAutoComplete = null;
if (leftOfCursor.charAt(0) == '/')
{
leftOfCursor = leftOfCursor.substring(1);
Minecraft mc = FMLClientHandler.instance().getClient();
if (mc.currentScreen instanceof GuiChat)
{
List<String> commands = getTabCompletions(mc.player, leftOfCursor, mc.player.getPosition());
if (!commands.isEmpty())
{
if (leftOfCursor.indexOf(' ') == -1)
{
for (int i = 0; i < commands.size(); i++)
{
commands.set(i, GRAY + "/" + commands.get(i) + RESET);
}
}
else
{
for (int i = 0; i < commands.size(); i++)
{
commands.set(i, GRAY + commands.get(i) + RESET);
}
}
latestAutoComplete = commands.toArray(new String[commands.size()]);
}
}
}
}
@Override
protected MinecraftServer getServer() {
return Minecraft.getMinecraft().getIntegratedServer();
}
}

View File

@@ -0,0 +1,492 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.function.Predicate;
import net.minecraftforge.client.resource.IResourceType;
import net.minecraftforge.client.resource.VanillaResourceType;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.client.resource.ISelectiveResourceReloadListener;
public class CloudRenderer implements ISelectiveResourceReloadListener
{
// Shared constants.
private static final float PX_SIZE = 1 / 256F;
// Building constants.
private static final VertexFormat FORMAT = DefaultVertexFormats.POSITION_TEX_COLOR;
private static final int TOP_SECTIONS = 12; // Number of slices a top face will span.
private static final int HEIGHT = 4;
private static final float INSET = 0.001F;
private static final float ALPHA = 0.8F;
// Debug
private static final boolean WIREFRAME = false;
// Instance fields
private final Minecraft mc = Minecraft.getMinecraft();
private final ResourceLocation texture = new ResourceLocation("textures/environment/clouds.png");
private int displayList = -1;
private VertexBuffer vbo;
private int cloudMode = -1;
private int renderDistance = -1;
private DynamicTexture COLOR_TEX = null;
private int texW;
private int texH;
public CloudRenderer()
{
// Resource manager should always be reloadable.
((IReloadableResourceManager) mc.getResourceManager()).registerReloadListener(this);
}
private int getScale()
{
return cloudMode == 2 ? 12 : 8;
}
private float ceilToScale(float value)
{
float scale = getScale();
return MathHelper.ceil(value / scale) * scale;
}
private void vertices(BufferBuilder buffer)
{
boolean fancy = cloudMode == 2; // Defines whether to hide all but the bottom.
float scale = getScale();
float CULL_DIST = 2 * scale;
float bCol = fancy ? 0.7F : 1F;
float sectEnd = ceilToScale((renderDistance * 2) * 16);
float sectStart = -sectEnd;
float sectStep = ceilToScale(sectEnd * 2 / TOP_SECTIONS);
float sectPx = PX_SIZE / scale;
buffer.begin(GL11.GL_QUADS, FORMAT);
float sectX0 = sectStart;
float sectX1 = sectX0;
while (sectX1 < sectEnd)
{
sectX1 += sectStep;
if (sectX1 > sectEnd)
sectX1 = sectEnd;
float sectZ0 = sectStart;
float sectZ1 = sectZ0;
while (sectZ1 < sectEnd)
{
sectZ1 += sectStep;
if (sectZ1 > sectEnd)
sectZ1 = sectEnd;
float u0 = sectX0 * sectPx;
float u1 = sectX1 * sectPx;
float v0 = sectZ0 * sectPx;
float v1 = sectZ1 * sectPx;
// Bottom
buffer.pos(sectX0, 0, sectZ0).tex(u0, v0).color(bCol, bCol, bCol, ALPHA).endVertex();
buffer.pos(sectX1, 0, sectZ0).tex(u1, v0).color(bCol, bCol, bCol, ALPHA).endVertex();
buffer.pos(sectX1, 0, sectZ1).tex(u1, v1).color(bCol, bCol, bCol, ALPHA).endVertex();
buffer.pos(sectX0, 0, sectZ1).tex(u0, v1).color(bCol, bCol, bCol, ALPHA).endVertex();
if (fancy)
{
// Top
buffer.pos(sectX0, HEIGHT, sectZ0).tex(u0, v0).color(1, 1, 1, ALPHA).endVertex();
buffer.pos(sectX0, HEIGHT, sectZ1).tex(u0, v1).color(1, 1, 1, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, sectZ1).tex(u1, v1).color(1, 1, 1, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, sectZ0).tex(u1, v0).color(1, 1, 1, ALPHA).endVertex();
float slice;
float sliceCoord0;
float sliceCoord1;
for (slice = sectX0; slice < sectX1;)
{
sliceCoord0 = slice * sectPx;
sliceCoord1 = sliceCoord0 + PX_SIZE;
// X sides
if (slice > -CULL_DIST)
{
slice += INSET;
buffer.pos(slice, 0, sectZ1).tex(sliceCoord0, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ1).tex(sliceCoord1, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ0).tex(sliceCoord1, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, 0, sectZ0).tex(sliceCoord0, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
slice -= INSET;
}
slice += scale;
if (slice <= CULL_DIST)
{
slice -= INSET;
buffer.pos(slice, 0, sectZ0).tex(sliceCoord0, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ0).tex(sliceCoord1, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ1).tex(sliceCoord1, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, 0, sectZ1).tex(sliceCoord0, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
slice += INSET;
}
}
for (slice = sectZ0; slice < sectZ1;)
{
sliceCoord0 = slice * sectPx;
sliceCoord1 = sliceCoord0 + PX_SIZE;
// Z sides
if (slice > -CULL_DIST)
{
slice += INSET;
buffer.pos(sectX0, 0, slice).tex(u0, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX0, HEIGHT, slice).tex(u0, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, slice).tex(u1, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX1, 0, slice).tex(u1, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
slice -= INSET;
}
slice += scale;
if (slice <= CULL_DIST)
{
slice -= INSET;
buffer.pos(sectX1, 0, slice).tex(u1, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, slice).tex(u1, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX0, HEIGHT, slice).tex(u0, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX0, 0, slice).tex(u0, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
slice += INSET;
}
}
}
sectZ0 = sectZ1;
}
sectX0 = sectX1;
}
}
private void dispose()
{
if (vbo != null)
{
vbo.deleteGlBuffers();
vbo = null;
}
if (displayList >= 0)
{
GLAllocation.deleteDisplayLists(displayList);
displayList = -1;
}
}
private void build()
{
Tessellator tess = Tessellator.getInstance();
BufferBuilder buffer = tess.getBuffer();
if (OpenGlHelper.useVbo())
vbo = new VertexBuffer(FORMAT);
else
GlStateManager.glNewList(displayList = GLAllocation.generateDisplayLists(1), GL11.GL_COMPILE);
vertices(buffer);
if (OpenGlHelper.useVbo())
{
buffer.finishDrawing();
buffer.reset();
vbo.bufferData(buffer.getByteBuffer());
}
else
{
tess.draw();
GlStateManager.glEndList();
}
}
private int fullCoord(double coord, int scale)
{ // Corrects misalignment of UV offset when on negative coords.
return ((int) coord / scale) - (coord < 0 ? 1 : 0);
}
private boolean isBuilt()
{
return OpenGlHelper.useVbo() ? vbo != null : displayList >= 0;
}
public void checkSettings()
{
boolean newEnabled = ForgeModContainer.forgeCloudsEnabled
&& mc.gameSettings.shouldRenderClouds() != 0
&& mc.world != null
&& mc.world.provider.isSurfaceWorld();
if (isBuilt()
&& (!newEnabled
|| mc.gameSettings.shouldRenderClouds() != cloudMode
|| mc.gameSettings.renderDistanceChunks != renderDistance))
{
dispose();
}
cloudMode = mc.gameSettings.shouldRenderClouds();
renderDistance = mc.gameSettings.renderDistanceChunks;
if (newEnabled && !isBuilt())
{
build();
}
}
public boolean render(int cloudTicks, float partialTicks)
{
if (!isBuilt())
return false;
Entity entity = mc.getRenderViewEntity();
double totalOffset = cloudTicks + partialTicks;
double x = entity.prevPosX + (entity.posX - entity.prevPosX) * partialTicks
+ totalOffset * 0.03;
double y = mc.world.provider.getCloudHeight()
- (entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks)
+ 0.33;
double z = entity.prevPosZ + (entity.posZ - entity.prevPosZ) * partialTicks;
int scale = getScale();
if (cloudMode == 2)
z += 0.33 * scale;
// Integer UVs to translate the texture matrix by.
int offU = fullCoord(x, scale);
int offV = fullCoord(z, scale);
GlStateManager.pushMatrix();
// Translate by the remainder after the UV offset.
GlStateManager.translate((offU * scale) - x, y, (offV * scale) - z);
// Modulo to prevent texture samples becoming inaccurate at extreme offsets.
offU = offU % texW;
offV = offV % texH;
// Translate the texture.
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.translate(offU * PX_SIZE, offV * PX_SIZE, 0);
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(
GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA,
GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
// Color multiplier.
Vec3d color = mc.world.getCloudColour(partialTicks);
float r = (float) color.x;
float g = (float) color.y;
float b = (float) color.z;
if (mc.gameSettings.anaglyph)
{
float tempR = r * 0.3F + g * 0.59F + b * 0.11F;
float tempG = r * 0.3F + g * 0.7F;
float tempB = r * 0.3F + b * 0.7F;
r = tempR;
g = tempG;
b = tempB;
}
if (COLOR_TEX == null)
COLOR_TEX = new DynamicTexture(1, 1);
// Apply a color multiplier through a texture upload if shaders aren't supported.
COLOR_TEX.getTextureData()[0] = 255 << 24
| ((int) (r * 255)) << 16
| ((int) (g * 255)) << 8
| (int) (b * 255);
COLOR_TEX.updateDynamicTexture();
GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GlStateManager.bindTexture(COLOR_TEX.getGlTextureId());
GlStateManager.enableTexture2D();
// Bind the clouds texture last so the shader's sampler2D is correct.
GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit);
mc.renderEngine.bindTexture(texture);
ByteBuffer buffer = Tessellator.getInstance().getBuffer().getByteBuffer();
// Set up pointers for the display list/VBO.
if (OpenGlHelper.useVbo())
{
vbo.bindBuffer();
int stride = FORMAT.getNextOffset();
GlStateManager.glVertexPointer(3, GL11.GL_FLOAT, stride, 0);
GlStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GlStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, stride, 12);
GlStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GlStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, stride, 20);
GlStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
}
else
{
buffer.limit(FORMAT.getNextOffset());
for (int i = 0; i < FORMAT.getElementCount(); i++)
FORMAT.getElements().get(i).getUsage().preDraw(FORMAT, i, FORMAT.getNextOffset(), buffer);
buffer.position(0);
}
// Depth pass to prevent insides rendering from the outside.
GlStateManager.colorMask(false, false, false, false);
if (OpenGlHelper.useVbo())
vbo.drawArrays(GL11.GL_QUADS);
else
GlStateManager.callList(displayList);
// Full render.
if (!mc.gameSettings.anaglyph)
{
GlStateManager.colorMask(true, true, true, true);
}
else
{
switch (EntityRenderer.anaglyphField)
{
case 0:
GlStateManager.colorMask(false, true, true, true);
break;
case 1:
GlStateManager.colorMask(true, false, false, true);
break;
}
}
// Wireframe for debug.
if (WIREFRAME)
{
GlStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
GlStateManager.glLineWidth(2.0F);
GlStateManager.disableTexture2D();
GlStateManager.depthMask(false);
GlStateManager.disableFog();
if (OpenGlHelper.useVbo())
vbo.drawArrays(GL11.GL_QUADS);
else
GlStateManager.callList(displayList);
GlStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GlStateManager.depthMask(true);
GlStateManager.enableTexture2D();
GlStateManager.enableFog();
}
if (OpenGlHelper.useVbo())
{
vbo.drawArrays(GL11.GL_QUADS);
vbo.unbindBuffer(); // Unbind buffer and disable pointers.
}
else
{
GlStateManager.callList(displayList);
}
buffer.limit(0);
for (int i = 0; i < FORMAT.getElementCount(); i++)
FORMAT.getElements().get(i).getUsage().postDraw(FORMAT, i, FORMAT.getNextOffset(), buffer);
buffer.position(0);
// Disable our coloring.
GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GlStateManager.disableTexture2D();
GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit);
// Reset texture matrix.
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.disableBlend();
GlStateManager.enableCull();
GlStateManager.popMatrix();
return true;
}
private void reloadTextures()
{
if (mc.renderEngine != null)
{
mc.renderEngine.bindTexture(texture);
texW = GlStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
texH = GlStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT);
}
}
@Override
public void onResourceManagerReload(@Nonnull IResourceManager resourceManager, @Nonnull Predicate<IResourceType> resourcePredicate)
{
if (resourcePredicate.test(VanillaResourceType.TEXTURES))
{
reloadTextures();
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import net.minecraft.client.audio.MusicTicker;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.Util.EnumOS;
import net.minecraft.world.GameType;
import net.minecraft.client.settings.GameSettings.Options;
import net.minecraftforge.common.util.EnumHelper;
import javax.annotation.Nullable;
public class EnumHelperClient extends EnumHelper
{
private static Class<?>[][] clientTypes =
{
{GameType.class, int.class, String.class, String.class},
{Options.class, String.class, boolean.class, boolean.class},
{EnumOS.class},
{MusicTicker.MusicType.class, SoundEvent.class, int.class, int.class}
};
@Nullable
public static GameType addGameType(String name, int id, String displayName, String shortName)
{
return addEnum(GameType.class, name, id, displayName, shortName);
}
@Nullable
public static Options addOptions(String name, String langName, boolean isSlider, boolean isToggle)
{
return addEnum(Options.class, name, langName, isSlider, isToggle);
}
@Nullable
public static Options addOptions(String name, String langName, boolean isSlider, boolean isToggle, float valMin, float valMax, float valStep)
{
return addEnum(Options.class, name,
new Class<?>[]{String.class, boolean.class, boolean.class, float.class, float.class, float.class},
langName, isSlider, isToggle, valMin, valMax, valStep);
}
@Nullable
public static EnumOS addOS2(String name)
{
return addEnum(EnumOS.class, name);
}
@Nullable
public static MusicTicker.MusicType addMusicType(String name, SoundEvent sound, int minDelay, int maxDelay)
{
return addEnum(MusicTicker.MusicType.class, name, sound, minDelay, maxDelay);
}
@Nullable
private static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues)
{
return addEnum(clientTypes, enumType, enumName, paramValues);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import net.minecraft.client.renderer.color.IItemColor;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import javax.annotation.Nonnull;
public class FluidContainerColorer implements IItemColor
{
@Override
public int colorMultiplier(@Nonnull ItemStack stack, int tintIndex)
{
if (tintIndex != 1) return 0xFFFFFFFF;
FluidStack fluidStack = FluidUtil.getFluidContained(stack);
if (fluidStack == null) return 0xFFFFFFFF;
return fluidStack.getFluid().getColor(fluidStack);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class ForgeClientHandler
{
@SubscribeEvent
public static void registerModels(ModelRegistryEvent event)
{
// register model for the universal bucket, if it exists
if (FluidRegistry.isUniversalBucketEnabled())
{
ModelLoader.setBucketModelDefinition(ForgeModContainer.getInstance().universalBucket);
}
}
@SubscribeEvent
public static void registerItemHandlers(ColorHandlerEvent.Item event)
{
if (FluidRegistry.isUniversalBucketEnabled())
{
event.getItemColors().registerItemColorHandler(new FluidContainerColorer(), ForgeModContainer.getInstance().universalBucket);
}
}
}

View File

@@ -0,0 +1,784 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import static net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType.BOSSINFO;
import static net.minecraftforge.common.ForgeVersion.Status.BETA;
import static net.minecraftforge.common.ForgeVersion.Status.BETA_OUTDATED;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.vecmath.Matrix3f;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.gui.BossInfoClient;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.RenderItem;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.BlockFaceUV;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemTransformVec3f;
import net.minecraft.client.renderer.block.model.ModelManager;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.block.model.ModelRotation;
import net.minecraft.client.renderer.block.model.SimpleBakedModel;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.client.renderer.color.ItemColors;
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.tileentity.TileEntityRendererDispatcher;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage;
import net.minecraft.client.resources.FoliageColorReloadListener;
import net.minecraft.client.resources.GrassColorReloadListener;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.client.resources.LanguageManager;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.util.SearchTreeManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.passive.EntityHorse;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.MovementInput;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.registry.IRegistry;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.client.event.EntityViewRenderEvent;
import net.minecraftforge.client.event.FOVUpdateEvent;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.client.event.InputUpdateEvent;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.event.MouseEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.client.event.ScreenshotEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.client.event.sound.PlaySoundEvent;
import net.minecraftforge.client.model.ModelDynBucket;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.animation.Animation;
import net.minecraftforge.client.resource.IResourceType;
import net.minecraftforge.client.resource.SelectiveReloadStateHandler;
import net.minecraftforge.client.resource.VanillaResourceType;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.ForgeVersion.Status;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.ITransformation;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import java.util.function.Predicate;
import com.google.common.collect.Maps;
public class ForgeHooksClient
{
//private static final ResourceLocation ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png");
static TextureManager engine()
{
return FMLClientHandler.instance().getClient().renderEngine;
}
public static String getArmorTexture(Entity entity, ItemStack armor, String _default, EntityEquipmentSlot slot, String type)
{
String result = armor.getItem().getArmorTexture(armor, entity, slot, type);
return result != null ? result : _default;
}
//Optifine Helper Functions u.u, these are here specifically for Optifine
//Note: When using Optifine, these methods are invoked using reflection, which
//incurs a major performance penalty.
public static void orientBedCamera(IBlockAccess world, BlockPos pos, IBlockState state, Entity entity)
{
Block block = state.getBlock();
if (block != null && block.isBed(state, world, pos, entity))
{
GL11.glRotatef((float)(block.getBedDirection(state, world, pos).getHorizontalIndex() * 90), 0.0F, 1.0F, 0.0F);
}
}
public static boolean onDrawBlockHighlight(RenderGlobal context, EntityPlayer player, RayTraceResult target, int subID, float partialTicks)
{
return MinecraftForge.EVENT_BUS.post(new DrawBlockHighlightEvent(context, player, target, subID, partialTicks));
}
public static void dispatchRenderLast(RenderGlobal context, float partialTicks)
{
MinecraftForge.EVENT_BUS.post(new RenderWorldLastEvent(context, partialTicks));
}
public static boolean renderFirstPersonHand(RenderGlobal context, float partialTicks, int renderPass)
{
return MinecraftForge.EVENT_BUS.post(new RenderHandEvent(context, partialTicks, renderPass));
}
public static boolean renderSpecificFirstPersonHand(EnumHand hand, float partialTicks, float interpPitch, float swingProgress, float equipProgress, ItemStack stack)
{
return MinecraftForge.EVENT_BUS.post(new RenderSpecificHandEvent(hand, partialTicks, interpPitch, swingProgress, equipProgress, stack));
}
public static void onTextureStitchedPre(TextureMap map)
{
MinecraftForge.EVENT_BUS.post(new TextureStitchEvent.Pre(map));
ModelLoader.White.INSTANCE.register(map);
ModelDynBucket.LoaderDynBucket.INSTANCE.register(map);
}
public static void onTextureStitchedPost(TextureMap map)
{
MinecraftForge.EVENT_BUS.post(new TextureStitchEvent.Post(map));
}
public static void onBlockColorsInit(BlockColors blockColors)
{
MinecraftForge.EVENT_BUS.post(new ColorHandlerEvent.Block(blockColors));
}
public static void onItemColorsInit(ItemColors itemColors, BlockColors blockColors)
{
MinecraftForge.EVENT_BUS.post(new ColorHandlerEvent.Item(itemColors, blockColors));
}
static int renderPass = -1;
public static void setRenderPass(int pass)
{
renderPass = pass;
}
static final ThreadLocal<BlockRenderLayer> renderLayer = new ThreadLocal<BlockRenderLayer>();
public static void setRenderLayer(BlockRenderLayer layer)
{
renderLayer.set(layer);
}
public static ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, EntityEquipmentSlot slot, ModelBiped _default)
{
ModelBiped model = itemStack.getItem().getArmorModel(entityLiving, itemStack, slot, _default);
return model == null ? _default : model;
}
//This properly moves the domain, if provided, to the front of the string before concatenating
public static String fixDomain(String base, String complex)
{
int idx = complex.indexOf(':');
if (idx == -1)
{
return base + complex;
}
String name = complex.substring(idx + 1, complex.length());
if (idx > 1)
{
String domain = complex.substring(0, idx);
return domain + ':' + base + name;
}
else
{
return base + name;
}
}
public static boolean postMouseEvent()
{
return MinecraftForge.EVENT_BUS.post(new MouseEvent());
}
public static float getOffsetFOV(EntityPlayer entity, float fov)
{
FOVUpdateEvent fovUpdateEvent = new FOVUpdateEvent(entity, fov);
MinecraftForge.EVENT_BUS.post(fovUpdateEvent);
return fovUpdateEvent.getNewfov();
}
public static float getFOVModifier(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks, float fov) {
EntityViewRenderEvent.FOVModifier event = new EntityViewRenderEvent.FOVModifier(renderer, entity, state, renderPartialTicks, fov);
MinecraftForge.EVENT_BUS.post(event);
return event.getFOV();
}
private static int skyX, skyZ;
private static boolean skyInit;
private static int skyRGBMultiplier;
public static int getSkyBlendColour(World world, BlockPos center)
{
if (center.getX() == skyX && center.getZ() == skyZ && skyInit)
{
return skyRGBMultiplier;
}
skyInit = true;
GameSettings settings = Minecraft.getMinecraft().gameSettings;
int[] ranges = ForgeModContainer.blendRanges;
int distance = 0;
if (settings.fancyGraphics && ranges.length > 0)
{
distance = ranges[MathHelper.clamp(settings.renderDistanceChunks, 0, ranges.length-1)];
}
int r = 0;
int g = 0;
int b = 0;
int divider = 0;
for (int x = -distance; x <= distance; ++x)
{
for (int z = -distance; z <= distance; ++z)
{
BlockPos pos = center.add(x, 0, z);
Biome biome = world.getBiome(pos);
int colour = biome.getSkyColorByTemp(biome.getTemperature(pos));
r += (colour & 0xFF0000) >> 16;
g += (colour & 0x00FF00) >> 8;
b += colour & 0x0000FF;
divider++;
}
}
int multiplier = (r / divider & 255) << 16 | (g / divider & 255) << 8 | b / divider & 255;
skyX = center.getX();
skyZ = center.getZ();
skyRGBMultiplier = multiplier;
return skyRGBMultiplier;
}
/**
* Initialization of Forge Renderers.
*/
static
{
//FluidRegistry.renderIdFluid = RenderingRegistry.getNextAvailableRenderId();
//RenderingRegistry.registerBlockHandler(RenderBlockFluid.instance);
}
private static int updatescrollcounter = 0;
public static String renderMainMenu(GuiMainMenu gui, FontRenderer font, int width, int height, String splashText)
{
Status status = ForgeVersion.getStatus();
if (status == BETA || status == BETA_OUTDATED)
{
// render a warning at the top of the screen,
String line = I18n.format("forge.update.beta.1", TextFormatting.RED, TextFormatting.RESET);
gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (0 * (font.FONT_HEIGHT + 1)), -1);
line = I18n.format("forge.update.beta.2");
gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (1 * (font.FONT_HEIGHT + 1)), -1);
}
String line = null;
switch(status)
{
//case FAILED: line = " Version check failed"; break;
//case UP_TO_DATE: line = "Forge up to date"}; break;
//case AHEAD: line = "Using non-recommended Forge build, issues may arise."}; break;
case OUTDATED:
case BETA_OUTDATED: line = I18n.format("forge.update.newversion", ForgeVersion.getTarget()); break;
default: break;
}
if (line != null)
{
// if we have a line, render it in the bottom right, above Mojang's copyright line
gui.drawString(font, line, width - font.getStringWidth(line) - 2, height - (2 * (font.FONT_HEIGHT + 1)), -1);
}
return splashText;
}
public static ISound playSound(SoundManager manager, ISound sound)
{
PlaySoundEvent e = new PlaySoundEvent(manager, sound);
MinecraftForge.EVENT_BUS.post(e);
return e.getResultSound();
}
//static RenderBlocks VertexBufferRB;
static int worldRenderPass;
public static int getWorldRenderPass()
{
return worldRenderPass;
}
public static void drawScreen(GuiScreen screen, int mouseX, int mouseY, float partialTicks)
{
if (!MinecraftForge.EVENT_BUS.post(new GuiScreenEvent.DrawScreenEvent.Pre(screen, mouseX, mouseY, partialTicks)))
screen.drawScreen(mouseX, mouseY, partialTicks);
MinecraftForge.EVENT_BUS.post(new GuiScreenEvent.DrawScreenEvent.Post(screen, mouseX, mouseY, partialTicks));
}
public static float getFogDensity(EntityRenderer renderer, Entity entity, IBlockState state, float partial, float density)
{
EntityViewRenderEvent.FogDensity event = new EntityViewRenderEvent.FogDensity(renderer, entity, state, partial, density);
if (MinecraftForge.EVENT_BUS.post(event)) return event.getDensity();
return -1;
}
public static void onFogRender(EntityRenderer renderer, Entity entity, IBlockState state, float partial, int mode, float distance)
{
MinecraftForge.EVENT_BUS.post(new EntityViewRenderEvent.RenderFogEvent(renderer, entity, state, partial, mode, distance));
}
public static void onModelBake(ModelManager modelManager, IRegistry<ModelResourceLocation, IBakedModel> modelRegistry, ModelLoader modelLoader)
{
MinecraftForge.EVENT_BUS.post(new ModelBakeEvent(modelManager, modelRegistry, modelLoader));
modelLoader.onPostBakeEvent(modelRegistry);
}
private static final Matrix4f flipX;
static {
flipX = new Matrix4f();
flipX.setIdentity();
flipX.m00 = -1;
}
public static IBakedModel handleCameraTransforms(IBakedModel model, ItemCameraTransforms.TransformType cameraTransformType, boolean leftHandHackery)
{
Pair<? extends IBakedModel, Matrix4f> pair = model.handlePerspective(cameraTransformType);
if (pair.getRight() != null)
{
Matrix4f matrix = new Matrix4f(pair.getRight());
if (leftHandHackery)
{
matrix.mul(flipX, matrix);
matrix.mul(matrix, flipX);
}
multiplyCurrentGlMatrix(matrix);
}
return pair.getLeft();
}
private static final FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
public static void multiplyCurrentGlMatrix(Matrix4f matrix)
{
matrixBuf.clear();
float[] t = new float[4];
for(int i = 0; i < 4; i++)
{
matrix.getColumn(i, t);
matrixBuf.put(t);
}
matrixBuf.flip();
GL11.glMultMatrix(matrixBuf);
}
// moved and expanded from WorldVertexBufferUploader.draw
public static void preDraw(EnumUsage attrType, VertexFormat format, int element, int stride, ByteBuffer buffer)
{
VertexFormatElement attr = format.getElement(element);
int count = attr.getElementCount();
int constant = attr.getType().getGlConstant();
buffer.position(format.getOffset(element));
switch(attrType)
{
case POSITION:
GlStateManager.glVertexPointer(count, constant, stride, buffer);
GlStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
break;
case NORMAL:
if(count != 3)
{
throw new IllegalArgumentException("Normal attribute should have the size 3: " + attr);
}
GlStateManager.glNormalPointer(constant, stride, buffer);
GlStateManager.glEnableClientState(GL11.GL_NORMAL_ARRAY);
break;
case COLOR:
GlStateManager.glColorPointer(count, constant, stride, buffer);
GlStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
break;
case UV:
OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit + attr.getIndex());
GlStateManager.glTexCoordPointer(count, constant, stride, buffer);
GlStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit);
break;
case PADDING:
break;
case GENERIC:
GL20.glEnableVertexAttribArray(attr.getIndex());
GL20.glVertexAttribPointer(attr.getIndex(), count, constant, false, stride, buffer);
default:
FMLLog.log.fatal("Unimplemented vanilla attribute upload: {}", attrType.getDisplayName());
}
}
public static void postDraw(EnumUsage attrType, VertexFormat format, int element, int stride, ByteBuffer buffer)
{
VertexFormatElement attr = format.getElement(element);
switch(attrType)
{
case POSITION:
GlStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
break;
case NORMAL:
GlStateManager.glDisableClientState(GL11.GL_NORMAL_ARRAY);
break;
case COLOR:
GlStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
// is this really needed?
GlStateManager.resetColor();
break;
case UV:
OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit + attr.getIndex());
GlStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit);
break;
case PADDING:
break;
case GENERIC:
GL20.glDisableVertexAttribArray(attr.getIndex());
default:
FMLLog.log.fatal("Unimplemented vanilla attribute upload: {}", attrType.getDisplayName());
}
}
public static void transform(org.lwjgl.util.vector.Vector3f vec, Matrix4f m)
{
Vector4f tmp = new Vector4f(vec.x, vec.y, vec.z, 1f);
m.transform(tmp);
if(Math.abs(tmp.w - 1f) > 1e-5) tmp.scale(1f / tmp.w);
vec.set(tmp.x, tmp.y, tmp.z);
}
public static Matrix4f getMatrix(ModelRotation modelRotation)
{
Matrix4f ret = new Matrix4f(TRSRTransformation.toVecmath(modelRotation.getMatrix4d())), tmp = new Matrix4f();
tmp.setIdentity();
tmp.m03 = tmp.m13 = tmp.m23 = .5f;
ret.mul(tmp, ret);
tmp.invert();
//tmp.m03 = tmp.m13 = tmp.m23 = -.5f;
ret.mul(tmp);
return ret;
}
public static void putQuadColor(BufferBuilder renderer, BakedQuad quad, int color)
{
float cb = color & 0xFF;
float cg = (color >>> 8) & 0xFF;
float cr = (color >>> 16) & 0xFF;
float ca = (color >>> 24) & 0xFF;
VertexFormat format = quad.getFormat();
int size = format.getIntegerSize();
int offset = format.getColorOffset() / 4; // assumes that color is aligned
for(int i = 0; i < 4; i++)
{
int vc = quad.getVertexData()[offset + size * i];
float vcr = vc & 0xFF;
float vcg = (vc >>> 8) & 0xFF;
float vcb = (vc >>> 16) & 0xFF;
float vca = (vc >>> 24) & 0xFF;
int ncr = Math.min(0xFF, (int)(cr * vcr / 0xFF));
int ncg = Math.min(0xFF, (int)(cg * vcg / 0xFF));
int ncb = Math.min(0xFF, (int)(cb * vcb / 0xFF));
int nca = Math.min(0xFF, (int)(ca * vca / 0xFF));
renderer.putColorRGBA(renderer.getColorIndex(4 - i), ncr, ncg, ncb, nca);
}
}
private static Map<Pair<Item, Integer>, Class<? extends TileEntity>> tileItemMap = Maps.newHashMap();
public static void renderTileItem(Item item, int metadata)
{
Class<? extends TileEntity> tileClass = tileItemMap.get(Pair.of(item, metadata));
if (tileClass != null)
{
TileEntitySpecialRenderer<?> r = TileEntityRendererDispatcher.instance.getRenderer(tileClass);
if (r != null)
{
r.render(null, 0, 0, 0, 0, -1, 0.0F);
}
}
}
/**
* @deprecated Will be removed as soon as possible. See {@link Item#getTileEntityItemStackRenderer()}.
*/
@Deprecated
public static void registerTESRItemStack(Item item, int metadata, Class<? extends TileEntity> TileClass)
{
tileItemMap.put(Pair.of(item, metadata), TileClass);
}
/**
* internal, relies on fixed format of FaceBakery
*/
public static void fillNormal(int[] faceData, EnumFacing facing)
{
Vector3f v1 = getVertexPos(faceData, 3);
Vector3f t = getVertexPos(faceData, 1);
Vector3f v2 = getVertexPos(faceData, 2);
v1.sub(t);
t.set(getVertexPos(faceData, 0));
v2.sub(t);
v1.cross(v2, v1);
v1.normalize();
int x = ((byte) Math.round(v1.x * 127)) & 0xFF;
int y = ((byte) Math.round(v1.y * 127)) & 0xFF;
int z = ((byte) Math.round(v1.z * 127)) & 0xFF;
int normal = x | (y << 0x08) | (z << 0x10);
for(int i = 0; i < 4; i++)
{
faceData[i * 7 + 6] = normal;
}
}
private static Vector3f getVertexPos(int[] data, int vertex)
{
int idx = vertex * 7;
float x = Float.intBitsToFloat(data[idx]);
float y = Float.intBitsToFloat(data[idx + 1]);
float z = Float.intBitsToFloat(data[idx + 2]);
return new Vector3f(x, y, z);
}
@SuppressWarnings("deprecation")
public static Optional<TRSRTransformation> applyTransform(ItemTransformVec3f transform, Optional<? extends IModelPart> part)
{
if(part.isPresent()) return Optional.empty();
return Optional.of(TRSRTransformation.blockCenterToCorner(TRSRTransformation.from(transform)));
}
public static Optional<TRSRTransformation> applyTransform(ModelRotation rotation, Optional<? extends IModelPart> part)
{
if(part.isPresent()) return Optional.empty();
return Optional.of(TRSRTransformation.from(rotation));
}
public static Optional<TRSRTransformation> applyTransform(Matrix4f matrix, Optional<? extends IModelPart> part)
{
if(part.isPresent()) return Optional.empty();
return Optional.of(new TRSRTransformation(matrix));
}
public static void loadEntityShader(Entity entity, EntityRenderer entityRenderer)
{
if (entity != null)
{
ResourceLocation shader = ClientRegistry.getEntityShader(entity.getClass());
if (shader != null)
{
entityRenderer.loadShader(shader);
}
}
}
public static IBakedModel getDamageModel(IBakedModel ibakedmodel, TextureAtlasSprite texture, IBlockState state, IBlockAccess world, BlockPos pos)
{
state = state.getBlock().getExtendedState(state, world, pos);
return (new SimpleBakedModel.Builder(state, ibakedmodel, texture, pos)).makeBakedModel();
}
private static int slotMainHand = 0;
public static boolean shouldCauseReequipAnimation(@Nonnull ItemStack from, @Nonnull ItemStack to, int slot)
{
boolean fromInvalid = from.isEmpty();
boolean toInvalid = to.isEmpty();
if (fromInvalid && toInvalid) return false;
if (fromInvalid || toInvalid) return true;
boolean changed = false;
if (slot != -1)
{
changed = slot != slotMainHand;
slotMainHand = slot;
}
return from.getItem().shouldCauseReequipAnimation(from, to, changed);
}
public static boolean shouldCauseBlockBreakReset(@Nonnull ItemStack from, @Nonnull ItemStack to)
{
return from.getItem().shouldCauseBlockBreakReset(from, to);
}
public static BlockFaceUV applyUVLock(BlockFaceUV blockFaceUV, EnumFacing originalSide, ITransformation rotation)
{
TRSRTransformation global = new TRSRTransformation(rotation.getMatrix());
Matrix4f uv = global.getUVLockTransform(originalSide).getMatrix();
Vector4f vec = new Vector4f(0, 0, 0, 1);
float u0 = blockFaceUV.getVertexU(blockFaceUV.getVertexRotatedRev(0));
float v0 = blockFaceUV.getVertexV(blockFaceUV.getVertexRotatedRev(0));
vec.x = u0 / 16;
vec.y = v0 / 16;
uv.transform(vec);
float uMin = 16 * vec.x; // / vec.w;
float vMin = 16 * vec.y; // / vec.w;
float u1 = blockFaceUV.getVertexU(blockFaceUV.getVertexRotatedRev(2));
float v1 = blockFaceUV.getVertexV(blockFaceUV.getVertexRotatedRev(2));
vec.x = u1 / 16;
vec.y = v1 / 16;
vec.z = 0;
vec.w = 1;
uv.transform(vec);
float uMax = 16 * vec.x; // / vec.w;
float vMax = 16 * vec.y; // / vec.w;
if (uMin > uMax && u0 < u1 || uMin < uMax && u0 > u1)
{
float t = uMin;
uMin = uMax;
uMax = t;
}
if (vMin > vMax && v0 < v1 || vMin < vMax && v0 > v1)
{
float t = vMin;
vMin = vMax;
vMax = t;
}
float a = (float)Math.toRadians(blockFaceUV.rotation);
Vector3f rv = new Vector3f(MathHelper.cos(a), MathHelper.sin(a), 0);
Matrix3f rot = new Matrix3f();
uv.getRotationScale(rot);
rot.transform(rv);
int angle = MathHelper.normalizeAngle(-(int)Math.round(Math.toDegrees(Math.atan2(rv.y, rv.x)) / 90) * 90, 360);
return new BlockFaceUV(new float[]{ uMin, vMin, uMax, vMax }, angle);
}
public static RenderGameOverlayEvent.BossInfo bossBarRenderPre(ScaledResolution res, BossInfoClient bossInfo, int x, int y, int increment)
{
RenderGameOverlayEvent.BossInfo evt = new RenderGameOverlayEvent.BossInfo(new RenderGameOverlayEvent(Animation.getPartialTickTime(), res),
BOSSINFO, bossInfo, x, y, increment);
MinecraftForge.EVENT_BUS.post(evt);
return evt;
}
public static void bossBarRenderPost(ScaledResolution res)
{
MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(new RenderGameOverlayEvent(Animation.getPartialTickTime(), res), BOSSINFO));
}
public static ScreenshotEvent onScreenshot(BufferedImage image, File screenshotFile)
{
ScreenshotEvent event = new ScreenshotEvent(image, screenshotFile);
MinecraftForge.EVENT_BUS.post(event);
return event;
}
@SuppressWarnings("deprecation")
public static Pair<? extends IBakedModel,Matrix4f> handlePerspective(IBakedModel model, ItemCameraTransforms.TransformType type)
{
TRSRTransformation tr = TRSRTransformation.from(model.getItemCameraTransforms().getTransform(type));
Matrix4f mat = null;
if (!tr.isIdentity()) mat = tr.getMatrix();
return Pair.of(model, mat);
}
public static void onInputUpdate(EntityPlayer player, MovementInput movementInput)
{
MinecraftForge.EVENT_BUS.post(new InputUpdateEvent(player, movementInput));
}
public static String getHorseArmorTexture(EntityHorse horse, ItemStack armorStack)
{
String texture = armorStack.getItem().getHorseArmorTexture(horse, armorStack);
if(texture == null) texture = horse.getHorseArmorType().getTextureName();
return texture;
}
public static boolean shouldUseVanillaReloadableListener(IResourceManagerReloadListener listener)
{
Predicate<IResourceType> predicate = SelectiveReloadStateHandler.INSTANCE.get();
if (listener instanceof ModelManager || listener instanceof RenderItem)
return predicate.test(VanillaResourceType.MODELS);
else if (listener instanceof BlockRendererDispatcher || listener instanceof RenderGlobal)
return predicate.test(VanillaResourceType.MODELS);
else if (listener instanceof TextureManager || listener instanceof FontRenderer)
return predicate.test(VanillaResourceType.TEXTURES);
else if (listener instanceof FoliageColorReloadListener || listener instanceof GrassColorReloadListener)
return predicate.test(VanillaResourceType.TEXTURES);
else if (listener instanceof SoundHandler)
return predicate.test(VanillaResourceType.SOUNDS);
else if (listener instanceof EntityRenderer)
return predicate.test(VanillaResourceType.SHADERS);
else if (listener instanceof LanguageManager || listener instanceof SearchTreeManager)
return predicate.test(VanillaResourceType.LANGUAGES);
return true;
}
}

View File

@@ -0,0 +1,941 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import static net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType.*;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiIngame;
import net.minecraft.client.gui.GuiOverlayDebug;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.MobEffects;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.scoreboard.ScoreObjective;
import net.minecraft.scoreboard.ScorePlayerTeam;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.util.FoodStats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringUtils;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.opengl.GL11;
public class GuiIngameForge extends GuiIngame
{
//private static final ResourceLocation VIGNETTE = new ResourceLocation("textures/misc/vignette.png");
//private static final ResourceLocation WIDGITS = new ResourceLocation("textures/gui/widgets.png");
//private static final ResourceLocation PUMPKIN_BLUR = new ResourceLocation("textures/misc/pumpkinblur.png");
private static final int WHITE = 0xFFFFFF;
//Flags to toggle the rendering of certain aspects of the HUD, valid conditions
//must be met for them to render normally. If those conditions are met, but this flag
//is false, they will not be rendered.
public static boolean renderVignette = true;
public static boolean renderHelmet = true;
public static boolean renderPortal = true;
public static boolean renderHotbar = true;
public static boolean renderCrosshairs = true;
public static boolean renderBossHealth = true;
public static boolean renderHealth = true;
public static boolean renderArmor = true;
public static boolean renderFood = true;
public static boolean renderHealthMount = true;
public static boolean renderAir = true;
public static boolean renderExperiance = true;
public static boolean renderJumpBar = true;
public static boolean renderObjective = true;
public static int left_height = 39;
public static int right_height = 39;
private ScaledResolution res = null;
private FontRenderer fontrenderer = null;
private RenderGameOverlayEvent eventParent;
//private static final String MC_VERSION = MinecraftForge.MC_VERSION;
private GuiOverlayDebugForge debugOverlay;
public GuiIngameForge(Minecraft mc)
{
super(mc);
debugOverlay = new GuiOverlayDebugForge(mc);
}
@Override
public void renderGameOverlay(float partialTicks)
{
res = new ScaledResolution(mc);
eventParent = new RenderGameOverlayEvent(partialTicks, res);
int width = res.getScaledWidth();
int height = res.getScaledHeight();
renderHealthMount = mc.player.getRidingEntity() instanceof EntityLivingBase;
renderFood = mc.player.getRidingEntity() == null;
renderJumpBar = mc.player.isRidingHorse();
right_height = 39;
left_height = 39;
if (pre(ALL)) return;
fontrenderer = mc.fontRenderer;
mc.entityRenderer.setupOverlayRendering();
GlStateManager.enableBlend();
if (renderVignette && Minecraft.isFancyGraphicsEnabled())
{
renderVignette(mc.player.getBrightness(), res);
}
else
{
GlStateManager.enableDepth();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
}
if (renderHelmet) renderHelmet(res, partialTicks);
if (renderPortal && !mc.player.isPotionActive(MobEffects.NAUSEA))
{
renderPortal(res, partialTicks);
}
if (renderHotbar) renderHotbar(res, partialTicks);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
zLevel = -90.0F;
rand.setSeed((long)(updateCounter * 312871));
if (renderCrosshairs) renderCrosshairs(partialTicks);
if (renderBossHealth) renderBossHealth();
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
if (this.mc.playerController.shouldDrawHUD() && this.mc.getRenderViewEntity() instanceof EntityPlayer)
{
if (renderHealth) renderHealth(width, height);
if (renderArmor) renderArmor(width, height);
if (renderFood) renderFood(width, height);
if (renderHealthMount) renderHealthMount(width, height);
if (renderAir) renderAir(width, height);
}
renderSleepFade(width, height);
if (renderJumpBar)
{
renderJumpBar(width, height);
}
else if (renderExperiance)
{
renderExperience(width, height);
}
renderToolHighlight(res);
renderHUDText(width, height);
renderFPSGraph();
renderPotionIcons(res);
renderRecordOverlay(width, height, partialTicks);
renderSubtitles(res);
renderTitle(width, height, partialTicks);
Scoreboard scoreboard = this.mc.world.getScoreboard();
ScoreObjective objective = null;
ScorePlayerTeam scoreplayerteam = scoreboard.getPlayersTeam(mc.player.getName());
if (scoreplayerteam != null)
{
int slot = scoreplayerteam.getColor().getColorIndex();
if (slot >= 0) objective = scoreboard.getObjectiveInDisplaySlot(3 + slot);
}
ScoreObjective scoreobjective1 = objective != null ? objective : scoreboard.getObjectiveInDisplaySlot(1);
if (renderObjective && scoreobjective1 != null)
{
this.renderScoreboard(scoreobjective1, res);
}
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
GlStateManager.disableAlpha();
renderChat(width, height);
renderPlayerList(width, height);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.disableLighting();
GlStateManager.enableAlpha();
post(ALL);
}
public ScaledResolution getResolution()
{
return res;
}
protected void renderCrosshairs(float partialTicks)
{
if (pre(CROSSHAIRS)) return;
bind(Gui.ICONS);
GlStateManager.enableBlend();
super.renderAttackIndicator(partialTicks, res);
post(CROSSHAIRS);
}
protected void renderPotionIcons(ScaledResolution resolution)
{
if (pre(POTION_ICONS)) return;
super.renderPotionEffects(resolution);
post(POTION_ICONS);
}
protected void renderSubtitles(ScaledResolution resolution)
{
if (pre(SUBTITLES)) return;
this.overlaySubtitle.renderSubtitles(res);
post(SUBTITLES);
}
//@Override
protected void renderBossHealth()
{
if (pre(BOSSHEALTH)) return;
bind(Gui.ICONS);
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
mc.mcProfiler.startSection("bossHealth");
GlStateManager.enableBlend();
this.overlayBoss.renderBossHealth();
GlStateManager.disableBlend();
mc.mcProfiler.endSection();
post(BOSSHEALTH);
}
/**
* Renders a Vignette arount the entire screen that changes with light level.
*/
@Override
protected void renderVignette(float lightLevel, ScaledResolution scaledRes)
{
if (pre(VIGNETTE))
{
// Need to put this here, since Vanilla assumes this state after the vignette was rendered.
GlStateManager.enableDepth();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
return;
}
super.renderVignette(lightLevel, scaledRes);
post(VIGNETTE);
}
private void renderHelmet(ScaledResolution res, float partialTicks)
{
if (pre(HELMET)) return;
ItemStack itemstack = this.mc.player.inventory.armorItemInSlot(3);
if (this.mc.gameSettings.thirdPersonView == 0 && !itemstack.isEmpty())
{
Item item = itemstack.getItem();
if (item == Item.getItemFromBlock(Blocks.PUMPKIN))
{
renderPumpkinOverlay(res);
}
else
{
item.renderHelmetOverlay(itemstack, mc.player, res, partialTicks);
}
}
post(HELMET);
}
protected void renderArmor(int width, int height)
{
if (pre(ARMOR)) return;
mc.mcProfiler.startSection("armor");
GlStateManager.enableBlend();
int left = width / 2 - 91;
int top = height - left_height;
int level = ForgeHooks.getTotalArmorValue(mc.player);
for (int i = 1; level > 0 && i < 20; i += 2)
{
if (i < level)
{
drawTexturedModalRect(left, top, 34, 9, 9, 9);
}
else if (i == level)
{
drawTexturedModalRect(left, top, 25, 9, 9, 9);
}
else if (i > level)
{
drawTexturedModalRect(left, top, 16, 9, 9, 9);
}
left += 8;
}
left_height += 10;
GlStateManager.disableBlend();
mc.mcProfiler.endSection();
post(ARMOR);
}
protected void renderPortal(ScaledResolution res, float partialTicks)
{
if (pre(PORTAL)) return;
float f1 = mc.player.prevTimeInPortal + (mc.player.timeInPortal - mc.player.prevTimeInPortal) * partialTicks;
if (f1 > 0.0F)
{
renderPortal(f1, res);
}
post(PORTAL);
}
@Override
protected void renderHotbar(ScaledResolution res, float partialTicks)
{
if (pre(HOTBAR)) return;
if (mc.playerController.isSpectator())
{
this.spectatorGui.renderTooltip(res, partialTicks);
}
else
{
super.renderHotbar(res, partialTicks);
}
post(HOTBAR);
}
@Override
public void setOverlayMessage(ITextComponent component, boolean animateColor)
{
this.setOverlayMessage(component.getFormattedText(), animateColor);
}
protected void renderAir(int width, int height)
{
if (pre(AIR)) return;
mc.mcProfiler.startSection("air");
EntityPlayer player = (EntityPlayer)this.mc.getRenderViewEntity();
GlStateManager.enableBlend();
int left = width / 2 + 91;
int top = height - right_height;
if (player.isInsideOfMaterial(Material.WATER))
{
int air = player.getAir();
int full = MathHelper.ceil((double)(air - 2) * 10.0D / 300.0D);
int partial = MathHelper.ceil((double)air * 10.0D / 300.0D) - full;
for (int i = 0; i < full + partial; ++i)
{
drawTexturedModalRect(left - i * 8 - 9, top, (i < full ? 16 : 25), 18, 9, 9);
}
right_height += 10;
}
GlStateManager.disableBlend();
mc.mcProfiler.endSection();
post(AIR);
}
public void renderHealth(int width, int height)
{
bind(ICONS);
if (pre(HEALTH)) return;
mc.mcProfiler.startSection("health");
GlStateManager.enableBlend();
EntityPlayer player = (EntityPlayer)this.mc.getRenderViewEntity();
int health = MathHelper.ceil(player.getHealth());
boolean highlight = healthUpdateCounter > (long)updateCounter && (healthUpdateCounter - (long)updateCounter) / 3L %2L == 1L;
if (health < this.playerHealth && player.hurtResistantTime > 0)
{
this.lastSystemTime = Minecraft.getSystemTime();
this.healthUpdateCounter = (long)(this.updateCounter + 20);
}
else if (health > this.playerHealth && player.hurtResistantTime > 0)
{
this.lastSystemTime = Minecraft.getSystemTime();
this.healthUpdateCounter = (long)(this.updateCounter + 10);
}
if (Minecraft.getSystemTime() - this.lastSystemTime > 1000L)
{
this.playerHealth = health;
this.lastPlayerHealth = health;
this.lastSystemTime = Minecraft.getSystemTime();
}
this.playerHealth = health;
int healthLast = this.lastPlayerHealth;
IAttributeInstance attrMaxHealth = player.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH);
float healthMax = (float)attrMaxHealth.getAttributeValue();
float absorb = MathHelper.ceil(player.getAbsorptionAmount());
int healthRows = MathHelper.ceil((healthMax + absorb) / 2.0F / 10.0F);
int rowHeight = Math.max(10 - (healthRows - 2), 3);
this.rand.setSeed((long)(updateCounter * 312871));
int left = width / 2 - 91;
int top = height - left_height;
left_height += (healthRows * rowHeight);
if (rowHeight != 10) left_height += 10 - rowHeight;
int regen = -1;
if (player.isPotionActive(MobEffects.REGENERATION))
{
regen = updateCounter % 25;
}
final int TOP = 9 * (mc.world.getWorldInfo().isHardcoreModeEnabled() ? 5 : 0);
final int BACKGROUND = (highlight ? 25 : 16);
int MARGIN = 16;
if (player.isPotionActive(MobEffects.POISON)) MARGIN += 36;
else if (player.isPotionActive(MobEffects.WITHER)) MARGIN += 72;
float absorbRemaining = absorb;
for (int i = MathHelper.ceil((healthMax + absorb) / 2.0F) - 1; i >= 0; --i)
{
//int b0 = (highlight ? 1 : 0);
int row = MathHelper.ceil((float)(i + 1) / 10.0F) - 1;
int x = left + i % 10 * 8;
int y = top - row * rowHeight;
if (health <= 4) y += rand.nextInt(2);
if (i == regen) y -= 2;
drawTexturedModalRect(x, y, BACKGROUND, TOP, 9, 9);
if (highlight)
{
if (i * 2 + 1 < healthLast)
drawTexturedModalRect(x, y, MARGIN + 54, TOP, 9, 9); //6
else if (i * 2 + 1 == healthLast)
drawTexturedModalRect(x, y, MARGIN + 63, TOP, 9, 9); //7
}
if (absorbRemaining > 0.0F)
{
if (absorbRemaining == absorb && absorb % 2.0F == 1.0F)
{
drawTexturedModalRect(x, y, MARGIN + 153, TOP, 9, 9); //17
absorbRemaining -= 1.0F;
}
else
{
drawTexturedModalRect(x, y, MARGIN + 144, TOP, 9, 9); //16
absorbRemaining -= 2.0F;
}
}
else
{
if (i * 2 + 1 < health)
drawTexturedModalRect(x, y, MARGIN + 36, TOP, 9, 9); //4
else if (i * 2 + 1 == health)
drawTexturedModalRect(x, y, MARGIN + 45, TOP, 9, 9); //5
}
}
GlStateManager.disableBlend();
mc.mcProfiler.endSection();
post(HEALTH);
}
public void renderFood(int width, int height)
{
if (pre(FOOD)) return;
mc.mcProfiler.startSection("food");
EntityPlayer player = (EntityPlayer)this.mc.getRenderViewEntity();
GlStateManager.enableBlend();
int left = width / 2 + 91;
int top = height - right_height;
right_height += 10;
boolean unused = false;// Unused flag in vanilla, seems to be part of a 'fade out' mechanic
FoodStats stats = mc.player.getFoodStats();
int level = stats.getFoodLevel();
for (int i = 0; i < 10; ++i)
{
int idx = i * 2 + 1;
int x = left - i * 8 - 9;
int y = top;
int icon = 16;
byte background = 0;
if (mc.player.isPotionActive(MobEffects.HUNGER))
{
icon += 36;
background = 13;
}
if (unused) background = 1; //Probably should be a += 1 but vanilla never uses this
if (player.getFoodStats().getSaturationLevel() <= 0.0F && updateCounter % (level * 3 + 1) == 0)
{
y = top + (rand.nextInt(3) - 1);
}
drawTexturedModalRect(x, y, 16 + background * 9, 27, 9, 9);
if (idx < level)
drawTexturedModalRect(x, y, icon + 36, 27, 9, 9);
else if (idx == level)
drawTexturedModalRect(x, y, icon + 45, 27, 9, 9);
}
GlStateManager.disableBlend();
mc.mcProfiler.endSection();
post(FOOD);
}
protected void renderSleepFade(int width, int height)
{
if (mc.player.getSleepTimer() > 0)
{
mc.mcProfiler.startSection("sleep");
GlStateManager.disableDepth();
GlStateManager.disableAlpha();
int sleepTime = mc.player.getSleepTimer();
float opacity = (float)sleepTime / 100.0F;
if (opacity > 1.0F)
{
opacity = 1.0F - (float)(sleepTime - 100) / 10.0F;
}
int color = (int)(220.0F * opacity) << 24 | 1052704;
drawRect(0, 0, width, height, color);
GlStateManager.enableAlpha();
GlStateManager.enableDepth();
mc.mcProfiler.endSection();
}
}
protected void renderExperience(int width, int height)
{
bind(ICONS);
if (pre(EXPERIENCE)) return;
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.disableBlend();
if (mc.playerController.gameIsSurvivalOrAdventure())
{
mc.mcProfiler.startSection("expBar");
int cap = this.mc.player.xpBarCap();
int left = width / 2 - 91;
if (cap > 0)
{
short barWidth = 182;
int filled = (int)(mc.player.experience * (float)(barWidth + 1));
int top = height - 32 + 3;
drawTexturedModalRect(left, top, 0, 64, barWidth, 5);
if (filled > 0)
{
drawTexturedModalRect(left, top, 0, 69, filled, 5);
}
}
this.mc.mcProfiler.endSection();
if (mc.playerController.gameIsSurvivalOrAdventure() && mc.player.experienceLevel > 0)
{
mc.mcProfiler.startSection("expLevel");
boolean flag1 = false;
int color = flag1 ? 16777215 : 8453920;
String text = "" + mc.player.experienceLevel;
int x = (width - fontrenderer.getStringWidth(text)) / 2;
int y = height - 31 - 4;
fontrenderer.drawString(text, x + 1, y, 0);
fontrenderer.drawString(text, x - 1, y, 0);
fontrenderer.drawString(text, x, y + 1, 0);
fontrenderer.drawString(text, x, y - 1, 0);
fontrenderer.drawString(text, x, y, color);
mc.mcProfiler.endSection();
}
}
GlStateManager.enableBlend();
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
post(EXPERIENCE);
}
protected void renderJumpBar(int width, int height)
{
bind(ICONS);
if (pre(JUMPBAR)) return;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.disableBlend();
mc.mcProfiler.startSection("jumpBar");
float charge = mc.player.getHorseJumpPower();
final int barWidth = 182;
int x = (width / 2) - (barWidth / 2);
int filled = (int)(charge * (float)(barWidth + 1));
int top = height - 32 + 3;
drawTexturedModalRect(x, top, 0, 84, barWidth, 5);
if (filled > 0)
{
this.drawTexturedModalRect(x, top, 0, 89, filled, 5);
}
GlStateManager.enableBlend();
mc.mcProfiler.endSection();
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
post(JUMPBAR);
}
protected void renderToolHighlight(ScaledResolution res)
{
if (this.mc.gameSettings.heldItemTooltips && !this.mc.playerController.isSpectator())
{
mc.mcProfiler.startSection("toolHighlight");
if (this.remainingHighlightTicks > 0 && !this.highlightingItemStack.isEmpty())
{
String name = this.highlightingItemStack.getDisplayName();
if (this.highlightingItemStack.hasDisplayName())
name = TextFormatting.ITALIC + name;
name = this.highlightingItemStack.getItem().getHighlightTip(this.highlightingItemStack, name);
int opacity = (int)((float)this.remainingHighlightTicks * 256.0F / 10.0F);
if (opacity > 255) opacity = 255;
if (opacity > 0)
{
int y = res.getScaledHeight() - 59;
if (!mc.playerController.shouldDrawHUD()) y += 14;
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
FontRenderer font = highlightingItemStack.getItem().getFontRenderer(highlightingItemStack);
if (font != null)
{
int x = (res.getScaledWidth() - font.getStringWidth(name)) / 2;
font.drawStringWithShadow(name, x, y, WHITE | (opacity << 24));
}
else
{
int x = (res.getScaledWidth() - fontrenderer.getStringWidth(name)) / 2;
fontrenderer.drawStringWithShadow(name, x, y, WHITE | (opacity << 24));
}
GlStateManager.disableBlend();
GlStateManager.popMatrix();
}
}
mc.mcProfiler.endSection();
}
else if (this.mc.player.isSpectator())
{
this.spectatorGui.renderSelectedItem(res);
}
}
protected void renderHUDText(int width, int height)
{
mc.mcProfiler.startSection("forgeHudText");
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
ArrayList<String> listL = new ArrayList<String>();
ArrayList<String> listR = new ArrayList<String>();
if (mc.isDemo())
{
long time = mc.world.getTotalWorldTime();
if (time >= 120500L)
{
listR.add(I18n.format("demo.demoExpired"));
}
else
{
listR.add(I18n.format("demo.remainingTime", StringUtils.ticksToElapsedTime((int)(120500L - time))));
}
}
if (this.mc.gameSettings.showDebugInfo && !pre(DEBUG))
{
listL.addAll(debugOverlay.getLeft());
listR.addAll(debugOverlay.getRight());
post(DEBUG);
}
RenderGameOverlayEvent.Text event = new RenderGameOverlayEvent.Text(eventParent, listL, listR);
if (!MinecraftForge.EVENT_BUS.post(event))
{
int top = 2;
for (String msg : listL)
{
if (msg == null) continue;
drawRect(1, top - 1, 2 + fontrenderer.getStringWidth(msg) + 1, top + fontrenderer.FONT_HEIGHT - 1, -1873784752);
fontrenderer.drawString(msg, 2, top, 14737632);
top += fontrenderer.FONT_HEIGHT;
}
top = 2;
for (String msg : listR)
{
if (msg == null) continue;
int w = fontrenderer.getStringWidth(msg);
int left = width - 2 - w;
drawRect(left - 1, top - 1, left + w + 1, top + fontrenderer.FONT_HEIGHT - 1, -1873784752);
fontrenderer.drawString(msg, left, top, 14737632);
top += fontrenderer.FONT_HEIGHT;
}
}
mc.mcProfiler.endSection();
post(TEXT);
}
protected void renderFPSGraph()
{
if (this.mc.gameSettings.showDebugInfo && this.mc.gameSettings.showLagometer && !pre(FPS_GRAPH))
{
this.debugOverlay.renderLagometer();
post(FPS_GRAPH);
}
}
protected void renderRecordOverlay(int width, int height, float partialTicks)
{
if (overlayMessageTime > 0)
{
mc.mcProfiler.startSection("overlayMessage");
float hue = (float)overlayMessageTime - partialTicks;
int opacity = (int)(hue * 256.0F / 20.0F);
if (opacity > 255) opacity = 255;
if (opacity > 0)
{
GlStateManager.pushMatrix();
GlStateManager.translate((float)(width / 2), (float)(height - 68), 0.0F);
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
int color = (animateOverlayMessageColor ? Color.HSBtoRGB(hue / 50.0F, 0.7F, 0.6F) & WHITE : WHITE);
fontrenderer.drawString(overlayMessage, -fontrenderer.getStringWidth(overlayMessage) / 2, -4, color | (opacity << 24));
GlStateManager.disableBlend();
GlStateManager.popMatrix();
}
mc.mcProfiler.endSection();
}
}
protected void renderTitle(int width, int height, float partialTicks)
{
if (titlesTimer > 0)
{
mc.mcProfiler.startSection("titleAndSubtitle");
float age = (float)this.titlesTimer - partialTicks;
int opacity = 255;
if (titlesTimer > titleFadeOut + titleDisplayTime)
{
float f3 = (float)(titleFadeIn + titleDisplayTime + titleFadeOut) - age;
opacity = (int)(f3 * 255.0F / (float)titleFadeIn);
}
if (titlesTimer <= titleFadeOut) opacity = (int)(age * 255.0F / (float)this.titleFadeOut);
opacity = MathHelper.clamp(opacity, 0, 255);
if (opacity > 8)
{
GlStateManager.pushMatrix();
GlStateManager.translate((float)(width / 2), (float)(height / 2), 0.0F);
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
GlStateManager.pushMatrix();
GlStateManager.scale(4.0F, 4.0F, 4.0F);
int l = opacity << 24 & -16777216;
this.getFontRenderer().drawString(this.displayedTitle, (float)(-this.getFontRenderer().getStringWidth(this.displayedTitle) / 2), -10.0F, 16777215 | l, true);
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
GlStateManager.scale(2.0F, 2.0F, 2.0F);
this.getFontRenderer().drawString(this.displayedSubTitle, (float)(-this.getFontRenderer().getStringWidth(this.displayedSubTitle) / 2), 5.0F, 16777215 | l, true);
GlStateManager.popMatrix();
GlStateManager.disableBlend();
GlStateManager.popMatrix();
}
this.mc.mcProfiler.endSection();
}
}
protected void renderChat(int width, int height)
{
mc.mcProfiler.startSection("chat");
RenderGameOverlayEvent.Chat event = new RenderGameOverlayEvent.Chat(eventParent, 0, height - 48);
if (MinecraftForge.EVENT_BUS.post(event)) return;
GlStateManager.pushMatrix();
GlStateManager.translate((float) event.getPosX(), (float) event.getPosY(), 0.0F);
persistantChatGUI.drawChat(updateCounter);
GlStateManager.popMatrix();
post(CHAT);
mc.mcProfiler.endSection();
}
protected void renderPlayerList(int width, int height)
{
ScoreObjective scoreobjective = this.mc.world.getScoreboard().getObjectiveInDisplaySlot(0);
NetHandlerPlayClient handler = mc.player.connection;
if (mc.gameSettings.keyBindPlayerList.isKeyDown() && (!mc.isIntegratedServerRunning() || handler.getPlayerInfoMap().size() > 1 || scoreobjective != null))
{
this.overlayPlayerList.updatePlayerList(true);
if (pre(PLAYER_LIST)) return;
this.overlayPlayerList.renderPlayerlist(width, this.mc.world.getScoreboard(), scoreobjective);
post(PLAYER_LIST);
}
else
{
this.overlayPlayerList.updatePlayerList(false);
}
}
protected void renderHealthMount(int width, int height)
{
EntityPlayer player = (EntityPlayer)mc.getRenderViewEntity();
Entity tmp = player.getRidingEntity();
if (!(tmp instanceof EntityLivingBase)) return;
bind(ICONS);
if (pre(HEALTHMOUNT)) return;
boolean unused = false;
int left_align = width / 2 + 91;
mc.mcProfiler.endStartSection("mountHealth");
GlStateManager.enableBlend();
EntityLivingBase mount = (EntityLivingBase)tmp;
int health = (int)Math.ceil((double)mount.getHealth());
float healthMax = mount.getMaxHealth();
int hearts = (int)(healthMax + 0.5F) / 2;
if (hearts > 30) hearts = 30;
final int MARGIN = 52;
final int BACKGROUND = MARGIN + (unused ? 1 : 0);
final int HALF = MARGIN + 45;
final int FULL = MARGIN + 36;
for (int heart = 0; hearts > 0; heart += 20)
{
int top = height - right_height;
int rowCount = Math.min(hearts, 10);
hearts -= rowCount;
for (int i = 0; i < rowCount; ++i)
{
int x = left_align - i * 8 - 9;
drawTexturedModalRect(x, top, BACKGROUND, 9, 9, 9);
if (i * 2 + 1 + heart < health)
drawTexturedModalRect(x, top, FULL, 9, 9, 9);
else if (i * 2 + 1 + heart == health)
drawTexturedModalRect(x, top, HALF, 9, 9, 9);
}
right_height += 10;
}
GlStateManager.disableBlend();
post(HEALTHMOUNT);
}
//Helper macros
private boolean pre(ElementType type)
{
return MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Pre(eventParent, type));
}
private void post(ElementType type)
{
MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(eventParent, type));
}
private void bind(ResourceLocation res)
{
mc.getTextureManager().bindTexture(res);
}
private class GuiOverlayDebugForge extends GuiOverlayDebug
{
private Minecraft mc;
private GuiOverlayDebugForge(Minecraft mc)
{
super(mc);
this.mc = mc;
}
@Override protected void renderDebugInfoLeft(){}
@Override protected void renderDebugInfoRight(ScaledResolution res){}
private List<String> getLeft()
{
List<String> ret = this.call();
ret.add("");
ret.add("Debug: Pie [shift]: " + (this.mc.gameSettings.showDebugProfilerChart ? "visible" : "hidden") + " FPS [alt]: " + (this.mc.gameSettings.showLagometer ? "visible" : "hidden"));
ret.add("For help: press F3 + Q");
return ret;
}
private List<String> getRight(){ return this.getDebugInfoRight(); }
}
}

View File

@@ -0,0 +1,39 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
/**
* Client-side commands can implement this interface to allow additional control over when the command may be used.
*/
public interface IClientCommand extends ICommand
{
/**
* Determine whether this command can be used without the "/" prefix. By default this is true.
* @param sender the command sender
* @param message the message, without potential prefix
* @return true to allow the usage of this command without the prefix
*/
boolean allowUsageWithoutPrefix(ICommandSender sender, String message);
}

View File

@@ -0,0 +1,35 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
/**
* @deprecated TODO remove in 1.13. This has never been used
*/
@Deprecated
public interface IRenderContextHandler
{
/** Run before the specified rendering context.
*/
void beforeRenderContext();
/** Run after the specified rendering context.
*/
void afterRenderContext();
}

View File

@@ -0,0 +1,31 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
public abstract class IRenderHandler
{
@SideOnly(Side.CLIENT)
public abstract void render(float partialTicks, WorldClient world, Minecraft mc);
}

View File

@@ -0,0 +1,133 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import java.util.Map;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.client.renderer.ItemMeshDefinition;
import net.minecraft.client.renderer.ItemModelMesher;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelBakery;
import net.minecraft.client.renderer.block.model.ModelManager;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.registries.IRegistryDelegate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Wrapper around ItemModeMesher that cleans up the internal maps to respect ID remapping.
*/
public class ItemModelMesherForge extends ItemModelMesher
{
final Map<IRegistryDelegate<Item>, Int2ObjectMap<ModelResourceLocation>> locations = Maps.newHashMap();
final Map<IRegistryDelegate<Item>, Int2ObjectMap<IBakedModel>> models = Maps.newHashMap();
public ItemModelMesherForge(ModelManager manager)
{
super(manager);
}
@Override
@Nullable
protected IBakedModel getItemModel(Item item, int meta)
{
Int2ObjectMap<IBakedModel> map = models.get(item.delegate);
return map == null ? null : map.get(meta);
}
@Override
public void register(Item item, int meta, ModelResourceLocation location)
{
IRegistryDelegate<Item> key = item.delegate;
Int2ObjectMap<ModelResourceLocation> locs = locations.get(key);
Int2ObjectMap<IBakedModel> mods = models.get(key);
if (locs == null)
{
locs = new Int2ObjectOpenHashMap<>();
locations.put(key, locs);
}
if (mods == null)
{
mods = new Int2ObjectOpenHashMap<>();
models.put(key, mods);
}
locs.put(meta, location);
mods.put(meta, getModelManager().getModel(location));
}
@Override
public void rebuildCache()
{
final ModelManager manager = this.getModelManager();
for (Map.Entry<IRegistryDelegate<Item>, Int2ObjectMap<ModelResourceLocation>> e : locations.entrySet())
{
Int2ObjectMap<IBakedModel> mods = models.get(e.getKey());
if (mods != null)
{
mods.clear();
}
else
{
mods = new Int2ObjectOpenHashMap<>();
models.put(e.getKey(), mods);
}
final Int2ObjectMap<IBakedModel> map = mods;
e.getValue().int2ObjectEntrySet().forEach(entry ->
map.put(entry.getIntKey(), manager.getModel(entry.getValue()))
);
}
}
public ModelResourceLocation getLocation(@Nonnull ItemStack stack)
{
Item item = stack.getItem();
ModelResourceLocation location = null;
Int2ObjectMap<ModelResourceLocation> map = locations.get(item.delegate);
if (map != null)
{
location = map.get(getMetadata(stack));
}
if (location == null)
{
ItemMeshDefinition definition = shapers.get(item);
if (definition != null)
{
location = definition.getModelLocation(stack);
}
}
if (location == null)
{
location = ModelBakery.MODEL_MISSING;
}
return location;
}
}

View File

@@ -0,0 +1,127 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import java.util.BitSet;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import net.minecraft.client.Minecraft;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkCache;
import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class MinecraftForgeClient
{
public static int getRenderPass()
{
return ForgeHooksClient.renderPass;
}
public static BlockRenderLayer getRenderLayer()
{
return ForgeHooksClient.renderLayer.get();
}
/**
* returns the Locale set by the player in Minecraft.
* Useful for creating string and number formatters.
*/
public static Locale getLocale()
{
return Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getJavaLocale();
}
private static BitSet stencilBits = new BitSet(8);
static
{
stencilBits.set(0,8);
}
/**
* Reserve a stencil bit for use in rendering
*
* Note: you must check the Framebuffer you are working with to
* determine if stencil bits are enabled on it before use.
*
* @return A bit or -1 if no further stencil bits are available
*/
public static int reserveStencilBit()
{
int bit = stencilBits.nextSetBit(0);
if (bit >= 0)
{
stencilBits.clear(bit);
}
return bit;
}
/**
* Release the stencil bit for other use
*
* @param bit The bit from {@link #reserveStencilBit()}
*/
public static void releaseStencilBit(int bit)
{
if (bit >= 0 && bit < stencilBits.length())
{
stencilBits.set(bit);
}
}
private static final LoadingCache<Pair<World, BlockPos>, ChunkCache> regionCache = CacheBuilder.newBuilder()
.maximumSize(500)
.concurrencyLevel(5)
.expireAfterAccess(1, TimeUnit.SECONDS)
.build(new CacheLoader<Pair<World, BlockPos>, ChunkCache>()
{
@Override
public ChunkCache load(Pair<World, BlockPos> key)
{
return new ChunkCache(key.getLeft(), key.getRight().add(-1, -1, -1), key.getRight().add(16, 16, 16), 1);
}
});
public static void onRebuildChunk(World world, BlockPos position, ChunkCache cache)
{
regionCache.put(Pair.of(world, position), cache);
}
public static ChunkCache getRegionRenderCache(World world, BlockPos pos)
{
int x = pos.getX() & ~0xF;
int y = pos.getY() & ~0xF;
int z = pos.getZ() & ~0xF;
return regionCache.getUnchecked(Pair.of(world, new BlockPos(x, y, z)));
}
public static void clearRenderCache()
{
regionCache.invalidateAll();
regionCache.cleanUp();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import com.google.common.base.Strings;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.common.MinecraftForge;
import net.minecraft.client.gui.GuiScreen;
/**
* ClientChatEvent is fired whenever the client is about to send a chat message or command to the server. <br>
* This event is fired via {@link ForgeEventFactory#onClientSendMessage(String)},
* which is executed by {@link GuiScreen#sendChatMessage(String, boolean)}<br>
* <br>
* {@link #message} contains the message that will be sent to the server. This can be changed by mods.<br>
* {@link #originalMessage} contains the original message that was going to be sent to the server. This cannot be changed by mods.<br>
* <br>
* This event is {@link Cancelable}. <br>
* If this event is canceled, the chat message or command is never sent to the server.<br>
* <br>
* This event does not have a result. {@link HasResult}<br>
* <br>
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
**/
@Cancelable
public class ClientChatEvent extends Event
{
private String message;
private final String originalMessage;
public ClientChatEvent(String message)
{
this.setMessage(message);
this.originalMessage = Strings.nullToEmpty(message);
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = Strings.nullToEmpty(message);
}
public String getOriginalMessage()
{
return originalMessage;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.util.text.ChatType;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
@Cancelable
public class ClientChatReceivedEvent extends Event
{
private ITextComponent message;
private final ChatType type;
public ClientChatReceivedEvent(ChatType type, ITextComponent message)
{
this.type = type;
this.setMessage(message);
}
public ITextComponent getMessage()
{
return message;
}
public void setMessage(ITextComponent message)
{
this.message = message;
}
public ChatType getType()
{
return type;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.client.renderer.color.ItemColors;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Use these events to register block/item
* color handlers at the appropriate time.
*/
public abstract class ColorHandlerEvent extends Event
{
public static class Block extends ColorHandlerEvent
{
private final BlockColors blockColors;
public Block(BlockColors blockColors)
{
this.blockColors = blockColors;
}
public BlockColors getBlockColors()
{
return blockColors;
}
}
public static class Item extends ColorHandlerEvent
{
private final ItemColors itemColors;
private final BlockColors blockColors;
public Item(ItemColors itemColors, BlockColors blockColors)
{
this.itemColors = itemColors;
this.blockColors = blockColors;
}
public ItemColors getItemColors()
{
return itemColors;
}
public BlockColors getBlockColors()
{
return blockColors;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.client.renderer.RenderGlobal;
@Cancelable
public class DrawBlockHighlightEvent extends Event
{
private final RenderGlobal context;
private final EntityPlayer player;
private final RayTraceResult target;
private final int subID;
private final float partialTicks;
public DrawBlockHighlightEvent(RenderGlobal context, EntityPlayer player, RayTraceResult target, int subID, float partialTicks)
{
this.context = context;
this.player = player;
this.target = target;
this.subID = subID;
this.partialTicks= partialTicks;
}
public RenderGlobal getContext() { return context; }
public EntityPlayer getPlayer() { return player; }
public RayTraceResult getTarget() { return target; }
public int getSubID() { return subID; }
public float getPartialTicks() { return partialTicks; }
}

View File

@@ -0,0 +1,194 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.entity.Entity;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.Event.HasResult;
/**
* Event that hooks into EntityRenderer, allowing any feature to customize visual attributes
* the player sees.
*/
public abstract class EntityViewRenderEvent extends Event
{
private final EntityRenderer renderer;
private final Entity entity;
private final IBlockState state;
private final double renderPartialTicks;
public EntityViewRenderEvent(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks)
{
this.renderer = renderer;
this.entity = entity;
this.state = state;
this.renderPartialTicks = renderPartialTicks;
}
public EntityRenderer getRenderer()
{
return renderer;
}
public Entity getEntity()
{
return entity;
}
public IBlockState getState()
{
return state;
}
public double getRenderPartialTicks()
{
return renderPartialTicks;
}
/**
* Event that allows any feature to customize the fog density the player sees.
* NOTE: In order to make this event have an effect, you must cancel the event
*/
@Cancelable
public static class FogDensity extends EntityViewRenderEvent
{
private float density;
public FogDensity(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks, float density)
{
super(renderer, entity, state, renderPartialTicks);
this.setDensity(density);
}
public float getDensity()
{
return density;
}
public void setDensity(float density)
{
this.density = density;
}
}
/**
* Event that allows any feature to customize the rendering of fog.
*/
@HasResult
public static class RenderFogEvent extends EntityViewRenderEvent
{
private final int fogMode;
private final float farPlaneDistance;
public RenderFogEvent(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks, int fogMode, float farPlaneDistance)
{
super(renderer, entity, state, renderPartialTicks);
this.fogMode = fogMode;
this.farPlaneDistance = farPlaneDistance;
}
public int getFogMode()
{
return fogMode;
}
public float getFarPlaneDistance()
{
return farPlaneDistance;
}
}
/**
* Event that allows any feature to customize the color of fog the player sees.
* NOTE: Any change made to one of the color variables will affect the result seen in-game.
*/
public static class FogColors extends EntityViewRenderEvent
{
private float red;
private float green;
private float blue;
public FogColors(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks, float red, float green, float blue)
{
super(renderer, entity, state, renderPartialTicks);
this.setRed(red);
this.setGreen(green);
this.setBlue(blue);
}
public float getRed() { return red; }
public void setRed(float red) { this.red = red; }
public float getGreen() { return green; }
public void setGreen(float green) { this.green = green; }
public float getBlue() { return blue; }
public void setBlue(float blue) { this.blue = blue; }
}
/**
* Event that allows mods to alter the angles of the player's camera. Mainly useful for applying roll.
*/
public static class CameraSetup extends EntityViewRenderEvent
{
private float yaw;
private float pitch;
private float roll;
public CameraSetup(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks, float yaw, float pitch, float roll)
{
super(renderer, entity, state, renderPartialTicks);
this.setYaw(yaw);
this.setPitch(pitch);
this.setRoll(roll);
}
public float getYaw() { return yaw; }
public void setYaw(float yaw) { this.yaw = yaw; }
public float getPitch() { return pitch; }
public void setPitch(float pitch) { this.pitch = pitch; }
public float getRoll() { return roll; }
public void setRoll(float roll) { this.roll = roll; }
}
/**
* Event that allows mods to alter the raw FOV itself.
* This directly affects to the FOV without being modified.
* */
public static class FOVModifier extends EntityViewRenderEvent
{
private float fov;
public FOVModifier(EntityRenderer renderer, Entity entity, IBlockState state, double renderPartialTicks, float fov) {
super(renderer, entity, state, renderPartialTicks);
this.setFOV(fov);
}
public float getFOV() {
return fov;
}
public void setFOV(float fov) {
this.fov = fov;
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Author: MachineMuse (Claire Semple)
* Created: 6:07 PM, 9/5/13
*/
public class FOVUpdateEvent extends Event
{
private final EntityPlayer entity;
private final float fov;
private float newfov;
public FOVUpdateEvent(EntityPlayer entity, float fov)
{
this.entity = entity;
this.fov = fov;
this.setNewfov(fov);
}
public EntityPlayer getEntity()
{
return entity;
}
public float getFov()
{
return fov;
}
public float getNewfov()
{
return newfov;
}
public void setNewfov(float newfov)
{
this.newfov = newfov;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Event class for handling GuiContainer specific events.
*/
public class GuiContainerEvent extends Event
{
private final GuiContainer guiContainer;
public GuiContainerEvent(GuiContainer guiContainer)
{
this.guiContainer = guiContainer;
}
public GuiContainer getGuiContainer()
{
return guiContainer;
}
/**
* This event is fired directly after the GuiContainer has draw any foreground elements,
* But before the "dragged" stack, and before any tooltips.
* This is useful for any slot / item specific overlays.
* Things that need to be on top of All GUI elements but bellow tooltips and dragged stacks.
*/
public static class DrawForeground extends GuiContainerEvent
{
private final int mouseX;
private final int mouseY;
/**
* Called directly after the GuiContainer has drawn any foreground elements.
*
* @param guiContainer The container.
* @param mouseX The current X position of the players mouse.
* @param mouseY The current Y position of the players mouse.
*/
public DrawForeground(GuiContainer guiContainer, int mouseX, int mouseY)
{
super(guiContainer);
this.mouseX = mouseX;
this.mouseY = mouseY;
}
public int getMouseX()
{
return mouseX;
}
public int getMouseY()
{
return mouseY;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.gui.GuiScreen;
/**
* This event is called before any Gui will open.
* If you don't want this to happen, cancel the event.
* If you want to override this Gui, simply set the gui variable to your own Gui.
*
* @author jk-5
*/
@Cancelable
public class GuiOpenEvent extends Event
{
private GuiScreen gui;
public GuiOpenEvent(GuiScreen gui)
{
this.setGui(gui);
}
public GuiScreen getGui()
{
return gui;
}
public void setGui(GuiScreen gui)
{
this.gui = gui;
}
}

View File

@@ -0,0 +1,363 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.InventoryEffectRenderer;
import org.lwjgl.input.Mouse;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/**
* Event classes for GuiScreen events.
*
* @author bspkrs
*/
@SideOnly(Side.CLIENT)
public class GuiScreenEvent extends Event
{
private final GuiScreen gui;
public GuiScreenEvent(GuiScreen gui)
{
this.gui = gui;
}
/**
* The GuiScreen object generating this event.
*/
public GuiScreen getGui()
{
return gui;
}
public static class InitGuiEvent extends GuiScreenEvent
{
private List<GuiButton> buttonList;
public InitGuiEvent(GuiScreen gui, List<GuiButton> buttonList)
{
super(gui);
this.setButtonList(buttonList);
}
/**
* The {@link #buttonList} field from the GuiScreen object referenced by {@link #gui}.
*/
public List<GuiButton> getButtonList()
{
return buttonList;
}
public void setButtonList(List<GuiButton> buttonList)
{
this.buttonList = buttonList;
}
/**
* This event fires just after initializing {@link GuiScreen#mc}, {@link GuiScreen#fontRenderer},
* {@link GuiScreen#width}, and {@link GuiScreen#height}.<br/><br/>
*
* If canceled the following lines are skipped in {@link GuiScreen#setWorldAndResolution(Minecraft, int, int)}:<br/>
* {@code this.buttonList.clear();}<br/>
* {@code this.initGui();}<br/>
*/
@Cancelable
public static class Pre extends InitGuiEvent
{
public Pre(GuiScreen gui, List<GuiButton> buttonList)
{
super(gui, buttonList);
}
}
/**
* This event fires right after {@link GuiScreen#initGui()}.
* This is a good place to alter a GuiScreen's component layout if desired.
*/
public static class Post extends InitGuiEvent
{
public Post(GuiScreen gui, List<GuiButton> buttonList)
{
super(gui, buttonList);
}
}
}
public static class DrawScreenEvent extends GuiScreenEvent
{
private final int mouseX;
private final int mouseY;
private final float renderPartialTicks;
public DrawScreenEvent(GuiScreen gui, int mouseX, int mouseY, float renderPartialTicks)
{
super(gui);
this.mouseX = mouseX;
this.mouseY = mouseY;
this.renderPartialTicks = renderPartialTicks;
}
/**
* The x coordinate of the mouse pointer on the screen.
*/
public int getMouseX()
{
return mouseX;
}
/**
* The y coordinate of the mouse pointer on the screen.
*/
public int getMouseY()
{
return mouseY;
}
/**
* Partial render ticks elapsed.
*/
public float getRenderPartialTicks()
{
return renderPartialTicks;
}
/**
* This event fires just before {@link GuiScreen#drawScreen(int, int, float)} is called.
* Cancel this event to skip {@link GuiScreen#drawScreen(int, int, float)}.
*/
@Cancelable
public static class Pre extends DrawScreenEvent
{
public Pre(GuiScreen gui, int mouseX, int mouseY, float renderPartialTicks)
{
super(gui, mouseX, mouseY, renderPartialTicks);
}
}
/**
* This event fires just after {@link GuiScreen#drawScreen(int, int, float)} is called.
*/
public static class Post extends DrawScreenEvent
{
public Post(GuiScreen gui, int mouseX, int mouseY, float renderPartialTicks)
{
super(gui, mouseX, mouseY, renderPartialTicks);
}
}
}
/**
* This event fires at the end of {@link GuiScreen#drawDefaultBackground()} and before the rest of the Gui draws.
* This allows drawing next to Guis, above the background but below any tooltips.
*/
public static class BackgroundDrawnEvent extends GuiScreenEvent
{
private final int mouseX;
private final int mouseY;
public BackgroundDrawnEvent(GuiScreen gui)
{
super(gui);
final ScaledResolution scaledresolution = new ScaledResolution(gui.mc);
final int scaledWidth = scaledresolution.getScaledWidth();
final int scaledHeight = scaledresolution.getScaledHeight();
this.mouseX = Mouse.getX() * scaledWidth / gui.mc.displayWidth;
this.mouseY = scaledHeight - Mouse.getY() * scaledHeight / gui.mc.displayHeight - 1;
}
/**
* The x coordinate of the mouse pointer on the screen.
*/
public int getMouseX()
{
return mouseX;
}
/**
* The y coordinate of the mouse pointer on the screen.
*/
public int getMouseY()
{
return mouseY;
}
}
/**
* This event fires in {@link InventoryEffectRenderer#updateActivePotionEffects()}
* when potion effects are active and the gui wants to move over.
* Cancel this event to prevent the Gui from being moved.
*/
@Cancelable
public static class PotionShiftEvent extends GuiScreenEvent
{
public PotionShiftEvent(GuiScreen gui)
{
super(gui);
}
}
public static class ActionPerformedEvent extends GuiScreenEvent
{
private GuiButton button;
private List<GuiButton> buttonList;
public ActionPerformedEvent(GuiScreen gui, GuiButton button, List<GuiButton> buttonList)
{
super(gui);
this.setButton(button);
this.setButtonList(new ArrayList<GuiButton>(buttonList));
}
/**
* The button that was clicked.
*/
public GuiButton getButton()
{
return button;
}
public void setButton(GuiButton button)
{
this.button = button;
}
/**
* A COPY of the {@link #buttonList} field from the GuiScreen referenced by {@link #gui}.
*/
public List<GuiButton> getButtonList()
{
return buttonList;
}
public void setButtonList(List<GuiButton> buttonList)
{
this.buttonList = buttonList;
}
/**
* This event fires once it has been determined that a GuiButton object has been clicked.
* Cancel this event to bypass {@link GuiScreen#actionPerformed(GuiButton)}.
* Replace button with a different button from buttonList to have that button's action executed.
*/
@Cancelable
public static class Pre extends ActionPerformedEvent
{
public Pre(GuiScreen gui, GuiButton button, List<GuiButton> buttonList)
{
super(gui, button, buttonList);
}
}
/**
* This event fires after {@link GuiScreen#actionPerformed(GuiButton)} provided that the active
* screen has not been changed as a result of {@link GuiScreen#actionPerformed(GuiButton)}.
*/
public static class Post extends ActionPerformedEvent
{
public Post(GuiScreen gui, GuiButton button, List<GuiButton> buttonList)
{
super(gui, button, buttonList);
}
}
}
public static class MouseInputEvent extends GuiScreenEvent
{
public MouseInputEvent(GuiScreen gui)
{
super(gui);
}
/**
* This event fires when mouse input is detected by a GuiScreen.
* Cancel this event to bypass {@link GuiScreen#handleMouseInput()}.
*/
@Cancelable
public static class Pre extends MouseInputEvent
{
public Pre(GuiScreen gui)
{
super(gui);
}
}
/**
* This event fires after {@link GuiScreen#handleMouseInput()} provided that the active
* screen has not been changed as a result of {@link GuiScreen#handleMouseInput()} and
* the {@link GuiScreen#mouseHandled} flag has not been set.
* Cancel this event when you successfully use the mouse input to prevent other handlers from using the same input.
*/
@Cancelable
public static class Post extends MouseInputEvent
{
public Post(GuiScreen gui)
{
super(gui);
}
}
}
public static class KeyboardInputEvent extends GuiScreenEvent
{
public KeyboardInputEvent(GuiScreen gui)
{
super(gui);
}
/**
* This event fires when keyboard input is detected by a GuiScreen.
* Cancel this event to bypass {@link GuiScreen#handleKeyboardInput()}.
*/
@Cancelable
public static class Pre extends KeyboardInputEvent
{
public Pre(GuiScreen gui)
{
super(gui);
}
}
/**
* This event fires after {@link GuiScreen#handleKeyboardInput()} provided that the active
* screen has not been changed as a result of {@link GuiScreen#handleKeyboardInput()} and
* the {@link GuiScreen#keyHandled} flag has not been set.
* Cancel this event when you successfully use the keyboard input to prevent other handlers from using the same input.
*/
@Cancelable
public static class Post extends KeyboardInputEvent
{
public Post(GuiScreen gui)
{
super(gui);
}
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.MovementInput;
import net.minecraftforge.event.entity.player.PlayerEvent;
/**
* This event is fired after player movement inputs are updated.<br>
* Handlers can freely manipulate {@link MovementInput} to cancel movement.<br>
*/
public class InputUpdateEvent extends PlayerEvent
{
private final MovementInput movementInput;
public InputUpdateEvent(EntityPlayer player, MovementInput movementInput)
{
super(player);
this.movementInput = movementInput;
}
public MovementInput getMovementInput()
{
return movementInput;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelManager;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.util.registry.IRegistry;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Fired when the ModelManager is notified of the resource manager reloading.
* Called after model registry is setup, but before it's passed to BlockModelShapes.
*/
// TODO: try to merge with ICustomModelLoader
public class ModelBakeEvent extends Event
{
private final ModelManager modelManager;
private final IRegistry<ModelResourceLocation, IBakedModel> modelRegistry;
private final ModelLoader modelLoader;
public ModelBakeEvent(ModelManager modelManager, IRegistry<ModelResourceLocation, IBakedModel> modelRegistry, ModelLoader modelLoader)
{
this.modelManager = modelManager;
this.modelRegistry = modelRegistry;
this.modelLoader = modelLoader;
}
public ModelManager getModelManager()
{
return modelManager;
}
public IRegistry<ModelResourceLocation, IBakedModel> getModelRegistry()
{
return modelRegistry;
}
public ModelLoader getModelLoader()
{
return modelLoader;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.IContextSetter;
/**
* Fired when the {@link net.minecraftforge.client.model.ModelLoader} is ready to receive registrations
*/
public class ModelRegistryEvent extends Event implements IContextSetter
{
}

View File

@@ -0,0 +1,63 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import org.lwjgl.input.Mouse;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Author: MachineMuse (Claire Semple)
* Created: 2:46 PM, 9/4/13
*/
@Cancelable
public class MouseEvent extends Event
{
private final int x;
private final int y;
private final int dx;
private final int dy;
private final int dwheel;
private final int button;
private final boolean buttonstate;
private final long nanoseconds;
public MouseEvent()
{
this.x = Mouse.getEventX();
this.y = Mouse.getEventY();
this.dx = Mouse.getEventDX();
this.dy = Mouse.getEventDY();
this.dwheel = Mouse.getEventDWheel();
this.button = Mouse.getEventButton();
this.buttonstate = Mouse.getEventButtonState();
this.nanoseconds = Mouse.getEventNanoseconds();
}
public int getX() { return x; }
public int getY() { return y; }
public int getDx() { return dx; }
public int getDy() { return dy; }
public int getDwheel() { return dwheel; }
public int getButton() { return button; }
public boolean isButtonstate() { return buttonstate; }
public long getNanoseconds() { return nanoseconds; }
}

View File

@@ -0,0 +1,47 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import javax.annotation.Nonnull;
/**
* This event is called before the pushOutOfBlocks calls in EntityPlayerSP.
*
* Cancelling the event will prevent pushOutOfBlocks from being called.
*/
@Cancelable
public class PlayerSPPushOutOfBlocksEvent extends PlayerEvent
{
private AxisAlignedBB entityBoundingBox;
public PlayerSPPushOutOfBlocksEvent(EntityPlayer player, AxisAlignedBB entityBoundingBox)
{
super(player);
this.entityBoundingBox = entityBoundingBox;
}
public AxisAlignedBB getEntityBoundingBox() { return entityBoundingBox; }
public void setEntityBoundingBox(@Nonnull AxisAlignedBB entityBoundingBox) { this.entityBoundingBox = entityBoundingBox; }
}

View File

@@ -0,0 +1,75 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Called when a block's texture is going to be overlaid on the player's HUD. Cancel this event to prevent the overlay.
*/
@Cancelable
public class RenderBlockOverlayEvent extends Event {
public static enum OverlayType {
FIRE, BLOCK, WATER
}
private final EntityPlayer player;
private final float renderPartialTicks;
private final OverlayType overlayType;
private final IBlockState blockForOverlay;
private final BlockPos blockPos;
@Deprecated
public RenderBlockOverlayEvent(EntityPlayer player, float renderPartialTicks, OverlayType type, Block block, int x, int y, int z)
{
this(player, renderPartialTicks, type, block.getDefaultState(), new BlockPos(x, y, z));
}
public RenderBlockOverlayEvent(EntityPlayer player, float renderPartialTicks, OverlayType type, IBlockState block, BlockPos blockPos)
{
this.player = player;
this.renderPartialTicks = renderPartialTicks;
this.overlayType = type;
this.blockForOverlay = block;
this.blockPos = blockPos;
}
/**
* The player which the overlay will apply to
*/
public EntityPlayer getPlayer() { return player; }
public float getRenderPartialTicks() { return renderPartialTicks; }
/**
* The type of overlay to occur
*/
public OverlayType getOverlayType() { return overlayType; }
/**
* If the overlay type is BLOCK, then this is the block which the overlay is getting it's icon from
*/
public IBlockState getBlockForOverlay() { return blockForOverlay; }
public BlockPos getBlockPos() { return blockPos; }
}

View File

@@ -0,0 +1,219 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import java.util.ArrayList;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.gui.BossInfoClient;
import net.minecraft.client.gui.ScaledResolution;
@Cancelable
public class RenderGameOverlayEvent extends Event
{
public float getPartialTicks()
{
return partialTicks;
}
public ScaledResolution getResolution()
{
return resolution;
}
public ElementType getType()
{
return type;
}
public static enum ElementType
{
ALL,
HELMET,
PORTAL,
CROSSHAIRS,
BOSSHEALTH, // All boss bars
BOSSINFO, // Individual boss bar
ARMOR,
HEALTH,
FOOD,
AIR,
HOTBAR,
EXPERIENCE,
TEXT,
HEALTHMOUNT,
JUMPBAR,
CHAT,
PLAYER_LIST,
DEBUG,
POTION_ICONS,
SUBTITLES,
FPS_GRAPH,
VIGNETTE
}
private final float partialTicks;
private final ScaledResolution resolution;
private final ElementType type;
public RenderGameOverlayEvent(float partialTicks, ScaledResolution resolution)
{
this.partialTicks = partialTicks;
this.resolution = resolution;
this.type = null;
}
private RenderGameOverlayEvent(RenderGameOverlayEvent parent, ElementType type)
{
this.partialTicks = parent.getPartialTicks();
this.resolution = parent.getResolution();
this.type = type;
}
public static class Pre extends RenderGameOverlayEvent
{
public Pre(RenderGameOverlayEvent parent, ElementType type)
{
super(parent, type);
}
}
public static class Post extends RenderGameOverlayEvent
{
public Post(RenderGameOverlayEvent parent, ElementType type)
{
super(parent, type);
}
@Override public boolean isCancelable(){ return false; }
}
public static class BossInfo extends Pre
{
private final BossInfoClient bossInfo;
private final int x;
private final int y;
private int increment;
public BossInfo(RenderGameOverlayEvent parent, ElementType type, BossInfoClient bossInfo, int x, int y, int increment)
{
super(parent, type);
this.bossInfo = bossInfo;
this.x = x;
this.y = y;
this.increment = increment;
}
/**
* @return The {@link BossInfoClient} currently being rendered
*/
public BossInfoClient getBossInfo()
{
return bossInfo;
}
/**
* @return The current x position we are rendering at
*/
public int getX()
{
return x;
}
/**
* @return The current y position we are rendering at
*/
public int getY()
{
return y;
}
/**
* @return How much to move down before rendering the next bar
*/
public int getIncrement()
{
return increment;
}
/**
* Sets the amount to move down before rendering the next bar
* @param increment The increment to set
*/
public void setIncrement(int increment)
{
this.increment = increment;
}
}
public static class Text extends Pre
{
private final ArrayList<String> left;
private final ArrayList<String> right;
public Text(RenderGameOverlayEvent parent, ArrayList<String> left, ArrayList<String> right)
{
super(parent, ElementType.TEXT);
this.left = left;
this.right = right;
}
public ArrayList<String> getLeft()
{
return left;
}
public ArrayList<String> getRight()
{
return right;
}
}
public static class Chat extends Pre
{
private int posX;
private int posY;
public Chat(RenderGameOverlayEvent parent, int posX, int posY)
{
super(parent, ElementType.CHAT);
this.setPosX(posX);
this.setPosY(posY);
}
public int getPosX()
{
return posX;
}
public void setPosX(int posX)
{
this.posX = posX;
}
public int getPosY()
{
return posY;
}
public void setPosY(int posY)
{
this.posY = posY;
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.renderer.RenderGlobal;
/**
* This event is fired on {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}
* before both hands are rendered.
* Canceling this event prevents either hand from being rendered,
* and prevents {@link RenderSpecificHandEvent} from firing.
* TODO This may get merged in 11 with RenderSpecificHandEvent to make a generic hand rendering
*/
@Cancelable
public class RenderHandEvent extends Event
{
private final RenderGlobal context;
private final float partialTicks;
private final int renderPass;
public RenderHandEvent(RenderGlobal context, float partialTicks, int renderPass)
{
this.context = context;
this.partialTicks = partialTicks;
this.renderPass = renderPass;
}
public RenderGlobal getContext()
{
return context;
}
public float getPartialTicks()
{
return partialTicks;
}
public int getRenderPass()
{
return renderPass;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.entity.RenderItemFrame;
import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import javax.annotation.Nonnull;
/**
* This event is called when an item is rendered in an item frame.
*
* You can set canceled to do no further vanilla processing.
*/
@Cancelable
public class RenderItemInFrameEvent extends Event
{
private final ItemStack item;
private final EntityItemFrame entityItemFrame;
private final RenderItemFrame renderer;
public RenderItemInFrameEvent(EntityItemFrame itemFrame, RenderItemFrame renderItemFrame)
{
item = itemFrame.getDisplayedItem();
entityItemFrame = itemFrame;
renderer = renderItemFrame;
}
@Nonnull
public ItemStack getItem()
{
return item;
}
public EntityItemFrame getEntityItemFrame()
{
return entityItemFrame;
}
public RenderItemFrame getRenderer()
{
return renderer;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
public abstract class RenderLivingEvent<T extends EntityLivingBase> extends Event
{
private final EntityLivingBase entity;
private final RenderLivingBase<T> renderer;
private final float partialRenderTick;
private final double x;
private final double y;
private final double z;
@Deprecated
public RenderLivingEvent(EntityLivingBase entity, RenderLivingBase<T> renderer, double x, double y, double z)
{
this(entity, renderer, 1, x, y, z);
}
public RenderLivingEvent(EntityLivingBase entity, RenderLivingBase<T> renderer, float partialRenderTick, double x, double y, double z)
{
this.entity = entity;
this.renderer = renderer;
this.partialRenderTick = partialRenderTick;
this.x = x;
this.y = y;
this.z = z;
}
public EntityLivingBase getEntity() { return entity; }
public RenderLivingBase<T> getRenderer() { return renderer; }
public float getPartialRenderTick() { return partialRenderTick; }
public double getX() { return x; }
public double getY() { return y; }
public double getZ() { return z; }
@Cancelable
public static class Pre<T extends EntityLivingBase> extends RenderLivingEvent<T>
{
@Deprecated
public Pre(EntityLivingBase entity, RenderLivingBase<T> renderer, double x, double y, double z){ super(entity, renderer, x, y, z); }
public Pre(EntityLivingBase entity, RenderLivingBase<T> renderer, float partialRenderTick, double x, double y, double z){ super(entity, renderer, partialRenderTick, x, y, z); }
}
public static class Post<T extends EntityLivingBase> extends RenderLivingEvent<T>
{
@Deprecated
public Post(EntityLivingBase entity, RenderLivingBase<T> renderer, double x, double y, double z){ super(entity, renderer, x, y, z); }
public Post(EntityLivingBase entity, RenderLivingBase<T> renderer, float partialRenderTick, double x, double y, double z){ super(entity, renderer, partialRenderTick, x, y, z); }
}
public abstract static class Specials<T extends EntityLivingBase> extends RenderLivingEvent<T>
{
public Specials(EntityLivingBase entity, RenderLivingBase<T> renderer, double x, double y, double z){ super(entity, renderer, 0, x, y, z); }
@Cancelable
public static class Pre<T extends EntityLivingBase> extends Specials<T>
{
public Pre(EntityLivingBase entity, RenderLivingBase<T> renderer, double x, double y, double z){ super(entity, renderer, x, y, z); }
}
public static class Post<T extends EntityLivingBase> extends Specials<T>
{
public Post(EntityLivingBase entity, RenderLivingBase<T> renderer, double x, double y, double z){ super(entity, renderer, x, y, z); }
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraftforge.event.entity.player.PlayerEvent;
import javax.annotation.Nonnull;
public abstract class RenderPlayerEvent extends PlayerEvent
{
private final RenderPlayer renderer;
private final float partialRenderTick;
private final double x;
private final double y;
private final double z;
public RenderPlayerEvent(EntityPlayer player, RenderPlayer renderer, float partialRenderTick, double x, double y, double z)
{
super(player);
this.renderer = renderer;
this.partialRenderTick = partialRenderTick;
this.x = x;
this.y = y;
this.z = z;
}
public RenderPlayer getRenderer() { return renderer; }
public float getPartialRenderTick() { return partialRenderTick; }
public double getX() { return x; }
public double getY() { return y; }
public double getZ() { return z; }
@Cancelable
public static class Pre extends RenderPlayerEvent
{
public Pre(EntityPlayer player, RenderPlayer renderer, float tick, double x, double y, double z){ super(player, renderer, tick, x, y, z); }
@Deprecated
public Pre(EntityPlayer player, RenderPlayer renderer, float tick){ this(player, renderer, tick, 0D, 0D, 0D); }
}
public static class Post extends RenderPlayerEvent
{
public Post(EntityPlayer player, RenderPlayer renderer, float tick, double x, double y, double z){ super(player, renderer, tick, x, y, z); }
@Deprecated
public Post(EntityPlayer player, RenderPlayer renderer, float tick){ this(player, renderer, tick, 0D, 0D, 0D); }
}
@Deprecated
public abstract static class Specials extends RenderPlayerEvent
{
public Specials(EntityPlayer player, RenderPlayer renderer, float partialTicks)
{
super(player, renderer, partialTicks, 0D, 0D, 0D);
}
@Cancelable
public static class Pre extends Specials
{
private boolean renderHelmet = true;
private boolean renderCape = true;
private boolean renderItem = true;
public Pre(EntityPlayer player, RenderPlayer renderer, float partialTicks){ super(player, renderer, partialTicks); }
public boolean shouldRenderHelmet() { return renderHelmet; }
public void setRenderHelmet(boolean renderHelmet) { this.renderHelmet = renderHelmet; }
public boolean shouldRenderCape() { return renderCape; }
public void setRenderCape(boolean renderCape) { this.renderCape = renderCape; }
public boolean shouldRenderItem() { return renderItem; }
public void setRenderItem(boolean renderItem) { this.renderItem = renderItem; }
}
public static class Post extends Specials
{
public Post(EntityPlayer player, RenderPlayer renderer, float partialTicks){ super(player, renderer, partialTicks); }
}
}
@Deprecated
public static class SetArmorModel extends RenderPlayerEvent
{
private int result = -1;
private final int slot;
@Nonnull
private final ItemStack stack;
public SetArmorModel(EntityPlayer player, RenderPlayer renderer, int slot, float partialTick, @Nonnull ItemStack stack)
{
super(player, renderer, partialTick, 0D, 0D, 0D);
this.slot = slot;
this.stack = stack;
}
/**
* Setting this to any value besides -1 will result in the function being
* Immediately exited with the return value specified.
*/
public int getResultValue()
{
return result;
}
public void setResult(int result)
{
this.result = result;
}
public int getSlot()
{
return slot;
}
@Nonnull
public ItemStack getStack()
{
return stack;
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import javax.annotation.Nonnull;
/**
* This event is fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}
* whenever a hand is rendered in first person.
* Canceling the event causes the hand to not render.
* TODO This may get merged in 11 with RenderHandEvent to make a generic hand rendering
*/
@Cancelable
public class RenderSpecificHandEvent extends Event
{
private final EnumHand hand;
private final float partialTicks;
private final float interpolatedPitch;
private final float swingProgress;
private final float equipProgress;
@Nonnull
private final ItemStack stack;
public RenderSpecificHandEvent(EnumHand hand, float partialTicks, float interpolatedPitch, float swingProgress, float equipProgress, @Nonnull ItemStack stack)
{
this.hand = hand;
this.partialTicks = partialTicks;
this.interpolatedPitch = interpolatedPitch;
this.swingProgress = swingProgress;
this.equipProgress = equipProgress;
this.stack = stack;
}
public EnumHand getHand()
{
return hand;
}
public float getPartialTicks()
{
return partialTicks;
}
/**
* @return The interpolated pitch of the player entity
*/
public float getInterpolatedPitch()
{
return interpolatedPitch;
}
/**
* @return The swing progress of the hand being rendered
*/
public float getSwingProgress()
{
return swingProgress;
}
/**
* @return The progress of the equip animation. 1.0 is fully equipped.
*/
public float getEquipProgress()
{
return equipProgress;
}
/**
* @return The ItemStack to be rendered, or null.
*/
@Nonnull
public ItemStack getItemStack()
{
return stack;
}
}

View File

@@ -0,0 +1,314 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.item.ItemStack;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* A set of events which are fired at various points during tooltip rendering.
* <p>
* Can be used to change the rendering parameters, draw something extra, etc.
* <p>
* Do not use this event directly, use one of the subclasses:
* <ul>
* <li>{@link RenderTooltipEvent.Pre}</li>
* <li>{@link RenderTooltipEvent.PostBackground}</li>
* <li>{@link RenderTooltipEvent.PostText}</li>
* </ul>
*/
public abstract class RenderTooltipEvent extends Event
{
@Nonnull
protected final ItemStack stack;
protected final List<String> lines;
protected int x;
protected int y;
protected FontRenderer fr;
public RenderTooltipEvent(@Nonnull ItemStack stack, @Nonnull List<String> lines, int x, int y, @Nonnull FontRenderer fr)
{
this.stack = stack;
this.lines = Collections.unmodifiableList(lines); // Leave editing to ItemTooltipEvent
this.x = x;
this.y = y;
this.fr = fr;
}
/**
* @return The stack which the tooltip is being rendered for. As tooltips can be drawn without itemstacks, this stack may be empty.
*/
@Nonnull
public ItemStack getStack()
{
return stack;
}
/**
* The lines to be drawn. May change between {@link RenderTooltipEvent.Pre} and {@link RenderTooltipEvent.Post}.
*
* @return An <i>unmodifiable</i> list of strings. Use {@link ItemTooltipEvent} to modify tooltip text.
*/
@Nonnull
public List<String> getLines()
{
return lines;
}
/**
* @return The X position of the tooltip box. By default, the mouse X position.
*/
public int getX()
{
return x;
}
/**
* @return The Y position of the tooltip box. By default, the mouse Y position.
*/
public int getY()
{
return y;
}
/**
* @return The {@link FontRenderer} instance the current render is using.
*/
@Nonnull
public FontRenderer getFontRenderer()
{
return fr;
}
/**
* This event is fired before any tooltip calculations are done. It provides setters for all aspects of the tooltip, so the final render can be modified.
* <p>
* This event is {@link Cancelable}.
*/
@Cancelable
public static class Pre extends RenderTooltipEvent
{
private int screenWidth;
private int screenHeight;
private int maxWidth;
public Pre(@Nonnull ItemStack stack, @Nonnull List<String> lines, int x, int y, int screenWidth, int screenHeight, int maxWidth, @Nonnull FontRenderer fr)
{
super(stack, lines, x, y, fr);
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.maxWidth = maxWidth;
}
public int getScreenWidth()
{
return screenWidth;
}
public void setScreenWidth(int screenWidth)
{
this.screenWidth = screenWidth;
}
public int getScreenHeight()
{
return screenHeight;
}
public void setScreenHeight(int screenHeight)
{
this.screenHeight = screenHeight;
}
/**
* @return The max width the tooltip can be. Defaults to -1 (unlimited).
*/
public int getMaxWidth()
{
return maxWidth;
}
/**
* Sets the max width of the tooltip. Use -1 for unlimited.
*/
public void setMaxWidth(int maxWidth)
{
this.maxWidth = maxWidth;
}
/**
* Sets the {@link FontRenderer} to be used to render text.
*/
public void setFontRenderer(@Nonnull FontRenderer fr)
{
this.fr = fr;
}
/**
* Set the X origin of the tooltip.
*/
public void setX(int x)
{
this.x = x;
}
/**
* Set the Y origin of the tooltip.
*/
public void setY(int y)
{
this.y = y;
}
}
/**
* Events inheriting from this class are fired at different stages during the tooltip rendering.
* <p>
* Do not use this event directly, use one of its subclasses:
* <ul>
* <li>{@link RenderTooltipEvent.PostBackground}</li>
* <li>{@link RenderTooltipEvent.PostText}</li>
* </ul>
*/
protected static abstract class Post extends RenderTooltipEvent
{
private final int width;
private final int height;
public Post(@Nonnull ItemStack stack, @Nonnull List<String> textLines, int x, int y, @Nonnull FontRenderer fr, int width, int height)
{
super(stack, textLines, x, y, fr);
this.width = width;
this.height = height;
}
/**
* @return The width of the tooltip box. This is the width of the <i>inner</i> box, not including the border.
*/
public int getWidth()
{
return width;
}
/**
* @return The height of the tooltip box. This is the height of the <i>inner</i> box, not including the border.
*/
public int getHeight()
{
return height;
}
}
/**
* This event is fired directly after the tooltip background is drawn, but before any text is drawn.
*/
public static class PostBackground extends Post
{
public PostBackground(@Nonnull ItemStack stack, @Nonnull List<String> textLines, int x, int y, @Nonnull FontRenderer fr, int width, int height)
{ super(stack, textLines, x, y, fr, width, height); }
}
/**
* This event is fired directly after the tooltip text is drawn, but before the GL state is reset.
*/
public static class PostText extends Post
{
public PostText(@Nonnull ItemStack stack, @Nonnull List<String> textLines, int x, int y, @Nonnull FontRenderer fr, int width, int height)
{ super(stack, textLines, x, y, fr, width, height); }
}
/**
* This event is fired when the colours for the tooltip background are determined.
*/
public static class Color extends RenderTooltipEvent
{
private final int originalBackground;
private final int originalBorderStart;
private final int originalBorderEnd;
private int background;
private int borderStart;
private int borderEnd;
public Color(@Nonnull ItemStack stack, @Nonnull List<String> textLines, int x, int y, @Nonnull FontRenderer fr, int background, int borderStart,
int borderEnd)
{
super(stack, textLines, x, y, fr);
this.originalBackground = background;
this.originalBorderStart = borderStart;
this.originalBorderEnd = borderEnd;
this.background = background;
this.borderStart = borderStart;
this.borderEnd = borderEnd;
}
public int getBackground()
{
return background;
}
public void setBackground(int background)
{
this.background = background;
}
public int getBorderStart()
{
return borderStart;
}
public void setBorderStart(int borderStart)
{
this.borderStart = borderStart;
}
public int getBorderEnd()
{
return borderEnd;
}
public void setBorderEnd(int borderEnd)
{
this.borderEnd = borderEnd;
}
public int getOriginalBackground()
{
return originalBackground;
}
public int getOriginalBorderStart()
{
return originalBorderStart;
}
public int getOriginalBorderEnd()
{
return originalBorderEnd;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.renderer.RenderGlobal;
public class RenderWorldLastEvent extends Event
{
private final RenderGlobal context;
private final float partialTicks;
public RenderWorldLastEvent(RenderGlobal context, float partialTicks)
{
this.context = context;
this.partialTicks = partialTicks;
}
public RenderGlobal getContext()
{
return context;
}
public float getPartialTicks()
{
return partialTicks;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
import java.awt.image.BufferedImage;
import java.io.File;
/**
* This event is fired before and after a screenshot is taken
* This event is fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}
* This event is {@link Cancelable}
*
* {@link #screenshotFile} contains the file the screenshot will be/was saved to
* {@link #image} contains the {@link BufferedImage} that will be saved
* {@link #resultMessage} contains the {@link ITextComponent} to be returned. If {@code null}, the default vanilla message will be used instead
*/
@Cancelable
public class ScreenshotEvent extends Event
{
public static final ITextComponent DEFAULT_CANCEL_REASON = new TextComponentString("Screenshot canceled");
private BufferedImage image;
private File screenshotFile;
private ITextComponent resultMessage = null;
public ScreenshotEvent(BufferedImage image, File screenshotFile)
{
this.image = image;
this.screenshotFile = screenshotFile;
}
public BufferedImage getImage()
{
return image;
}
public File getScreenshotFile()
{
return screenshotFile;
}
public void setScreenshotFile(File screenshotFile)
{
this.screenshotFile = screenshotFile;
}
public ITextComponent getResultMessage()
{
return resultMessage;
}
public void setResultMessage(ITextComponent resultMessage)
{
this.resultMessage = resultMessage;
}
public ITextComponent getCancelMessage()
{
return getResultMessage() != null ? getResultMessage() : DEFAULT_CANCEL_REASON;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.renderer.texture.TextureMap;
public class TextureStitchEvent extends Event
{
private final TextureMap map;
public TextureStitchEvent(TextureMap map)
{
this.map = map;
}
public TextureMap getMap()
{
return map;
}
/**
* Fired when the TextureMap is told to refresh it's stitched texture.
* Called after the Stitched list is cleared, but before any blocks or items
* add themselves to the list.
*/
public static class Pre extends TextureStitchEvent
{
public Pre(TextureMap map){ super(map); }
}
/**
* This event is fired once the texture map has loaded all textures and
* stitched them together. All Icons should have there locations defined
* by the time this is fired.
*/
public static class Post extends TextureStitchEvent
{
public Post(TextureMap map){ super(map); }
}
}

View File

@@ -0,0 +1,64 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.SoundManager;
/***
* Raised when the SoundManager tries to play a normal sound.
*
* If you return null from this function it will prevent the sound from being played,
* you can return a different entry if you want to change the sound being played.
*/
public class PlaySoundEvent extends SoundEvent
{
private final String name;
private final ISound sound;
private ISound result;
public PlaySoundEvent(SoundManager manager, ISound sound)
{
super(manager);
this.sound = sound;
this.name = sound.getSoundLocation().getResourcePath();
this.setResultSound(sound);
}
public String getName()
{
return name;
}
public ISound getSound()
{
return sound;
}
public ISound getResultSound()
{
return result;
}
public void setResultSound(ISound result)
{
this.result = result;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.SoundManager;
import net.minecraftforge.client.event.sound.SoundEvent.SoundSourceEvent;
public class PlaySoundSourceEvent extends SoundSourceEvent
{
public PlaySoundSourceEvent(SoundManager manager, ISound sound, String uuid)
{
super(manager, sound, uuid);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.SoundManager;
import net.minecraftforge.client.event.sound.SoundEvent.SoundSourceEvent;
public class PlayStreamingSourceEvent extends SoundSourceEvent
{
public PlayStreamingSourceEvent(SoundManager manager, ISound sound, String uuid)
{
super(manager, sound, uuid);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event.sound;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.SoundManager;
public class SoundEvent extends Event
{
private final SoundManager manager;
public SoundEvent(SoundManager manager)
{
this.manager = manager;
}
public SoundManager getManager()
{
return manager;
}
public static class SoundSourceEvent extends SoundEvent
{
private final ISound sound;
private final String uuid;
private final String name;
public SoundSourceEvent(SoundManager manager, ISound sound, String uuid)
{
super(manager);
this.name = sound.getSoundLocation().getResourcePath();
this.sound = sound;
this.uuid = uuid;
}
public ISound getSound()
{
return sound;
}
public String getUuid()
{
return uuid;
}
public String getName()
{
return name;
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
/**
* Raised by the SoundManager.loadSoundSettings, this would be a good place for
* adding your custom sounds to the SoundPool.
*/
public class SoundLoadEvent extends SoundEvent
{
public SoundLoadEvent(SoundManager manager)
{
super(manager);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
/**
* This event is raised by the SoundManager when it does its first setup of the
* SoundSystemConfig's codecs, use this function to add your own codecs.
*/
public class SoundSetupEvent extends SoundEvent
{
public SoundSetupEvent(SoundManager manager)
{
super(manager);
}
}

View File

@@ -0,0 +1,437 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.gui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.client.IModGuiFactory;
import net.minecraftforge.fml.client.config.ConfigGuiType;
import net.minecraftforge.fml.client.config.DummyConfigElement;
import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement;
import net.minecraftforge.fml.client.config.GuiConfig;
import net.minecraftforge.fml.client.config.GuiConfigEntries;
import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry;
import net.minecraftforge.fml.client.config.GuiConfigEntries.IConfigEntry;
import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry;
import net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry;
import net.minecraftforge.fml.client.config.IConfigElement;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import static net.minecraftforge.common.ForgeModContainer.VERSION_CHECK_CAT;
import net.minecraftforge.fml.client.IModGuiFactory.RuntimeOptionCategoryElement;
/**
* This is the base GuiConfig screen class that all the other Forge-specific config screens will be called from.
* Since Forge has multiple config files I thought I would use that opportunity to show some of the ways
* that the config GUI system can be extended to create custom config GUIs that have additional features
* over the base functionality of just displaying Properties and ConfigCategories.
*
* The concepts implemented here are:
* - using custom IConfigEntry objects to define child-screens that have specific Properties listed
* - using custom IConfigEntry objects to define a dummy property that can be used to generate new ConfigCategory objects
* - defining the configID string for a GuiConfig object so that the config changed events will be posted when that GuiConfig screen is closed
* (the configID string is optional; if it is not defined the config changed events will be posted when the top-most GuiConfig screen
* is closed, eg when the parent is null or is not an instance of GuiConfig)
* - overriding the IConfigEntry.enabled() method to control the enabled state of one list entry based on the value of another entry
* - overriding the IConfigEntry.onGuiClosed() method to perform custom actions when the screen that owns the entry is closed (in this
* case a new ConfigCategory is added to the Configuration object)
*
* The config file structure looks like this:
* forge.cfg (general settings all in one category)
* forgeChunkLoading.cfg
* - Forge (category)
* - defaults (category)
* - [optional mod override categories]...
*
* The GUI structure is this:
* Base Screen
* - General Settings (from forge.cfg)
* - Chunk Loader Settings (from forgeChunkLoading.cfg)
* - Defaults (these elements are listed directly on this screen)
* - Mod Overrides
* - Add New Mod Override
* - Mod1
* - Mod2
* - etc.
*
* Other things to check out:
* ForgeModContainer.syncConfig()
* ForgeModContainer.onConfigChanged()
* ForgeChunkManager.syncConfigDefaults()
* ForgeChunkManager.loadConfiguration()
*/
public class ForgeGuiFactory implements IModGuiFactory
{
@Override
public void initialize(Minecraft minecraftInstance) {}
@Override
public boolean hasConfigGui() { return true; }
@Override
public GuiScreen createConfigGui(GuiScreen parent) { return new ForgeConfigGui(parent); }
@Override
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() { return null; }
public static class ForgeConfigGui extends GuiConfig
{
public ForgeConfigGui(GuiScreen parentScreen)
{
super(parentScreen, getConfigElements(), ForgeVersion.MOD_ID, false, false, I18n.format("forge.configgui.forgeConfigTitle"));
}
private static List<IConfigElement> getConfigElements()
{
List<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new DummyCategoryElement("forgeCfg", "forge.configgui.ctgy.forgeGeneralConfig", GeneralEntry.class));
list.add(new DummyCategoryElement("forgeClientCfg", "forge.configgui.ctgy.forgeClientConfig", ClientEntry.class));
list.add(new DummyCategoryElement("forgeChunkLoadingCfg", "forge.configgui.ctgy.forgeChunkLoadingConfig", ChunkLoaderEntry.class));
list.add(new DummyCategoryElement("forgeVersionCheckCfg", "forge.configgui.ctgy.VersionCheckConfig", VersionCheckEntry.class));
return list;
}
/**
* This custom list entry provides the General Settings entry on the Minecraft Forge Configuration screen.
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
*/
public static class GeneralEntry extends CategoryEntry
{
public GeneralEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
@Override
protected GuiScreen buildChildScreen()
{
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent
// GuiConfig object's entryList will also be refreshed to reflect the changes.
return new GuiConfig(this.owningScreen,
(new ConfigElement(ForgeModContainer.getConfig().getCategory(Configuration.CATEGORY_GENERAL))).getChildElements(),
this.owningScreen.modID, Configuration.CATEGORY_GENERAL, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart,
this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart,
GuiConfig.getAbridgedConfigPath(ForgeModContainer.getConfig().toString()));
}
}
/**
* This custom list entry provides the Client only Settings entry on the Minecraft Forge Configuration screen.
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
*/
public static class ClientEntry extends CategoryEntry
{
public ClientEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
@Override
protected GuiScreen buildChildScreen()
{
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent
// GuiConfig object's entryList will also be refreshed to reflect the changes.
return new GuiConfig(this.owningScreen,
(new ConfigElement(ForgeModContainer.getConfig().getCategory(Configuration.CATEGORY_CLIENT))).getChildElements(),
this.owningScreen.modID, Configuration.CATEGORY_CLIENT, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart,
this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart,
GuiConfig.getAbridgedConfigPath(ForgeModContainer.getConfig().toString()));
}
}
/**
* This custom list entry provides the Forge Chunk Manager Config entry on the Minecraft Forge Configuration screen.
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
*/
public static class ChunkLoaderEntry extends CategoryEntry
{
public ChunkLoaderEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
@Override
protected GuiScreen buildChildScreen()
{
List<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new DummyCategoryElement("forgeChunkLoadingModCfg", "forge.configgui.ctgy.forgeChunkLoadingModConfig",
ModOverridesEntry.class));
list.addAll((new ConfigElement(ForgeChunkManager.getDefaultsCategory())).getChildElements());
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent
// GuiConfig object's propertyList will also be refreshed to reflect the changes.
return new GuiConfig(this.owningScreen, list, this.owningScreen.modID, "chunkLoader",
this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart,
this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart,
GuiConfig.getAbridgedConfigPath(ForgeChunkManager.getConfig().toString()),
I18n.format("forge.configgui.ctgy.forgeChunkLoadingConfig"));
}
}
/**
* This custom list entry provides the Forge Version Checking Config entry on the Minecraft Forge Configuration screen.
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
*/
public static class VersionCheckEntry extends CategoryEntry
{
public VersionCheckEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
@Override
protected GuiScreen buildChildScreen()
{
ConfigCategory cfg = ForgeModContainer.getConfig().getCategory(VERSION_CHECK_CAT);
Map<String, Property> values = new HashMap<String, Property>(cfg.getValues());
values.remove("Global");
Property global = ForgeModContainer.getConfig().get(VERSION_CHECK_CAT, "Global", true);
List<Property> props = new ArrayList<Property>();
for (ModContainer mod : ForgeVersion.gatherMods().keySet())
{
values.remove(mod.getModId());
props.add(ForgeModContainer.getConfig().get(VERSION_CHECK_CAT, mod.getModId(), true)); //Get or make the value in the config
}
props.addAll(values.values()); // Add any left overs from the config
props.sort(Comparator.comparing(Property::getName));
List<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new ConfigElement(global));
for (Property prop : props)
{
list.add(new ConfigElement(prop));
}
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent
// GuiConfig object's propertyList will also be refreshed to reflect the changes.
return new GuiConfig(this.owningScreen,
list,
this.owningScreen.modID, VERSION_CHECK_CAT, true, true,
GuiConfig.getAbridgedConfigPath(ForgeModContainer.getConfig().toString()));
}
}
/**
* This custom list entry provides the Mod Overrides entry on the Forge Chunk Loading config screen.
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
* In this case it adds the custom entry for adding a new mod override and lists the existing mod overrides.
*/
public static class ModOverridesEntry extends CategoryEntry
{
public ModOverridesEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
/**
* This method is called in the constructor and is used to set the childScreen field.
*/
@Override
protected GuiScreen buildChildScreen()
{
List<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new DummyCategoryElement("addForgeChunkLoadingModCfg", "forge.configgui.ctgy.forgeChunkLoadingAddModConfig",
AddModOverrideEntry.class));
for (ConfigCategory cc : ForgeChunkManager.getModCategories())
list.add(new ConfigElement(cc));
return new GuiConfig(this.owningScreen, list, this.owningScreen.modID,
this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart,
this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, this.owningScreen.title,
I18n.format("forge.configgui.ctgy.forgeChunkLoadingModConfig"));
}
/**
* By overriding the enabled() method and checking the value of the "enabled" entry this entry is enabled/disabled based on the value of
* the other entry.
*/
@Override
public boolean enabled()
{
for (IConfigEntry entry : this.owningEntryList.listEntries)
{
if (entry.getName().equals("enabled") && entry instanceof BooleanEntry)
{
return Boolean.valueOf(entry.getCurrentValue().toString());
}
}
return true;
}
/**
* Check to see if the child screen's entry list has changed.
*/
@Override
public boolean isChanged()
{
if (childScreen instanceof GuiConfig)
{
GuiConfig child = (GuiConfig) childScreen;
return child.entryList.listEntries.size() != child.initEntries.size() || child.entryList.hasChangedEntry(true);
}
return false;
}
/**
* Since adding a new entry to the child screen is what constitutes a change here, reset the child
* screen listEntries to the saved list.
*/
@Override
public void undoChanges()
{
if (childScreen instanceof GuiConfig)
{
GuiConfig child = (GuiConfig) childScreen;
for (IConfigEntry ice : child.entryList.listEntries)
if (!child.initEntries.contains(ice) && ForgeChunkManager.getConfig().hasCategory(ice.getName()))
ForgeChunkManager.getConfig().removeCategory(ForgeChunkManager.getConfig().getCategory(ice.getName()));
child.entryList.listEntries = new ArrayList<IConfigEntry>(child.initEntries);
}
}
}
/**
* This custom list entry provides a button that will open to a screen that will allow a user to define a new mod override.
*/
public static class AddModOverrideEntry extends CategoryEntry
{
public AddModOverrideEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
@Override
protected GuiScreen buildChildScreen()
{
List<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new DummyConfigElement("modID", "", ConfigGuiType.STRING, "forge.configgui.modID").setCustomListEntryClass(ModIDEntry.class));
list.add(new ConfigElement(new Property("maximumTicketCount", "200", Property.Type.INTEGER, "forge.configgui.maximumTicketCount")));
list.add(new ConfigElement(new Property("maximumChunksPerTicket", "25", Property.Type.INTEGER, "forge.configgui.maximumChunksPerTicket")));
return new GuiConfig(this.owningScreen, list, this.owningScreen.modID,
this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart,
this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, this.owningScreen.title,
I18n.format("forge.configgui.ctgy.forgeChunkLoadingAddModConfig"));
}
@Override
public boolean isChanged()
{
return false;
}
}
/**
* This custom list entry provides a Mod ID selector. The control is a button that opens a list of values to select from.
* This entry also overrides onGuiClosed() to run code to save the data to a new ConfigCategory when the user is done.
*/
public static class ModIDEntry extends SelectValueEntry
{
public ModIDEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop, getSelectableValues());
if (this.selectableValues.size() == 0)
this.btnValue.enabled = false;
}
private static Map<Object, String> getSelectableValues()
{
Map<Object, String> selectableValues = new TreeMap<Object, String>();
for (ModContainer mod : Loader.instance().getActiveModList())
// only add mods to the list that have a non-immutable ModContainer
if (!mod.isImmutable() && mod.getMod() != null)
selectableValues.put(mod.getModId(), mod.getName());
return selectableValues;
}
/**
* By overriding onGuiClosed() for this entry we can perform additional actions when the user is done such as saving
* a new ConfigCategory object to the Configuration object.
*/
@Override
public void onGuiClosed()
{
Object modObject = Loader.instance().getModObjectList().get(Loader.instance().getIndexedModList().get(currentValue));
int maxTickets = 200;
int maxChunks = 25;
if (modObject != null)
{
this.owningEntryList.saveConfigElements();
for(IConfigElement ice : this.owningScreen.configElements)
if ("maximumTicketCount".equals(ice.getName()))
maxTickets = Integer.valueOf(ice.get().toString());
else if ("maximumChunksPerTicket".equals(ice.getName()))
maxChunks = Integer.valueOf(ice.get().toString());
ForgeChunkManager.addConfigProperty(modObject, "maximumTicketCount", String.valueOf(maxTickets), Property.Type.INTEGER);
ForgeChunkManager.addConfigProperty(modObject, "maximumChunksPerTicket", String.valueOf(maxChunks), Property.Type.INTEGER);
if (this.owningScreen.parentScreen instanceof GuiConfig)
{
GuiConfig superParent = (GuiConfig) this.owningScreen.parentScreen;
ConfigCategory modCtgy = ForgeChunkManager.getConfigFor(modObject);
modCtgy.setPropertyOrder(ForgeChunkManager.MOD_PROP_ORDER);
ConfigElement modConfig = new ConfigElement(modCtgy);
boolean found = false;
for (IConfigElement ice : superParent.configElements)
if (ice.getName().equals(currentValue))
found = true;
if (!found)
superParent.configElements.add(modConfig);
superParent.needsRefresh = true;
superParent.initGui();
}
}
}
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.gui;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.ForgeVersion.Status;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class NotificationModUpdateScreen extends GuiScreen
{
private static final ResourceLocation VERSION_CHECK_ICONS = new ResourceLocation(ForgeVersion.MOD_ID, "textures/gui/version_check_icons.png");
private final GuiButton modButton;
private Status showNotification = null;
private boolean hasCheckedForUpdates = false;
public NotificationModUpdateScreen(GuiButton modButton)
{
this.modButton = modButton;
}
/**
* Adds the buttons (and other controls) to the screen in question. Called when the GUI is displayed and when the
* window resizes, the buttonList is cleared beforehand.
*/
@Override
public void initGui()
{
if (!hasCheckedForUpdates)
{
if (modButton != null)
{
for (ModContainer mod : Loader.instance().getModList())
{
Status status = ForgeVersion.getResult(mod).status;
if (status == Status.OUTDATED || status == Status.BETA_OUTDATED)
{
// TODO: Needs better visualization, maybe stacked icons
// drawn in a terrace-like pattern?
showNotification = Status.OUTDATED;
}
}
}
hasCheckedForUpdates = true;
}
}
/**
* Draws the screen and all the components in it.
*/
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks)
{
if (showNotification == null || !showNotification.shouldDraw() || ForgeModContainer.disableVersionCheck)
{
return;
}
Minecraft.getMinecraft().getTextureManager().bindTexture(VERSION_CHECK_ICONS);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.pushMatrix();
int x = modButton.x;
int y = modButton.y;
int w = modButton.width;
int h = modButton.height;
drawModalRectWithCustomSizedTexture(x + w - (h / 2 + 4), y + (h / 2 - 4), showNotification.getSheetOffset() * 8, (showNotification.isAnimated() && ((System.currentTimeMillis() / 800 & 1) == 1)) ? 8 : 0, 8, 8, 64, 16);
GlStateManager.popMatrix();
}
public static NotificationModUpdateScreen init(GuiMainMenu guiMainMenu, GuiButton modButton)
{
NotificationModUpdateScreen notificationModUpdateScreen = new NotificationModUpdateScreen(modButton);
notificationModUpdateScreen.setGuiSize(guiMainMenu.width, guiMainMenu.height);
notificationModUpdateScreen.initGui();
return notificationModUpdateScreen;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumType;
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage;
public class Attributes
{
/*
* Default format of the data in IBakedModel
*/
public static final VertexFormat DEFAULT_BAKED_FORMAT;
static
{
DEFAULT_BAKED_FORMAT = new VertexFormat();
DEFAULT_BAKED_FORMAT.addElement(new VertexFormatElement(0, EnumType.FLOAT, EnumUsage.POSITION, 3));
DEFAULT_BAKED_FORMAT.addElement(new VertexFormatElement(0, EnumType.UBYTE, EnumUsage.COLOR, 4));
DEFAULT_BAKED_FORMAT.addElement(new VertexFormatElement(0, EnumType.FLOAT, EnumUsage.UV, 2));
DEFAULT_BAKED_FORMAT.addElement(new VertexFormatElement(0, EnumType.BYTE, EnumUsage.PADDING, 4));
}
/*
* Can first format be used where second is expected
*/
public static boolean moreSpecific(VertexFormat first, VertexFormat second)
{
int size = first.getNextOffset();
if(size != second.getNextOffset()) return false;
int padding = 0;
int j = 0;
for(VertexFormatElement firstAttr : first.getElements())
{
while(j < second.getElementCount() && second.getElement(j).getUsage() == EnumUsage.PADDING)
{
padding += second.getElement(j++).getSize();
}
if(j >= second.getElementCount() && padding == 0)
{
// if no padding is left, but there are still elements in first (we're processing one) - it doesn't fit
return false;
}
if(padding == 0)
{
// no padding - attributes have to match
VertexFormatElement secondAttr = second.getElement(j++);
if(
firstAttr.getIndex() != secondAttr.getIndex() ||
firstAttr.getElementCount() != secondAttr.getElementCount() ||
firstAttr.getType() != secondAttr.getType() ||
firstAttr.getUsage() != secondAttr.getUsage())
{
return false;
}
}
else
{
// padding - attribute should fit in it
padding -= firstAttr.getSize();
if(padding < 0) return false;
}
}
if(padding != 0 || j != second.getElementCount()) return false;
return true;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.model.TRSRTransformation;
import org.apache.commons.lang3.tuple.Pair;
public class BakedItemModel implements IBakedModel
{
protected final ImmutableList<BakedQuad> quads;
protected final TextureAtlasSprite particle;
protected final ImmutableMap<TransformType, TRSRTransformation> transforms;
protected final ItemOverrideList overrides;
protected final IBakedModel guiModel;
public BakedItemModel(ImmutableList<BakedQuad> quads, TextureAtlasSprite particle, ImmutableMap<TransformType, TRSRTransformation> transforms, ItemOverrideList overrides)
{
this.quads = quads;
this.particle = particle;
this.transforms = transforms;
this.overrides = overrides;
this.guiModel = hasGuiIdentity(transforms) ? new BakedGuiItemModel<>(this) : null;
}
private static boolean hasGuiIdentity(ImmutableMap<TransformType, TRSRTransformation> transforms)
{
TRSRTransformation guiTransform = transforms.get(TransformType.GUI);
return guiTransform == null || guiTransform.isIdentity();
}
@Override public boolean isAmbientOcclusion() { return true; }
@Override public boolean isGui3d() { return false; }
@Override public boolean isBuiltInRenderer() { return false; }
@Override public TextureAtlasSprite getParticleTexture() { return particle; }
@Override public ItemOverrideList getOverrides() { return overrides; }
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
if (side == null)
{
return quads;
}
return ImmutableList.of();
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType type)
{
if (type == TransformType.GUI && this.guiModel != null)
{
return this.guiModel.handlePerspective(type);
}
return PerspectiveMapWrapper.handlePerspective(this, transforms, type);
}
public static class BakedGuiItemModel<T extends BakedItemModel> extends BakedModelWrapper<T>
{
private final ImmutableList<BakedQuad> quads;
public BakedGuiItemModel(T originalModel)
{
super(originalModel);
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
for (BakedQuad quad : originalModel.quads)
{
if (quad.getFace() == EnumFacing.SOUTH)
{
builder.add(quad);
}
}
this.quads = builder.build();
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
if(side == null)
{
return quads;
}
return ImmutableList.of();
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType type)
{
if (type == TransformType.GUI)
{
return PerspectiveMapWrapper.handlePerspective(this, originalModel.transforms, type);
}
return this.originalModel.handlePerspective(type);
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import java.util.List;
import net.minecraft.block.state.IBlockState;
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.util.EnumFacing;
import org.apache.commons.lang3.tuple.Pair;
public abstract class BakedModelWrapper<T extends IBakedModel> implements IBakedModel
{
protected final T originalModel;
public BakedModelWrapper(T originalModel)
{
this.originalModel = originalModel;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
return originalModel.getQuads(state, side, rand);
}
@Override
public boolean isAmbientOcclusion()
{
return originalModel.isAmbientOcclusion();
}
@Override
public boolean isAmbientOcclusion(IBlockState state)
{
return originalModel.isAmbientOcclusion(state);
}
@Override
public boolean isGui3d()
{
return originalModel.isGui3d();
}
@Override
public boolean isBuiltInRenderer()
{
return originalModel.isBuiltInRenderer();
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return originalModel.getParticleTexture();
}
@Override
public ItemCameraTransforms getItemCameraTransforms()
{
return originalModel.getItemCameraTransforms();
}
@Override
public ItemOverrideList getOverrides()
{
return originalModel.getOverrides();
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
{
return originalModel.handlePerspective(cameraTransformType);
}
}

View File

@@ -0,0 +1,242 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.minecraft.client.renderer.block.model.ModelBlockDefinition;
import net.minecraft.client.renderer.block.model.ModelRotation;
import net.minecraft.client.renderer.block.model.Variant;
import net.minecraft.client.renderer.block.model.VariantList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javax.annotation.Nullable;
public class BlockStateLoader
{
private static final Gson GSON = (new GsonBuilder())
.registerTypeAdapter(ForgeBlockStateV1.class, ForgeBlockStateV1.Deserializer.INSTANCE)
.registerTypeAdapter(ForgeBlockStateV1.Variant.class, ForgeBlockStateV1.Variant.Deserializer.INSTANCE)
.registerTypeAdapter(TRSRTransformation.class, ForgeBlockStateV1.TRSRDeserializer.INSTANCE)
.create();
/**
* Loads a BlockStates json file.
* Will attempt to parse it as a Forge Enhanced version if possible.
* Will fall back to standard loading if marker is not present.
*
* Note: This method is NOT thread safe
*
* @param reader json read
* @param location blockstate location
* @param vanillaGSON ModelBlockDefinition's GSON reader.
*
* @return Model definition including variants for all known combinations.
*/
public static ModelBlockDefinition load(Reader reader, ResourceLocation location, final Gson vanillaGSON)
{
try
{
byte[] data = IOUtils.toByteArray(reader, StandardCharsets.UTF_8);
reader = new InputStreamReader(new ByteArrayInputStream(data), StandardCharsets.UTF_8);
Marker marker = GSON.fromJson(new String(data, StandardCharsets.UTF_8), Marker.class); // Read "forge_marker" to determine what to load.
switch (marker.forge_marker)
{
case 1: // Version 1
ForgeBlockStateV1 v1 = GSON.fromJson(reader, ForgeBlockStateV1.class);
Map<String, VariantList> variants = Maps.newLinkedHashMap();
for (Entry<String, Collection<ForgeBlockStateV1.Variant>> entry : v1.variants.asMap().entrySet())
{ // Convert Version1 variants into vanilla variants for the ModelBlockDefinition.
List<Variant> mcVars = Lists.newArrayList();
for (ForgeBlockStateV1.Variant var : entry.getValue())
{
boolean uvLock = var.getUvLock().orElse(false);
boolean smooth = var.getSmooth().orElse(true);
boolean gui3d = var.getGui3d().orElse(true);
int weight = var.getWeight().orElse(1);
if (var.getModel() != null && var.getSubmodels().size() == 0 && var.getTextures().size() == 0 && var.getCustomData().size() == 0 && var.getState().orElse(ModelRotation.X0_Y0) instanceof ModelRotation)
mcVars.add(new Variant(var.getModel(), (ModelRotation)var.getState().orElse(ModelRotation.X0_Y0), uvLock, weight));
else
mcVars.add(new ForgeVariant(location, var.getModel(), var.getState().orElse(TRSRTransformation.identity()), uvLock, smooth, gui3d, weight, var.getTextures(), var.getOnlyPartsVariant(), var.getCustomData()));
}
variants.put(entry.getKey(), new VariantList(mcVars));
}
return new ModelBlockDefinition(variants, null);
default: //Unknown version.. try loading it as normal.
return vanillaGSON.fromJson(reader, ModelBlockDefinition.class);
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public static class Marker
{
public int forge_marker = -1;
}
//This is here specifically so that we do not have a hard reference to ForgeBlockStateV1.Variant in ForgeVariant
public static class SubModel
{
private final IModelState state;
private final boolean uvLock;
private final boolean smooth;
private final boolean gui3d;
private final ImmutableMap<String, String> textures;
@Nullable
private final ResourceLocation model;
private final ImmutableMap<String, String> customData;
public SubModel(IModelState state, boolean uvLock, boolean smooth, boolean gui3d, ImmutableMap<String, String> textures, @Nullable ResourceLocation model, ImmutableMap<String, String> customData)
{
this.state = state;
this.uvLock = uvLock;
this.smooth = smooth;
this.gui3d = gui3d;
this.textures = textures;
this.model = model;
this.customData = customData;
}
public IModelState getState() { return state; }
public boolean isUVLock() { return uvLock; }
public ImmutableMap<String, String> getTextures() { return textures; }
@Nullable
public ResourceLocation getModelLocation() { return model; }
public ImmutableMap<String, String> getCustomData() { return customData; }
}
private static class ForgeVariant extends Variant implements ISmartVariant
{
private final ResourceLocation blockstateLocation;
private final ImmutableMap<String, String> textures;
private final ImmutableMap<String, SubModel> parts;
private final ImmutableMap<String, String> customData;
private final boolean smooth;
private final boolean gui3d;
private final IModelState state;
public ForgeVariant(ResourceLocation blockstateLocation, @Nullable ResourceLocation model, IModelState state, boolean uvLock, boolean smooth, boolean gui3d, int weight, ImmutableMap<String, String> textures, ImmutableMap<String, SubModel> parts, ImmutableMap<String, String> customData)
{
super(model == null ? new ResourceLocation("builtin/missing") : model, state instanceof ModelRotation ? (ModelRotation)state : ModelRotation.X0_Y0, uvLock, weight);
this.blockstateLocation = blockstateLocation;
this.textures = textures;
this.parts = parts;
this.customData = customData;
this.state = state;
this.smooth = smooth;
this.gui3d = gui3d;
}
private IModel runModelHooks(IModel base, boolean smooth, boolean gui3d, boolean uvlock, ImmutableMap<String, String> textureMap, ImmutableMap<String, String> customData)
{
base = base.process(customData);
base = base.retexture(textureMap);
base = base.smoothLighting(smooth);
base = base.gui3d(gui3d);
base = base.uvlock(uvlock);
return base;
}
/**
* Used to replace the base model with a re-textured model containing sub-models.
*/
@Override
public IModel process(IModel base)
{
int size = parts.size();
// FIXME: should missing base be handled this way?
boolean hasBase = base != ModelLoaderRegistry.getMissingModel();
if (hasBase)
{
base = runModelHooks(base, smooth, gui3d, this.isUvLock(), textures, customData);
if (size <= 0)
return base;
}
ImmutableMap.Builder<String, Pair<IModel, IModelState>> models = ImmutableMap.builder();
for (Entry<String, SubModel> entry : parts.entrySet())
{
SubModel part = entry.getValue();
final ResourceLocation modelLocation = part.getModelLocation();
final IModel model;
if (modelLocation == null)
{
FMLLog.log.error("model not found for variant {} for blockstate {}", entry.getKey(), blockstateLocation);
model = ModelLoaderRegistry.getMissingModel(blockstateLocation, new Throwable());
}
else
{
model = ModelLoaderRegistry.getModelOrLogError(modelLocation, "Unable to load block sub-model: \'" + modelLocation);
}
models.put(entry.getKey(), Pair.of(runModelHooks(model, part.smooth, part.gui3d, part.uvLock, part.getTextures(), part.getCustomData()), part.getState()));
}
return new MultiModel(getModelLocation(), hasBase ? base : null, models.build());
}
@Override
public IModelState getState()
{
return state;
}
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
buf.append("TexturedVariant:");
for (Entry<String, String> e: this.textures.entrySet())
buf.append(" ").append(e.getKey()).append(" = ").append(e.getValue());
return buf.toString();
}
}
}

View File

@@ -0,0 +1,232 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.function.Function;
import java.util.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<VertexFormat, SimpleModelFontRenderer> fontCache = CacheBuilder.newBuilder().maximumSize(3).build(new CacheLoader<VertexFormat, SimpleModelFontRenderer>()
{
@Override
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
) {
/**
* Render a single Unicode character at current (posX,posY) location using one of the /font/glyph_XX.png
* files...
*/
@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<ResourceLocation> getTextures()
{
return ImmutableList.of(font2);
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> 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));
}
static final class BakedModel implements IBakedModel
{
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<BakedQuad> 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<BakedQuad> 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<String> 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<BakedQuad> 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 ItemOverrideList getOverrides() { return ItemOverrideList.NONE; }
@Override
public Pair<? extends IBakedModel, Matrix4f> 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());
}
}
}

View File

@@ -0,0 +1,965 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ModelRotation;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.BlockStateLoader.Marker;
import net.minecraftforge.client.model.BlockStateLoader.SubModel;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.common.FMLLog;
import java.util.Objects;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
public class ForgeBlockStateV1 extends Marker
{
ForgeBlockStateV1.Variant defaults;
Multimap<String, ForgeBlockStateV1.Variant> variants = LinkedHashMultimap.create();
public static class Deserializer implements JsonDeserializer<ForgeBlockStateV1>
{
static ForgeBlockStateV1.Deserializer INSTANCE = new Deserializer();
@Override
public ForgeBlockStateV1 deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
JsonObject json = element.getAsJsonObject();
ForgeBlockStateV1 ret = new ForgeBlockStateV1();
ret.forge_marker = JsonUtils.getInt(json, "forge_marker");
if (json.has("defaults")) // Load defaults Variant.
{
ret.defaults = context.deserialize(json.get("defaults"), ForgeBlockStateV1.Variant.class);
if (ret.defaults.simpleSubmodels.size() > 0)
throw new RuntimeException("\"defaults\" variant cannot contain a simple \"submodel\" definition.");
}
Map<String, Map<String, ForgeBlockStateV1.Variant>> condensed = Maps.newLinkedHashMap(); // map(property name -> map(property value -> variant))
Multimap<String, ForgeBlockStateV1.Variant> specified = LinkedHashMultimap.create(); // Multimap containing all the states specified with "property=value".
for (Entry<String, JsonElement> e : JsonUtils.getJsonObject(json, "variants").entrySet())
{
if (e.getValue().isJsonArray())
{
// array of fully-defined variants
for (JsonElement a : e.getValue().getAsJsonArray())
{
Variant.Deserializer.INSTANCE.simpleSubmodelKey = e.getKey();
specified.put(e.getKey(), context.deserialize(a, Variant.class));
}
}
else
{
JsonObject obj = e.getValue().getAsJsonObject();
if(obj.entrySet().iterator().next().getValue().isJsonObject())
{
// first value is a json object, so we know it's not a fully-defined variant
Map<String, ForgeBlockStateV1.Variant> subs = Maps.newLinkedHashMap();
condensed.put(e.getKey(), subs);
for (Entry<String, JsonElement> se : e.getValue().getAsJsonObject().entrySet())
{
Variant.Deserializer.INSTANCE.simpleSubmodelKey = e.getKey() + "=" + se.getKey();
subs.put(se.getKey(), context.deserialize(se.getValue(), Variant.class));
}
}
else
{
// fully-defined variant
Variant.Deserializer.INSTANCE.simpleSubmodelKey = e.getKey();
specified.put(e.getKey(), context.deserialize(e.getValue(), Variant.class));
}
}
}
Multimap<String, ForgeBlockStateV1.Variant> permutations = getPermutations(condensed); // Get permutations of Forge-style states.
for (Entry<String, Collection<ForgeBlockStateV1.Variant>> e : specified.asMap().entrySet())
{ // Make fully-specified variants override Forge variant permutations, inheriting the permutations' values.
Collection<ForgeBlockStateV1.Variant> baseVars = permutations.get(e.getKey());
List<ForgeBlockStateV1.Variant> addVars = Lists.newArrayList();
for (ForgeBlockStateV1.Variant specVar : e.getValue())
{
if (!baseVars.isEmpty())
{
for (ForgeBlockStateV1.Variant baseVar : baseVars)
addVars.add(new Variant(specVar).sync(baseVar));
}
else
addVars.add(specVar);
}
baseVars.clear();
baseVars.addAll(addVars);
}
for (Entry<String, ForgeBlockStateV1.Variant> e : permutations.entries()) // Create the output map(state -> Variant).
{
ForgeBlockStateV1.Variant v = e.getValue();
if (ret.defaults != null)
{
v.sync(ret.defaults); // Sync defaults into all permutation variants
for (Entry<String, Object> partKey : v.simpleSubmodels.entrySet())
{ // Sync variant values (including defaults) into simple submodel declarations.
if (partKey.getValue() == null)
continue;
if (!v.submodels.containsKey(partKey.getKey()))
throw new RuntimeException("This should never happen! Simple submodel is not contained in the submodel map!");
List<ForgeBlockStateV1.Variant> partList = v.submodels.get(partKey.getKey());
if (partList.size() > 1)
throw new RuntimeException("This should never happen! Simple submodel has multiple variants!");
ForgeBlockStateV1.Variant part = partList.get(0);
// Must keep old rotation for the part, because the base variant's rotation is applied to the parts already.
Optional<IModelState> state = part.state;
part.sync(v);
part.simpleSubmodels.clear();
part.state = state;
}
}
v.submodels.values().removeIf(Objects::isNull);
if (v.textures != null)
{
for (Entry<String, String> tex : v.textures.entrySet())
{
if (tex.getValue() != null && tex.getValue().charAt(0) == '#')
{
String value = v.textures.get(tex.getValue().substring(1));
if (value == null)
{
FMLLog.log.fatal("Could not resolve texture name \"{}\" for permutation \"{}\"", tex.getValue(), e.getKey());
for (Entry<String, String> t: v.textures.entrySet())
FMLLog.log.fatal("{}={}", t.getKey(), t.getValue());
throw new JsonParseException("Could not resolve texture name \"" + tex.getValue() + "\" for permutation \"" + e.getKey() + "\"");
}
v.textures.put(tex.getKey(), value);
}
}
for (List<ForgeBlockStateV1.Variant> part : v.submodels.values()) // Sync base variant's textures (including defaults) into all submodels.
{
for (ForgeBlockStateV1.Variant partVar : part)
{
for (Entry<String, String> texEntry : v.textures.entrySet())
{
if (!partVar.textures.containsKey(texEntry.getKey()))
partVar.textures.put(texEntry.getKey(), texEntry.getValue());
}
}
}
}
if (!v.submodels.isEmpty())
ret.variants.putAll(e.getKey(), getSubmodelPermutations(v, v.submodels)); // Do permutations of submodel variants.
else
ret.variants.put(e.getKey(), v);
}
return ret;
}
private Multimap<String, ForgeBlockStateV1.Variant> getPermutations(List<String> sorted, Map<String, Map<String, ForgeBlockStateV1.Variant>> base, int depth, String prefix, Multimap<String, ForgeBlockStateV1.Variant> ret, @Nullable ForgeBlockStateV1.Variant parent)
{
if (depth == sorted.size())
{
if(parent != null)
ret.put(prefix, parent);
return ret;
}
String name = sorted.get(depth);
for (Entry<String, ForgeBlockStateV1.Variant> e : base.get(name).entrySet())
{
ForgeBlockStateV1.Variant newHead = parent == null ? new Variant(e.getValue()) : new Variant(parent).sync(e.getValue());
getPermutations(sorted, base, depth + 1, prefix + (depth == 0 ? "" : ",") + name + "=" + e.getKey(), ret, newHead);
}
return ret;
}
private Multimap<String, ForgeBlockStateV1.Variant> getPermutations(Map<String, Map<String, ForgeBlockStateV1.Variant>> base)
{
List<String> sorted = Lists.newArrayList(base.keySet());
Collections.sort(sorted); // Sort to get consistent results.
return getPermutations(sorted, base, 0, "", LinkedHashMultimap.create(), null);
}
private List<ForgeBlockStateV1.Variant> getSubmodelPermutations(ForgeBlockStateV1.Variant baseVar, List<String> sorted, Map<String, List<ForgeBlockStateV1.Variant>> map, int depth, Map<String, ForgeBlockStateV1.Variant> parts, List<ForgeBlockStateV1.Variant> ret)
{
if (depth >= sorted.size())
{
ForgeBlockStateV1.Variant add = new Variant(baseVar); // Create a duplicate variant object so modifying it doesn't modify baseVar.
for (Entry<String, ForgeBlockStateV1.Variant> part : parts.entrySet()) // Put all the parts with single variants for this permutation.
add.submodels.put(part.getKey(), Collections.singletonList(part.getValue()));
ret.add(add);
return ret;
}
String name = sorted.get(depth);
List<ForgeBlockStateV1.Variant> vars = map.get(sorted.get(depth));
if (vars != null)
{
for (ForgeBlockStateV1.Variant v : vars)
{
if (v != null)
{ // We put this part variant in the permutation's map to add further in recursion, and then remove it afterward just in case.
parts.put(name, v);
getSubmodelPermutations(baseVar, sorted, map, depth + 1, parts, ret);
parts.remove(name);
}
}
}
else
{
getSubmodelPermutations(baseVar, sorted, map, depth + 1, parts, ret);
}
return ret;
}
private List<ForgeBlockStateV1.Variant> getSubmodelPermutations(ForgeBlockStateV1.Variant baseVar, Map<String, List<ForgeBlockStateV1.Variant>> variants)
{
List<String> sorted = Lists.newArrayList(variants.keySet());
Collections.sort(sorted); // Sort to get consistent results.
return getSubmodelPermutations(baseVar, sorted, variants, 0, new LinkedHashMap<>(), new ArrayList<>());
}
}
public static class Variant
{
public static final Object SET_VALUE = new Object();
@Nullable
private ResourceLocation model = null;
private boolean modelSet = false;
private Optional<IModelState> state = Optional.empty();
private Optional<Boolean> uvLock = Optional.empty();
private Optional<Boolean> smooth = Optional.empty();
private Optional<Boolean> gui3d = Optional.empty();
private Optional<Integer> weight = Optional.empty();
private Map<String, String> textures = Maps.newHashMap();
private Map<String, List<ForgeBlockStateV1.Variant>> submodels = Maps.newHashMap();
private Map<String, Object> simpleSubmodels = Maps.newHashMap(); // Makeshift Set to allow us to "remove" (replace value with null) singleParts when needed.
private Map<String, String> customData = Maps.newHashMap();
private Variant(){}
/**
* Clone a variant.
* @param other Variant to clone.
*/
private Variant(ForgeBlockStateV1.Variant other)
{
this.model = other.model;
this.modelSet = other.modelSet;
this.state = other.state;
this.uvLock = other.uvLock;
this.smooth = other.smooth;
this.gui3d = other.gui3d;
this.weight = other.weight;
this.textures.putAll(other.textures);
this.mergeModelPartVariants(this.submodels, other.submodels);
this.simpleSubmodels.putAll(other.simpleSubmodels);
this.customData.putAll(other.customData);
}
/**
* Sets values in this variant to the input's values only if they haven't been set already. Essentially inherits values from o.
*/
ForgeBlockStateV1.Variant sync(ForgeBlockStateV1.Variant parent)
{
if (!this.modelSet)
{
this.model = parent.model;
this.modelSet = parent.modelSet;
}
if (!this.state.isPresent()) this.state = parent.state;
if (!this.uvLock.isPresent()) this.uvLock = parent.uvLock;
if (!this.smooth.isPresent()) this.smooth = parent.smooth;
if (!this.gui3d.isPresent()) this.gui3d = parent.gui3d;
if (!this.weight.isPresent()) this.weight = parent.weight;
for (Entry<String, String> e : parent.textures.entrySet())
{
if (!this.textures.containsKey(e.getKey()))
this.textures.put(e.getKey(), e.getValue());
}
mergeModelPartVariants(this.submodels, parent.submodels);
for (Entry<String, Object> e : parent.simpleSubmodels.entrySet())
{
if (!this.simpleSubmodels.containsKey(e.getKey()))
this.simpleSubmodels.put(e.getKey(), e.getValue());
}
for (Entry<String, String> e : parent.customData.entrySet())
{
if (!this.customData.containsKey(e.getKey()))
this.customData.put(e.getKey(), e.getValue());
}
return this;
}
/**
* Inherits model parts from a parent, creating deep clones of all Variants.
*/
Map<String, List<ForgeBlockStateV1.Variant>> mergeModelPartVariants(Map<String, List<ForgeBlockStateV1.Variant>> output, Map<String, List<ForgeBlockStateV1.Variant>> input)
{
for (Entry<String, List<ForgeBlockStateV1.Variant>> e : input.entrySet())
{
String key = e.getKey();
if (!output.containsKey(key))
{
List<ForgeBlockStateV1.Variant> variants = e.getValue();
if (variants != null)
{
List<ForgeBlockStateV1.Variant> newVariants = Lists.newArrayListWithCapacity(variants.size());
for (ForgeBlockStateV1.Variant v : variants)
newVariants.add(new Variant(v));
output.put(key, newVariants);
}
else
output.put(key, variants);
}
}
return output;
}
protected SubModel asGenericSubModel()
{
return new SubModel(state.orElse(TRSRTransformation.identity()), uvLock.orElse(false), smooth.orElse(true), gui3d.orElse(true), getTextures(), model, getCustomData());
}
/**
* Gets a list containing the single variant of each part.
* Will throw an error if this Variant has multiple variants for a submodel.
*/
public ImmutableMap<String, SubModel> getOnlyPartsVariant()
{
if (submodels.size() > 0)
{
ImmutableMap.Builder<String, SubModel> builder = ImmutableMap.builder();
for (Entry<String, List<ForgeBlockStateV1.Variant>> entry : submodels.entrySet())
{
List<ForgeBlockStateV1.Variant> part = entry.getValue();
if (part != null)
{
if (part.size() == 1)
builder.put(entry.getKey(), part.get(0).asGenericSubModel());
else
throw new RuntimeException("Something attempted to get the list of submodels "
+ "for a variant with model \"" + model + "\", when this variant "
+ "contains multiple variants for submodel " + entry.getKey());
}
}
return builder.build();
}
else {
return ImmutableMap.of();
}
}
public Optional<Boolean> getSmooth()
{
return smooth;
}
public Optional<Boolean> getGui3d()
{
return gui3d;
}
public static class Deserializer implements JsonDeserializer<ForgeBlockStateV1.Variant>
{
static Variant.Deserializer INSTANCE = new Deserializer();
/** Used <i>once</i> (then set null) for the key to put a simple submodel declaration under in the submodel map. */
public String simpleSubmodelKey = null;
protected ResourceLocation getBlockLocation(String location)
{
ResourceLocation tmp = new ResourceLocation(location);
return new ResourceLocation(tmp.getResourceDomain(), "block/" + tmp.getResourcePath());
}
/** Throws an error if there are submodels in this submodel. */
private void throwIfNestedSubmodels(ForgeBlockStateV1.Variant submodel)
{
if (submodel.submodels.size() > 0)
throw new UnsupportedOperationException("Forge BlockStateLoader V1 does not support nested submodels.");
}
private static TRSRTransformation get(float tx, float ty, float tz, float ax, float ay, float az, float s)
{
return TRSRTransformation.blockCenterToCorner(new TRSRTransformation(
new Vector3f(tx / 16, ty / 16, tz / 16),
TRSRTransformation.quatFromXYZDegrees(new Vector3f(ax, ay, az)),
new Vector3f(s, s, s),
null));
}
private static final TRSRTransformation flipX = new TRSRTransformation(null, null, new Vector3f(-1, 1, 1), null);
private static TRSRTransformation leftify(TRSRTransformation transform)
{
return TRSRTransformation.blockCenterToCorner(flipX.compose(TRSRTransformation.blockCornerToCenter(transform)).compose(flipX));
}
// Note: these strings might change to a full-blown resource locations in the future, and move from here to some json somewhere
// TODO: vanilla now includes from parent, deprecate?
private static final ImmutableMap<String, IModelState> transforms;
static
{
ImmutableMap.Builder<String, IModelState> builder = ImmutableMap.builder();
builder.put("identity", TRSRTransformation.identity());
// block/block
{
EnumMap<TransformType, TRSRTransformation> map = new EnumMap<>(TransformType.class);
TRSRTransformation thirdperson = get(0, 2.5f, 0, 75, 45, 0, 0.375f);
map.put(TransformType.GUI, get(0, 0, 0, 30, 225, 0, 0.625f));
map.put(TransformType.GROUND, get(0, 3, 0, 0, 0, 0, 0.25f));
map.put(TransformType.FIXED, get(0, 0, 0, 0, 0, 0, 0.5f));
map.put(TransformType.THIRD_PERSON_RIGHT_HAND, thirdperson);
map.put(TransformType.THIRD_PERSON_LEFT_HAND, leftify(thirdperson));
map.put(TransformType.FIRST_PERSON_RIGHT_HAND, get(0, 0, 0, 0, 45, 0, 0.4f));
map.put(TransformType.FIRST_PERSON_LEFT_HAND, get(0, 0, 0, 0, 225, 0, 0.4f));
builder.put("forge:default-block", new SimpleModelState(ImmutableMap.copyOf(map)));
}
// item/generated
{
EnumMap<TransformType, TRSRTransformation> map = new EnumMap<>(TransformType.class);
TRSRTransformation thirdperson = get(0, 3, 1, 0, 0, 0, 0.55f);
TRSRTransformation firstperson = get(1.13f, 3.2f, 1.13f, 0, -90, 25, 0.68f);
map.put(TransformType.GROUND, get(0, 2, 0, 0, 0, 0, 0.5f));
map.put(TransformType.HEAD, get(0, 13, 7, 0, 180, 0, 1));
map.put(TransformType.THIRD_PERSON_RIGHT_HAND, thirdperson);
map.put(TransformType.THIRD_PERSON_LEFT_HAND, leftify(thirdperson));
map.put(TransformType.FIRST_PERSON_RIGHT_HAND, firstperson);
map.put(TransformType.FIRST_PERSON_LEFT_HAND, leftify(firstperson));
map.put(TransformType.FIXED, get(0, 0, 0, 0, 180, 0, 1));
builder.put("forge:default-item", new SimpleModelState(ImmutableMap.copyOf(map)));
}
// item/handheld
{
EnumMap<TransformType, TRSRTransformation> map = new EnumMap<>(TransformType.class);
map.put(TransformType.GROUND, get(0, 2, 0, 0, 0, 0, 0.5f));
map.put(TransformType.HEAD, get(0, 13, 7, 0, 180, 0, 1));
map.put(TransformType.THIRD_PERSON_RIGHT_HAND, get(0, 4, 0.5f, 0, -90, 55, 0.85f));
map.put(TransformType.THIRD_PERSON_LEFT_HAND, get(0, 4, 0.5f, 0, 90, -55, 0.85f));
map.put(TransformType.FIRST_PERSON_RIGHT_HAND, get(1.13f, 3.2f, 1.13f, 0, -90, 25, 0.68f));
map.put(TransformType.FIRST_PERSON_LEFT_HAND, get(1.13f, 3.2f, 1.13f, 0, 90, -25, 0.68f));
map.put(TransformType.FIXED, get(0, 0, 0, 0, 180, 0, 1));
builder.put("forge:default-tool", new SimpleModelState(ImmutableMap.copyOf(map)));
}
transforms = builder.build();
}
@Override
public ForgeBlockStateV1.Variant deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
ForgeBlockStateV1.Variant ret = new Variant();
JsonObject json = element.getAsJsonObject();
if (json.has("model"))
{ // Load base model location.
if (json.get("model").isJsonNull())
ret.model = null; // Allow overriding base model to remove it from a state.
else
ret.model = getBlockLocation(JsonUtils.getString(json, "model"));
ret.modelSet = true;
}
if (json.has("textures"))
{ // Load textures map.
for (Entry<String, JsonElement> e : json.get("textures").getAsJsonObject().entrySet())
{
if (e.getValue().isJsonNull())
ret.textures.put(e.getKey(), ""); // We have to use "" because ImmutableMaps don't allow nulls -.-
else
ret.textures.put(e.getKey(), e.getValue().getAsString());
}
}
if (json.has("x") || json.has("y"))
{ // Load rotation values.
int x = JsonUtils.getInt(json, "x", 0);
int y = JsonUtils.getInt(json, "y", 0);
ret.state = Optional.ofNullable(ModelRotation.getModelRotation(x, y));
if (!ret.state.isPresent())
throw new JsonParseException("Invalid BlockModelRotation x: " + x + " y: " + y);
}
if (json.has("transform"))
{
if (json.get("transform").isJsonPrimitive() && json.get("transform").getAsJsonPrimitive().isString())
{
String transform = json.get("transform").getAsString();
ret.state = Optional.ofNullable(transforms.get(transform));
if (!ret.state.isPresent())
{
throw new JsonParseException("transform: unknown default string: " + transform);
}
}
else if (!json.get("transform").isJsonObject())
{
try
{
TRSRTransformation base = context.deserialize(json.get("transform"), TRSRTransformation.class);
ret.state = Optional.of(TRSRTransformation.blockCenterToCorner(base));
}
catch (JsonParseException e)
{
throw new JsonParseException("transform: expected a string, object or valid base transformation, got: " + json.get("transform"));
}
}
else
{
JsonObject transform = json.get("transform").getAsJsonObject();
EnumMap<TransformType, TRSRTransformation> transforms = Maps.newEnumMap(TransformType.class);
if(transform.has("thirdperson"))
{
TRSRTransformation t = context.deserialize(transform.get("thirdperson"), TRSRTransformation.class);
transform.remove("thirdperson");
transforms.put(TransformType.THIRD_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("thirdperson_righthand"))
{
TRSRTransformation t = context.deserialize(transform.get("thirdperson_righthand"), TRSRTransformation.class);
transform.remove("thirdperson_righthand");
transforms.put(TransformType.THIRD_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("thirdperson_lefthand"))
{
TRSRTransformation t = context.deserialize(transform.get("thirdperson_lefthand"), TRSRTransformation.class);
transform.remove("thirdperson_lefthand");
transforms.put(TransformType.THIRD_PERSON_LEFT_HAND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("firstperson"))
{
TRSRTransformation t = context.deserialize(transform.get("firstperson"), TRSRTransformation.class);
transform.remove("firstperson");
transforms.put(TransformType.FIRST_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("firstperson_righthand"))
{
TRSRTransformation t = context.deserialize(transform.get("firstperson_righthand"), TRSRTransformation.class);
transform.remove("firstperson_righthand");
transforms.put(TransformType.FIRST_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("firstperson_lefthand"))
{
TRSRTransformation t = context.deserialize(transform.get("firstperson_lefthand"), TRSRTransformation.class);
transform.remove("firstperson_lefthand");
transforms.put(TransformType.FIRST_PERSON_LEFT_HAND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("head"))
{
TRSRTransformation t = context.deserialize(transform.get("head"), TRSRTransformation.class);
transform.remove("head");
transforms.put(TransformType.HEAD, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("gui"))
{
TRSRTransformation t = context.deserialize(transform.get("gui"), TRSRTransformation.class);
transform.remove("gui");
transforms.put(TransformType.GUI, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("ground"))
{
TRSRTransformation t = context.deserialize(transform.get("ground"), TRSRTransformation.class);
transform.remove("ground");
transforms.put(TransformType.GROUND, TRSRTransformation.blockCenterToCorner(t));
}
if(transform.has("fixed"))
{
TRSRTransformation t = context.deserialize(transform.get("fixed"), TRSRTransformation.class);
transform.remove("fixed");
transforms.put(TransformType.FIXED, TRSRTransformation.blockCenterToCorner(t));
}
int k = transform.entrySet().size();
if(transform.has("matrix")) k--;
if(transform.has("translation")) k--;
if(transform.has("rotation")) k--;
if(transform.has("scale")) k--;
if(transform.has("post-rotation")) k--;
if(k > 0)
{
throw new JsonParseException("transform: allowed keys: 'thirdperson', 'firstperson', 'gui', 'head', 'matrix', 'translation', 'rotation', 'scale', 'post-rotation'");
}
TRSRTransformation base = TRSRTransformation.identity();
if(!transform.entrySet().isEmpty())
{
base = context.deserialize(transform, TRSRTransformation.class);
base = TRSRTransformation.blockCenterToCorner(base);
}
IModelState state;
if(transforms.isEmpty())
{
state = base;
}
else
{
state = new SimpleModelState(Maps.immutableEnumMap(transforms), Optional.of(base));
}
ret.state = Optional.of(state);
}
}
if (json.has("uvlock"))
{ // Load uvlock.
ret.uvLock = Optional.of(JsonUtils.getBoolean(json, "uvlock"));
}
if (json.has("smooth_lighting"))
{
ret.smooth = Optional.of(JsonUtils.getBoolean(json, "smooth_lighting"));
}
if (json.has("gui3d"))
{
ret.gui3d = Optional.of(JsonUtils.getBoolean(json, "gui3d"));
}
if (json.has("weight"))
{ // Load weight.
ret.weight = Optional.of(JsonUtils.getInt(json, "weight"));
}
if (json.has("submodel"))
{ // Load submodels.
JsonElement submodels = json.get("submodel");
if (submodels.isJsonPrimitive())
{ // Load a simple submodel declaration.
if (simpleSubmodelKey == null)
throw new RuntimeException("Attempted to use a simple submodel declaration outside a valid state variant declaration.");
String key = simpleSubmodelKey;
simpleSubmodelKey = null;
ret.model = getBlockLocation(submodels.getAsString());
ret.modelSet = true;
ForgeBlockStateV1.Variant dummyVar = new Variant(); // Create a dummy Variant to use as the owner of the simple submodel.
dummyVar.submodels.put(key, Collections.singletonList(ret));
dummyVar.simpleSubmodels = Collections.singletonMap(key, Variant.SET_VALUE);
return dummyVar;
}
else
{ // Load full submodel declarations.
// Clear the simple submodel key so that when deserializing submodels, the deserializer doesn't use the key.
simpleSubmodelKey = null;
for (Entry<String, JsonElement> submodel : submodels.getAsJsonObject().entrySet())
{
JsonElement varEl = submodel.getValue();
List<ForgeBlockStateV1.Variant> submodelVariants;
if (varEl.isJsonArray())
{ // Multiple variants of the submodel.
submodelVariants = Lists.newArrayList();
for (JsonElement e : varEl.getAsJsonArray())
submodelVariants.add(context.deserialize(e, Variant.class));
}
else if (varEl.isJsonNull())
{
submodelVariants = null;
}
else
{
submodelVariants = Collections.singletonList(context.deserialize(varEl, Variant.class));
}
if (submodelVariants != null) // Throw an error if there are submodels inside a submodel.
for (ForgeBlockStateV1.Variant part : submodelVariants)
throwIfNestedSubmodels(part);
ret.submodels.put(submodel.getKey(), submodelVariants);
// Put null to remove this submodel from the list of simple submodels when something inherits this submodel.
ret.simpleSubmodels.put(submodel.getKey(), null);
}
}
}
if (json.has("custom"))
{
for (Entry<String, JsonElement> e : json.get("custom").getAsJsonObject().entrySet())
{
if (e.getValue().isJsonNull())
ret.customData.put(e.getKey(), null);
else
ret.customData.put(e.getKey(), e.getValue().toString());
}
}
simpleSubmodelKey = null;
return ret;
}
}
@Nullable
public ResourceLocation getModel() { return model; }
public boolean isModelSet() { return modelSet; }
public Optional<IModelState> getState() { return state; }
public Optional<Boolean> getUvLock() { return uvLock; }
public Optional<Integer> getWeight() { return weight; }
public ImmutableMap<String, String> getTextures() { return ImmutableMap.copyOf(textures); }
public ImmutableMap<String, List<ForgeBlockStateV1.Variant>> getSubmodels() { return ImmutableMap.copyOf(submodels); }
public ImmutableMap<String, String> getCustomData() { return ImmutableMap.copyOf(customData); }
}
public static class TRSRDeserializer implements JsonDeserializer<TRSRTransformation>
{
public static final TRSRDeserializer INSTANCE = new TRSRDeserializer();
@Override
public TRSRTransformation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString())
{
String transform = json.getAsString();
if(transform.equals("identity"))
{
return TRSRTransformation.identity();
}
else
{
throw new JsonParseException("TRSR: unknown default string: " + transform);
}
}
if (json.isJsonArray())
{
// direct matrix array
return new TRSRTransformation(parseMatrix(json));
}
if (!json.isJsonObject()) throw new JsonParseException("TRSR: expected array or object, got: " + json);
JsonObject obj = json.getAsJsonObject();
TRSRTransformation ret;
if (obj.has("matrix"))
{
// matrix as a sole key
ret = new TRSRTransformation(parseMatrix(obj.get("matrix")));
obj.remove("matrix");
if (obj.entrySet().size() != 0)
{
throw new JsonParseException("TRSR: can't combine matrix and other keys");
}
return ret;
}
Vector3f translation = null;
Quat4f leftRot = null;
Vector3f scale = null;
Quat4f rightRot = null;
if (obj.has("translation"))
{
translation = new Vector3f(parseFloatArray(obj.get("translation"), 3, "Translation"));
obj.remove("translation");
}
if (obj.has("rotation"))
{
leftRot = parseRotation(obj.get("rotation"));
obj.remove("rotation");
}
if (obj.has("scale"))
{
if(!obj.get("scale").isJsonArray())
{
try
{
float s = obj.get("scale").getAsNumber().floatValue();
scale = new Vector3f(s, s, s);
}
catch (ClassCastException ex)
{
throw new JsonParseException("TRSR scale: expected number or array, got: " + obj.get("scale"));
}
}
else
{
scale = new Vector3f(parseFloatArray(obj.get("scale"), 3, "Scale"));
}
obj.remove("scale");
}
if (obj.has("post-rotation"))
{
rightRot = parseRotation(obj.get("post-rotation"));
obj.remove("post-rotation");
}
if (!obj.entrySet().isEmpty()) throw new JsonParseException("TRSR: can either have single 'matrix' key, or a combination of 'translation', 'rotation', 'scale', 'post-rotation'");
return new TRSRTransformation(translation, leftRot, scale, rightRot);
}
public static Matrix4f parseMatrix(JsonElement e)
{
if (!e.isJsonArray()) throw new JsonParseException("Matrix: expected an array, got: " + e);
JsonArray m = e.getAsJsonArray();
if (m.size() != 3) throw new JsonParseException("Matrix: expected an array of length 3, got: " + m.size());
Matrix4f ret = new Matrix4f();
for (int i = 0; i < 3; i++)
{
if (!m.get(i).isJsonArray()) throw new JsonParseException("Matrix row: expected an array, got: " + m.get(i));
JsonArray r = m.get(i).getAsJsonArray();
if (r.size() != 4) throw new JsonParseException("Matrix row: expected an array of length 4, got: " + r.size());
for (int j = 0; j < 4; j++)
{
try
{
ret.setElement(i, j, r.get(j).getAsNumber().floatValue());
}
catch (ClassCastException ex)
{
throw new JsonParseException("Matrix element: expected number, got: " + r.get(j));
}
}
}
return ret;
}
public static float[] parseFloatArray(JsonElement e, int length, String prefix)
{
if (!e.isJsonArray()) throw new JsonParseException(prefix + ": expected an array, got: " + e);
JsonArray t = e.getAsJsonArray();
if (t.size() != length) throw new JsonParseException(prefix + ": expected an array of length " + length + ", got: " + t.size());
float[] ret = new float[length];
for (int i = 0; i < length; i++)
{
try
{
ret[i] = t.get(i).getAsNumber().floatValue();
}
catch (ClassCastException ex)
{
throw new JsonParseException(prefix + " element: expected number, got: " + t.get(i));
}
}
return ret;
}
public static Quat4f parseAxisRotation(JsonElement e)
{
if (!e.isJsonObject()) throw new JsonParseException("Axis rotation: object expected, got: " + e);
JsonObject obj = e.getAsJsonObject();
if (obj.entrySet().size() != 1) throw new JsonParseException("Axis rotation: expected single axis object, got: " + e);
Map.Entry<String, JsonElement> entry = obj.entrySet().iterator().next();
Quat4f ret = new Quat4f();
try
{
if (entry.getKey().equals("x"))
{
ret.set(new AxisAngle4d(1, 0, 0, Math.toRadians(entry.getValue().getAsNumber().floatValue())));
}
else if (entry.getKey().equals("y"))
{
ret.set(new AxisAngle4d(0, 1, 0, Math.toRadians(entry.getValue().getAsNumber().floatValue())));
}
else if (entry.getKey().equals("z"))
{
ret.set(new AxisAngle4d(0, 0, 1, Math.toRadians(entry.getValue().getAsNumber().floatValue())));
}
else throw new JsonParseException("Axis rotation: expected single axis key, got: " + entry.getKey());
}
catch(ClassCastException ex)
{
throw new JsonParseException("Axis rotation value: expected number, got: " + entry.getValue());
}
return ret;
}
public static Quat4f parseRotation(JsonElement e)
{
if (e.isJsonArray())
{
if (e.getAsJsonArray().get(0).isJsonObject())
{
Quat4f ret = new Quat4f(0, 0, 0, 1);
for (JsonElement a : e.getAsJsonArray())
{
ret.mul(parseAxisRotation(a));
}
return ret;
}
else if (e.isJsonArray())
{
JsonArray array = e.getAsJsonArray();
if (array.size() == 3) //Vanilla rotation
return TRSRTransformation.quatFromXYZDegrees(new Vector3f(parseFloatArray(e, 3, "Rotation")));
else // quaternion
return new Quat4f(parseFloatArray(e, 4, "Rotation"));
}
else throw new JsonParseException("Rotation: expected array or object, got: " + e);
}
else if (e.isJsonObject())
{
return parseAxisRotation(e);
}
else throw new JsonParseException("Rotation: expected array or object, got: " + e);
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.function.Predicate;
import net.minecraftforge.client.resource.IResourceType;
import net.minecraftforge.client.resource.ISelectiveResourceReloadListener;
import net.minecraftforge.client.resource.VanillaResourceType;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.util.ResourceLocation;
public interface ICustomModelLoader extends ISelectiveResourceReloadListener
{
@Override
void onResourceManagerReload(IResourceManager resourceManager);
@Override
default void onResourceManagerReload(IResourceManager resourceManager, Predicate<IResourceType> resourcePredicate)
{
if (resourcePredicate.test(VanillaResourceType.MODELS))
{
onResourceManagerReload(resourceManager);
}
}
/*
* Checks if given model should be loaded by this loader.
* Reading file contents is inadvisable, if possible decision should be made based on the location alone.
*/
boolean accepts(ResourceLocation modelLocation);
/*
* loads (or reloads) specified model
*/
IModel loadModel(ResourceLocation modelLocation) throws Exception;
}

View File

@@ -0,0 +1,124 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.Collection;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.model.IModelState;
import java.util.function.Function;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
/*
* Interface for models that can be baked
* (possibly to different vertex formats and with different state).
*/
public interface IModel
{
/*
* Returns all model locations that this model depends on.
* Assume that returned collection is immutable.
* See ModelLoaderRegistry.getModel for dependency loading.
*/
default Collection<ResourceLocation> getDependencies() {
return ImmutableList.of();
}
/*
* Returns all texture locations that this model depends on.
* Assume that returned collection is immutable.
*/
default Collection<ResourceLocation> getTextures() {
return ImmutableList.of();
}
/*
* All model texture coordinates should be resolved at this method.
* Returned model should be in the simplest form possible, for performance
* reasons (if it's not ISmartBlock/ItemModel - then it should be
* represented by List<BakedQuad> internally).
* Returned model's getFormat() can me less specific than the passed
* format argument (some attributes can be replaced with padding),
* if there's no such info in this model.
*/
IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter);
/*
* Default state this model will be baked with.
* See IModelState.
*/
default IModelState getDefaultState() {
return TRSRTransformation.identity();
}
default Optional<? extends IClip> getClip(String name) {
return Optional.empty();
}
/**
* Allows the model to process custom data from the variant definition.
* If unknown data is encountered it should be skipped.
* @return a new model, with data applied.
*/
default IModel process(ImmutableMap<String, String> customData) {
return this;
}
default IModel smoothLighting(boolean value) {
return this;
}
default IModel gui3d(boolean value) {
return this;
}
default IModel uvlock(boolean value) {
return this;
}
/**
* Applies new textures to the model.
* The returned model should be independent of the accessed one,
* as a model should be able to be retextured multiple times producing
* a separate model each time.
*
* The input map MAY map to an empty string "" which should be used
* to indicate the texture was removed. Handling of that is up to
* the model itself. Such as using default, missing texture, or
* removing vertices.
*
* The input should be considered a DIFF of the old textures, not a
* replacement as it may not contain everything.
*
* @param textures New
* @return Model with textures applied.
*/
default IModel retexture(ImmutableMap<String, String> textures) {
return this;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
public interface ISmartVariant
{
IModel process(IModel base);
}

View File

@@ -0,0 +1,469 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import javax.vecmath.Vector4f;
import net.minecraftforge.common.ForgeVersion;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelBlock;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
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.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Optional;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public final class ItemLayerModel implements IModel
{
public static final ItemLayerModel INSTANCE = new ItemLayerModel(ImmutableList.of());
private static final EnumFacing[] HORIZONTALS = {EnumFacing.UP, EnumFacing.DOWN};
private static final EnumFacing[] VERTICALS = {EnumFacing.WEST, EnumFacing.EAST};
private final ImmutableList<ResourceLocation> textures;
private final ItemOverrideList overrides;
public ItemLayerModel(ImmutableList<ResourceLocation> textures)
{
this(textures, ItemOverrideList.NONE);
}
public ItemLayerModel(ImmutableList<ResourceLocation> textures, ItemOverrideList overrides)
{
this.textures = textures;
this.overrides = overrides;
}
public ItemLayerModel(ModelBlock model)
{
this(getTextures(model), model.createOverrides());
}
private static ImmutableList<ResourceLocation> getTextures(ModelBlock model)
{
ImmutableList.Builder<ResourceLocation> builder = ImmutableList.builder();
for(int i = 0; model.isTexturePresent("layer" + i); i++)
{
builder.add(new ResourceLocation(model.resolveTextureName("layer" + i)));
}
return builder.build();
}
public Collection<ResourceLocation> getTextures()
{
return textures;
}
public ItemLayerModel retexture(ImmutableMap<String, String> textures)
{
ImmutableList.Builder<ResourceLocation> builder = ImmutableList.builder();
for(int i = 0; i < textures.size() + this.textures.size(); i++)
{
if(textures.containsKey("layer" + i))
{
builder.add(new ResourceLocation(textures.get("layer" + i)));
}
else if(i < this.textures.size())
{
builder.add(this.textures.get(i));
}
}
return new ItemLayerModel(builder.build(), overrides);
}
@Override
public IBakedModel bake(IModelState state, final VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
Optional<TRSRTransformation> transform = state.apply(Optional.empty());
for(int i = 0; i < textures.size(); i++)
{
TextureAtlasSprite sprite = bakedTextureGetter.apply(textures.get(i));
builder.addAll(getQuadsForSprite(i, sprite, format, transform));
}
TextureAtlasSprite particle = bakedTextureGetter.apply(textures.isEmpty() ? new ResourceLocation("missingno") : textures.get(0));
ImmutableMap<TransformType, TRSRTransformation> map = PerspectiveMapWrapper.getTransforms(state);
return new BakedItemModel(builder.build(), particle, map, overrides);
}
public static ImmutableList<BakedQuad> getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format, Optional<TRSRTransformation> transform)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
int uMax = sprite.getIconWidth();
int vMax = sprite.getIconHeight();
FaceData faceData = new FaceData(uMax, vMax);
boolean translucent = false;
for(int f = 0; f < sprite.getFrameCount(); f++)
{
int[] pixels = sprite.getFrameTextureData(f)[0];
boolean ptu;
boolean[] ptv = new boolean[uMax];
Arrays.fill(ptv, true);
for(int v = 0; v < vMax; v++)
{
ptu = true;
for(int u = 0; u < uMax; u++)
{
int alpha = getAlpha(pixels, uMax, vMax, u, v);
boolean t = alpha / 255f <= 0.1f;
if (!t && alpha < 255)
{
translucent = true;
}
if(ptu && !t) // left - transparent, right - opaque
{
faceData.set(EnumFacing.WEST, u, v);
}
if(!ptu && t) // left - opaque, right - transparent
{
faceData.set(EnumFacing.EAST, u-1, v);
}
if(ptv[u] && !t) // up - transparent, down - opaque
{
faceData.set(EnumFacing.UP, u, v);
}
if(!ptv[u] && t) // up - opaque, down - transparent
{
faceData.set(EnumFacing.DOWN, u, v-1);
}
ptu = t;
ptv[u] = t;
}
if(!ptu) // last - opaque
{
faceData.set(EnumFacing.EAST, uMax-1, v);
}
}
// last line
for(int u = 0; u < uMax; u++)
{
if(!ptv[u])
{
faceData.set(EnumFacing.DOWN, u, vMax-1);
}
}
}
// horizontal quads
for (EnumFacing facing : HORIZONTALS)
{
for (int v = 0; v < vMax; v++)
{
int uStart = 0, uEnd = uMax;
boolean building = false;
for (int u = 0; u < uMax; u++)
{
boolean face = faceData.get(facing, u, v);
if (!translucent)
{
if (face)
{
if (!building)
{
building = true;
uStart = u;
}
uEnd = u + 1;
}
}
else
{
if (building && !face) // finish current quad
{
// make quad [uStart, u]
int off = facing == EnumFacing.DOWN ? 1 : 0;
builder.add(buildSideQuad(format, transform, facing, tint, sprite, uStart, v+off, u-uStart));
building = false;
}
else if (!building && face) // start new quad
{
building = true;
uStart = u;
}
}
}
if (building) // build remaining quad
{
// make quad [uStart, uEnd]
int off = facing == EnumFacing.DOWN ? 1 : 0;
builder.add(buildSideQuad(format, transform, facing, tint, sprite, uStart, v+off, uEnd-uStart));
}
}
}
// vertical quads
for (EnumFacing facing : VERTICALS)
{
for (int u = 0; u < uMax; u++)
{
int vStart = 0, vEnd = vMax;
boolean building = false;
for (int v = 0; v < vMax; v++)
{
boolean face = faceData.get(facing, u, v);
if (!translucent)
{
if (face)
{
if (!building)
{
building = true;
vStart = v;
}
vEnd = v + 1;
}
}
else
{
if (building && !face) // finish current quad
{
// make quad [vStart, v]
int off = facing == EnumFacing.EAST ? 1 : 0;
builder.add(buildSideQuad(format, transform, facing, tint, sprite, u+off, vStart, v-vStart));
building = false;
}
else if (!building && face) // start new quad
{
building = true;
vStart = v;
}
}
}
if (building) // build remaining quad
{
// make quad [vStart, vEnd]
int off = facing == EnumFacing.EAST ? 1 : 0;
builder.add(buildSideQuad(format, transform, facing, tint, sprite, u+off, vStart, vEnd-vStart));
}
}
}
// front
builder.add(buildQuad(format, transform, EnumFacing.NORTH, sprite, tint,
0, 0, 7.5f / 16f, sprite.getMinU(), sprite.getMaxV(),
0, 1, 7.5f / 16f, sprite.getMinU(), sprite.getMinV(),
1, 1, 7.5f / 16f, sprite.getMaxU(), sprite.getMinV(),
1, 0, 7.5f / 16f, sprite.getMaxU(), sprite.getMaxV()
));
// back
builder.add(buildQuad(format, transform, EnumFacing.SOUTH, sprite, tint,
0, 0, 8.5f / 16f, sprite.getMinU(), sprite.getMaxV(),
1, 0, 8.5f / 16f, sprite.getMaxU(), sprite.getMaxV(),
1, 1, 8.5f / 16f, sprite.getMaxU(), sprite.getMinV(),
0, 1, 8.5f / 16f, sprite.getMinU(), sprite.getMinV()
));
return builder.build();
}
private static class FaceData
{
private final EnumMap<EnumFacing, BitSet> data = new EnumMap<>(EnumFacing.class);
private final int vMax;
FaceData(int uMax, int vMax)
{
this.vMax = vMax;
data.put(EnumFacing.WEST, new BitSet(uMax * vMax));
data.put(EnumFacing.EAST, new BitSet(uMax * vMax));
data.put(EnumFacing.UP, new BitSet(uMax * vMax));
data.put(EnumFacing.DOWN, new BitSet(uMax * vMax));
}
public void set(EnumFacing facing, int u, int v)
{
data.get(facing).set(getIndex(u, v));
}
public boolean get(EnumFacing facing, int u, int v)
{
return data.get(facing).get(getIndex(u, v));
}
private int getIndex(int u, int v)
{
return v * vMax + u;
}
}
private static int getAlpha(int[] pixels, int uMax, int vMax, int u, int v)
{
return pixels[u + (vMax - 1 - v) * uMax] >> 24 & 0xFF;
}
private static BakedQuad buildSideQuad(VertexFormat format, Optional<TRSRTransformation> transform, EnumFacing side, int tint, TextureAtlasSprite sprite, int u, int v, int size)
{
final float eps = 1e-2f;
int width = sprite.getIconWidth();
int height = sprite.getIconHeight();
float x0 = (float) u / width;
float y0 = (float) v / height;
float x1 = x0, y1 = y0;
float z0 = 7.5f / 16f, z1 = 8.5f / 16f;
switch(side)
{
case WEST:
z0 = 8.5f / 16f;
z1 = 7.5f / 16f;
case EAST:
y1 = (float) (v + size) / height;
break;
case DOWN:
z0 = 8.5f / 16f;
z1 = 7.5f / 16f;
case UP:
x1 = (float) (u + size) / width;
break;
default:
throw new IllegalArgumentException("can't handle z-oriented side");
}
float dx = side.getDirectionVec().getX() * eps / width;
float dy = side.getDirectionVec().getY() * eps / height;
float u0 = 16f * (x0 - dx);
float u1 = 16f * (x1 - dx);
float v0 = 16f * (1f - y0 - dy);
float v1 = 16f * (1f - y1 - dy);
return buildQuad(
format, transform, remap(side), sprite, tint,
x0, y0, z0, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0),
x1, y1, z0, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1),
x1, y1, z1, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1),
x0, y0, z1, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0)
);
}
private static EnumFacing remap(EnumFacing side)
{
// getOpposite is related to the swapping of V direction
return side.getAxis() == EnumFacing.Axis.Y ? side.getOpposite() : side;
}
private static BakedQuad buildQuad(
VertexFormat format, Optional<TRSRTransformation> transform, EnumFacing side, TextureAtlasSprite sprite, int tint,
float x0, float y0, float z0, float u0, float v0,
float x1, float y1, float z1, float u1, float v1,
float x2, float y2, float z2, float u2, float v2,
float x3, float y3, float z3, float u3, float v3)
{
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadTint(tint);
builder.setQuadOrientation(side);
builder.setTexture(sprite);
putVertex(builder, format, transform, side, x0, y0, z0, u0, v0);
putVertex(builder, format, transform, side, x1, y1, z1, u1, v1);
putVertex(builder, format, transform, side, x2, y2, z2, u2, v2);
putVertex(builder, format, transform, side, x3, y3, z3, u3, v3);
return builder.build();
}
private static void putVertex(UnpackedBakedQuad.Builder builder, VertexFormat format, Optional<TRSRTransformation> transform, EnumFacing side, float x, float y, float z, float u, float v)
{
Vector4f vec = new Vector4f();
for(int e = 0; e < format.getElementCount(); e++)
{
switch(format.getElement(e).getUsage())
{
case POSITION:
if(transform.isPresent())
{
vec.x = x;
vec.y = y;
vec.z = z;
vec.w = 1;
transform.get().getMatrix().transform(vec);
builder.put(e, vec.x, vec.y, vec.z, vec.w);
}
else
{
builder.put(e, x, y, z, 1);
}
break;
case COLOR:
builder.put(e, 1f, 1f, 1f, 1f);
break;
case UV: if(format.getElement(e).getIndex() == 0)
{
builder.put(e, u, v, 0f, 1f);
break;
}
case NORMAL:
builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f);
break;
default:
builder.put(e);
break;
}
}
}
public static enum Loader implements ICustomModelLoader
{
INSTANCE;
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {}
@Override
public boolean accepts(ResourceLocation modelLocation)
{
return modelLocation.getResourceDomain().equals(ForgeVersion.MOD_ID) && (
modelLocation.getResourcePath().equals("item-layer") ||
modelLocation.getResourcePath().equals("models/block/item-layer") ||
modelLocation.getResourcePath().equals("models/item/item-layer"));
}
@Override
public IModel loadModel(ResourceLocation modelLocation)
{
return ItemLayerModel.INSTANCE;
}
}
}

View File

@@ -0,0 +1,339 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import com.google.common.collect.Lists;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.TRSRTransformation;
import javax.vecmath.Vector4f;
import java.util.List;
public final class ItemTextureQuadConverter
{
private ItemTextureQuadConverter()
{
// non-instantiable
}
/** @deprecated use {@link #convertTexture(VertexFormat, TRSRTransformation, TextureAtlasSprite, TextureAtlasSprite, float, EnumFacing, int, int)}*/
@Deprecated // TODO: remove
public static List<UnpackedBakedQuad> convertTexture(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color)
{
return convertTexture(format, transform, template, sprite, z, facing, color, -1);
}
/**
* Takes a texture and converts it into BakedQuads.
* The conversion is done by scanning the texture horizontally and vertically and creating "strips" of the texture.
* Strips that are of the same size and follow each other are converted into one bigger quad.
* </br>
* The resulting list of quads is the texture represented as a list of horizontal OR vertical quads,
* depending on which creates less quads. If the amount of quads is equal, horizontal is preferred.
*
* @param format
* @param template The input texture to convert
* @param sprite The texture whose UVs shall be used
* @return The generated quads.
*/
public static List<UnpackedBakedQuad> convertTexture(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color, int tint)
{
List<UnpackedBakedQuad> horizontal = convertTextureHorizontal(format, transform, template, sprite, z, facing, color, tint);
List<UnpackedBakedQuad> vertical = convertTextureVertical(format, transform, template, sprite, z, facing, color, tint);
return horizontal.size() >= vertical.size() ? horizontal : vertical;
}
/** @deprecated use {@link #convertTextureHorizontal(VertexFormat, TRSRTransformation, TextureAtlasSprite, TextureAtlasSprite, float, EnumFacing, int, int)} */
@Deprecated // TODO: remove
public static List<UnpackedBakedQuad> convertTextureHorizontal(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color)
{
return convertTextureHorizontal(format, transform, template, sprite, z, facing, color, -1);
}
/**
* Scans a texture and converts it into a list of horizontal strips stacked on top of each other.
* The height of the strips is as big as possible.
*/
public static List<UnpackedBakedQuad> convertTextureHorizontal(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color, int tint)
{
int w = template.getIconWidth();
int h = template.getIconHeight();
float wScale = 16f / (float)w;
float hScale = 16f / (float)h;
int[] data = template.getFrameTextureData(0)[0];
List<UnpackedBakedQuad> quads = Lists.newArrayList();
// the upper left x-position of the current quad
int start = -1;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
// current pixel
int pixel = data[y * w + x];
// no current quad but found a new one
if (start < 0 && isVisible(pixel))
{
start = x;
}
// got a current quad, but it ends here
if (start >= 0 && !isVisible(pixel))
{
// we now check if the visibility of the next row matches the one fo the current row
// if they are, we can extend the quad downwards
int endY = y + 1;
boolean sameRow = true;
while (sameRow && endY < h)
{
for (int i = 0; i < w; i++)
{
int px1 = data[y * w + i];
int px2 = data[endY * w + i];
if (isVisible(px1) != isVisible(px2))
{
sameRow = false;
break;
}
}
if (sameRow)
{
endY++;
}
}
// create the quad
quads.add(genQuad(format, transform,
(float)start * wScale,
(float)y * hScale,
(float)x * wScale,
(float)endY * hScale,
z, sprite, facing, color, tint));
// update Y if all the rows match. no need to rescan
if (endY - y > 1)
{
y = endY - 1;
}
// clear current quad
start = -1;
}
}
}
return quads;
}
/** @deprecated use {@link #convertTextureVertical(VertexFormat, TRSRTransformation, TextureAtlasSprite, TextureAtlasSprite, float, EnumFacing, int, int)} */
@Deprecated // TODO: remove
public static List<UnpackedBakedQuad> convertTextureVertical(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color)
{
return convertTextureVertical(format, transform, template, sprite, z, facing, color, -1);
}
/**
* Scans a texture and converts it into a list of vertical strips stacked next to each other from left to right.
* The width of the strips is as big as possible.
*/
public static List<UnpackedBakedQuad> convertTextureVertical(VertexFormat format, TRSRTransformation transform, TextureAtlasSprite template, TextureAtlasSprite sprite, float z, EnumFacing facing, int color, int tint)
{
int w = template.getIconWidth();
int h = template.getIconHeight();
float wScale = 16f / (float)w;
float hScale = 16f / (float)h;
int[] data = template.getFrameTextureData(0)[0];
List<UnpackedBakedQuad> quads = Lists.newArrayList();
// the upper left y-position of the current quad
int start = -1;
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
// current pixel
int pixel = data[y * w + x];
// no current quad but found a new one
if (start < 0 && isVisible(pixel))
{
start = y;
}
// got a current quad, but it ends here
if (start >= 0 && !isVisible(pixel))
{
// we now check if the visibility of the next column matches the one fo the current row
// if they are, we can extend the quad downwards
int endX = x + 1;
boolean sameColumn = true;
while (sameColumn && endX < w)
{
for (int i = 0; i < h; i++)
{
int px1 = data[i * w + x];
int px2 = data[i * w + endX];
if (isVisible(px1) != isVisible(px2))
{
sameColumn = false;
break;
}
}
if (sameColumn)
{
endX++;
}
}
// create the quad
quads.add(genQuad(format, transform,
(float)x * wScale,
(float)start * hScale,
(float)endX * wScale,
(float)y * hScale,
z, sprite, facing, color, tint));
// update X if all the columns match. no need to rescan
if (endX - x > 1)
{
x = endX - 1;
}
// clear current quad
start = -1;
}
}
}
return quads;
}
private static boolean isVisible(int color)
{
return (color >> 24 & 255) / 255f > 0.1f;
}
/** @deprecated use {@link #genQuad(VertexFormat, TRSRTransformation, float, float, float, float, float, TextureAtlasSprite, EnumFacing, int, int)} */
@Deprecated // TODO: remove
public static UnpackedBakedQuad genQuad(VertexFormat format, TRSRTransformation transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, EnumFacing facing, int color)
{
return genQuad(format, transform, x1, y1, x2, y2, z, sprite, facing, color, -1);
}
/**
* Generates a Front/Back quad for an itemmodel. Therefore only supports facing NORTH and SOUTH.
* Coordinates are [0,16] to match the usual coordinates used in TextureAtlasSprites
*/
public static UnpackedBakedQuad genQuad(VertexFormat format, TRSRTransformation transform, float x1, float y1, float x2, float y2, float z, TextureAtlasSprite sprite, EnumFacing facing, int color, int tint)
{
float u1 = sprite.getInterpolatedU(x1);
float v1 = sprite.getInterpolatedV(y1);
float u2 = sprite.getInterpolatedU(x2);
float v2 = sprite.getInterpolatedV(y2);
x1 /= 16f;
y1 /= 16f;
x2 /= 16f;
y2 /= 16f;
float tmp = y1;
y1 = 1f - y2;
y2 = 1f - tmp;
return putQuad(format, transform, facing, sprite, color, tint, x1, y1, x2, y2, z, u1, v1, u2, v2);
}
private static UnpackedBakedQuad putQuad(VertexFormat format, TRSRTransformation transform, EnumFacing side, TextureAtlasSprite sprite, int color, int tint,
float x1, float y1, float x2, float y2, float z,
float u1, float v1, float u2, float v2)
{
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadTint(tint);
builder.setQuadOrientation(side);
builder.setTexture(sprite);
if (side == EnumFacing.SOUTH)
{
putVertex(builder, format, transform, side, x1, y1, z, u1, v2, color);
putVertex(builder, format, transform, side, x2, y1, z, u2, v2, color);
putVertex(builder, format, transform, side, x2, y2, z, u2, v1, color);
putVertex(builder, format, transform, side, x1, y2, z, u1, v1, color);
}
else
{
putVertex(builder, format, transform, side, x1, y1, z, u1, v2, color);
putVertex(builder, format, transform, side, x1, y2, z, u1, v1, color);
putVertex(builder, format, transform, side, x2, y2, z, u2, v1, color);
putVertex(builder, format, transform, side, x2, y1, z, u2, v2, color);
}
return builder.build();
}
private static void putVertex(UnpackedBakedQuad.Builder builder, VertexFormat format, TRSRTransformation transform, EnumFacing side,
float x, float y, float z, float u, float v, int color)
{
Vector4f vec = new Vector4f();
for (int e = 0; e < format.getElementCount(); e++)
{
switch (format.getElement(e).getUsage())
{
case POSITION:
if (transform.isIdentity())
{
builder.put(e, x, y, z, 1);
}
// only apply the transform if it's not identity
else
{
vec.x = x;
vec.y = y;
vec.z = z;
vec.w = 1;
transform.getMatrix().transform(vec);
builder.put(e, vec.x, vec.y, vec.z, vec.w);
}
break;
case COLOR:
float r = ((color >> 16) & 0xFF) / 255f; // red
float g = ((color >> 8) & 0xFF) / 255f; // green
float b = ((color >> 0) & 0xFF) / 255f; // blue
float a = ((color >> 24) & 0xFF) / 255f; // alpha
builder.put(e, r, g, b, a);
break;
case UV:
if (format.getElement(e).getIndex() == 0)
{
builder.put(e, u, v, 0f, 1f);
break;
}
case NORMAL:
builder.put(e, (float) side.getFrontOffsetX(), (float) side.getFrontOffsetY(), (float) side.getFrontOffsetZ(), 0f);
break;
default:
builder.put(e);
break;
}
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.Map;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import com.google.common.base.Objects;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
/*
* Simple implementation of IModelState via a map and a default value. Provides a full state for each part.
* You probably don't want to use this.
*/
public class MapModelState implements IModelState
{
private final ImmutableMap<Wrapper, IModelState> map;
private final IModelState def;
public MapModelState(Map<Wrapper, IModelState> map)
{
this(map, TRSRTransformation.identity());
}
public MapModelState(Map<Wrapper, IModelState> map, TRSRTransformation def)
{
this(map, (IModelState)def);
}
public MapModelState(Map<Wrapper, IModelState> map, IModelState def)
{
this.map = ImmutableMap.copyOf(map);
this.def = def;
}
@Override
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
if(!part.isPresent() || !map.containsKey(part.get())) return def.apply(part);
return map.get(part.get()).apply(Optional.empty());
}
public IModelState getState(Object obj)
{
Wrapper w = wrap(obj);
if(!map.containsKey(w)) return def;
return map.get(w);
}
public static class Wrapper implements IModelPart
{
private final Object obj;
public Wrapper(Object obj)
{
this.obj = obj;
}
@Override
public int hashCode()
{
return obj.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Wrapper other = (Wrapper)obj;
return Objects.equal(this.obj, other.obj);
}
}
public static Wrapper wrap(Object obj)
{
return new Wrapper(obj);
}
}

View File

@@ -0,0 +1,470 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.vecmath.Quat4f;
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.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import java.util.Objects;
import java.util.function.Function;
import java.util.Optional;
import static net.minecraftforge.client.model.ModelDynBucket.LoaderDynBucket.getResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.IOUtils;
public final class ModelDynBucket implements IModel
{
public static final ModelResourceLocation LOCATION = new ModelResourceLocation(new ResourceLocation(ForgeVersion.MOD_ID, "dynbucket"), "inventory");
// minimal Z offset to prevent depth-fighting
private static final float NORTH_Z_COVER = 7.496f / 16f;
private static final float SOUTH_Z_COVER = 8.504f / 16f;
private static final float NORTH_Z_FLUID = 7.498f / 16f;
private static final float SOUTH_Z_FLUID = 8.502f / 16f;
public static final IModel MODEL = new ModelDynBucket();
@Nullable
private final ResourceLocation baseLocation;
@Nullable
private final ResourceLocation liquidLocation;
@Nullable
private final ResourceLocation coverLocation;
@Nullable
private final Fluid fluid;
private final boolean flipGas;
private final boolean tint;
public ModelDynBucket()
{
this(null, null, null, null, false, true);
}
/** @deprecated use {@link #ModelDynBucket(ResourceLocation, ResourceLocation, ResourceLocation, Fluid, boolean, boolean)} */
@Deprecated // TODO: remove
public ModelDynBucket(@Nullable ResourceLocation baseLocation, @Nullable ResourceLocation liquidLocation, @Nullable ResourceLocation coverLocation, @Nullable Fluid fluid, boolean flipGas)
{
this(baseLocation, liquidLocation, coverLocation, fluid, flipGas, true);
}
public ModelDynBucket(@Nullable ResourceLocation baseLocation, @Nullable ResourceLocation liquidLocation, @Nullable ResourceLocation coverLocation, @Nullable Fluid fluid, boolean flipGas, boolean tint)
{
this.baseLocation = baseLocation;
this.liquidLocation = liquidLocation;
this.coverLocation = coverLocation;
this.fluid = fluid;
this.flipGas = flipGas;
this.tint = tint;
}
@Override
public Collection<ResourceLocation> getTextures()
{
ImmutableSet.Builder<ResourceLocation> builder = ImmutableSet.builder();
if (baseLocation != null)
builder.add(baseLocation);
if (liquidLocation != null)
builder.add(liquidLocation);
if (coverLocation != null)
builder.add(coverLocation);
if (fluid != null)
builder.add(fluid.getStill());
return builder.build();
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format,
Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableMap<TransformType, TRSRTransformation> transformMap = PerspectiveMapWrapper.getTransforms(state);
// if the fluid is lighter than air, will manipulate the initial state to be rotated 180° to turn it upside down
if (flipGas && fluid != null && fluid.isLighterThanAir())
{
state = new ModelStateComposition(state, TRSRTransformation.blockCenterToCorner(new TRSRTransformation(null, new Quat4f(0, 0, 1, 0), null, null)));
}
TRSRTransformation transform = state.apply(Optional.empty()).orElse(TRSRTransformation.identity());
TextureAtlasSprite fluidSprite = null;
TextureAtlasSprite particleSprite = null;
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
if(fluid != null) {
fluidSprite = bakedTextureGetter.apply(fluid.getStill());
}
if (baseLocation != null)
{
// build base (insidest)
IBakedModel model = (new ItemLayerModel(ImmutableList.of(baseLocation))).bake(state, format, bakedTextureGetter);
builder.addAll(model.getQuads(null, null, 0));
particleSprite = model.getParticleTexture();
}
if (liquidLocation != null && fluidSprite != null)
{
TextureAtlasSprite liquid = bakedTextureGetter.apply(liquidLocation);
// build liquid layer (inside)
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, liquid, fluidSprite, NORTH_Z_FLUID, EnumFacing.NORTH, tint ? fluid.getColor() : 0xFFFFFFFF, 1));
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, liquid, fluidSprite, SOUTH_Z_FLUID, EnumFacing.SOUTH, tint ? fluid.getColor() : 0xFFFFFFFF, 1));
particleSprite = fluidSprite;
}
if (coverLocation != null)
{
// cover (the actual item around the other two)
TextureAtlasSprite cover = bakedTextureGetter.apply(coverLocation);
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, NORTH_Z_COVER, cover, EnumFacing.NORTH, 0xFFFFFFFF, 2));
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, SOUTH_Z_COVER, cover, EnumFacing.SOUTH, 0xFFFFFFFF, 2));
if (particleSprite == null)
{
particleSprite = cover;
}
}
return new BakedDynBucket(this, builder.build(), particleSprite, format, Maps.immutableEnumMap(transformMap), Maps.newHashMap());
}
/**
* Sets the fluid in the model.
* "fluid" - Name of the fluid in the fluid registry.
* "flipGas" - If "true" the model will be flipped upside down if the fluid is lighter than air. If "false" it won't.
* "applyTint" - If "true" the model will tint the fluid quads according to the fluid's base color.
* <p/>
* If the fluid can't be found, water is used.
*/
@Override
public ModelDynBucket process(ImmutableMap<String, String> customData)
{
String fluidName = customData.get("fluid");
Fluid fluid = FluidRegistry.getFluid(fluidName);
if (fluid == null) fluid = this.fluid;
boolean flip = flipGas;
if (customData.containsKey("flipGas"))
{
String flipStr = customData.get("flipGas");
if (flipStr.equals("true")) flip = true;
else if (flipStr.equals("false")) flip = false;
else
throw new IllegalArgumentException(String.format("DynBucket custom data \"flipGas\" must have value \'true\' or \'false\' (was \'%s\')", flipStr));
}
boolean tint = this.tint;
if (customData.containsKey("applyTint"))
{
String string = customData.get("applyTint");
switch (string)
{
case "true": tint = true; break;
case "false": tint = false; break;
default: throw new IllegalArgumentException(String.format("DynBucket custom data \"applyTint\" must have value \'true\' or \'false\' (was \'%s\')", string));
}
}
// create new model with correct liquid
return new ModelDynBucket(baseLocation, liquidLocation, coverLocation, fluid, flip, tint);
}
/**
* Allows to use different textures for the model.
* There are 3 layers:
* base - The empty bucket/container
* fluid - A texture representing the liquid portion. Non-transparent = liquid
* cover - An overlay that's put over the liquid (optional)
* <p/>
* If no liquid is given a hardcoded variant for the bucket is used.
*/
@Override
public ModelDynBucket retexture(ImmutableMap<String, String> textures)
{
ResourceLocation base = baseLocation;
ResourceLocation liquid = liquidLocation;
ResourceLocation cover = coverLocation;
if (textures.containsKey("base"))
base = new ResourceLocation(textures.get("base"));
if (textures.containsKey("fluid"))
liquid = new ResourceLocation(textures.get("fluid"));
if (textures.containsKey("cover"))
cover = new ResourceLocation(textures.get("cover"));
return new ModelDynBucket(base, liquid, cover, fluid, flipGas, tint);
}
public enum LoaderDynBucket implements ICustomModelLoader
{
INSTANCE;
@Override
public boolean accepts(ResourceLocation modelLocation)
{
return modelLocation.getResourceDomain().equals(ForgeVersion.MOD_ID) && modelLocation.getResourcePath().contains("forgebucket");
}
@Override
public IModel loadModel(ResourceLocation modelLocation)
{
return MODEL;
}
@Override
public void onResourceManagerReload(IResourceManager resourceManager)
{
// no need to clear cache since we create a new model instance
}
public void register(TextureMap map)
{
// only create these textures if they are not added by a resource pack
if (getResource(new ResourceLocation(ForgeVersion.MOD_ID, "textures/items/bucket_cover.png")) == null)
{
ResourceLocation bucketCover = new ResourceLocation(ForgeVersion.MOD_ID, "items/bucket_cover");
BucketCoverSprite bucketCoverSprite = new BucketCoverSprite(bucketCover);
map.setTextureEntry(bucketCoverSprite);
}
if (getResource(new ResourceLocation(ForgeVersion.MOD_ID, "textures/items/bucket_base.png")) == null)
{
ResourceLocation bucketBase = new ResourceLocation(ForgeVersion.MOD_ID, "items/bucket_base");
BucketBaseSprite bucketBaseSprite = new BucketBaseSprite(bucketBase);
map.setTextureEntry(bucketBaseSprite);
}
}
@Nullable
protected static IResource getResource(ResourceLocation resourceLocation)
{
try
{
return Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
}
catch (IOException ignored)
{
return null;
}
}
}
private static final class BucketBaseSprite extends TextureAtlasSprite
{
private final ResourceLocation bucket = new ResourceLocation("items/bucket_empty");
private final ImmutableList<ResourceLocation> dependencies = ImmutableList.of(bucket);
private BucketBaseSprite(ResourceLocation resourceLocation)
{
super(resourceLocation.toString());
}
@Override
public boolean hasCustomLoader(@Nonnull IResourceManager manager, @Nonnull ResourceLocation location)
{
return true;
}
@Override
public Collection<ResourceLocation> getDependencies()
{
return dependencies;
}
@Override
public boolean load(@Nonnull IResourceManager manager, @Nonnull ResourceLocation location, @Nonnull Function<ResourceLocation, TextureAtlasSprite> textureGetter)
{
final TextureAtlasSprite sprite = textureGetter.apply(bucket);
width = sprite.getIconWidth();
height = sprite.getIconHeight();
final int[][] pixels = sprite.getFrameTextureData(0);
this.clearFramesTextureData();
this.framesTextureData.add(pixels);
return false;
}
}
/**
* Creates a bucket cover sprite from the vanilla resource.
*/
private static final class BucketCoverSprite extends TextureAtlasSprite
{
private final ResourceLocation bucket = new ResourceLocation("items/bucket_empty");
private final ResourceLocation bucketCoverMask = new ResourceLocation(ForgeVersion.MOD_ID, "items/vanilla_bucket_cover_mask");
private final ImmutableList<ResourceLocation> dependencies = ImmutableList.of(bucket, bucketCoverMask);
private BucketCoverSprite(ResourceLocation resourceLocation)
{
super(resourceLocation.toString());
}
@Override
public boolean hasCustomLoader(@Nonnull IResourceManager manager, @Nonnull ResourceLocation location)
{
return true;
}
@Override
public Collection<ResourceLocation> getDependencies()
{
return dependencies;
}
@Override
public boolean load(@Nonnull IResourceManager manager, @Nonnull ResourceLocation location, @Nonnull Function<ResourceLocation, TextureAtlasSprite> textureGetter)
{
final TextureAtlasSprite sprite = textureGetter.apply(bucket);
final TextureAtlasSprite alphaMask = textureGetter.apply(bucketCoverMask);
width = sprite.getIconWidth();
height = sprite.getIconHeight();
final int[][] pixels = new int[Minecraft.getMinecraft().gameSettings.mipmapLevels + 1][];
pixels[0] = new int[width * height];
try (
IResource empty = getResource(new ResourceLocation("textures/items/bucket_empty.png"));
IResource mask = getResource(new ResourceLocation(ForgeVersion.MOD_ID, "textures/items/vanilla_bucket_cover_mask.png"))
) {
// use the alpha mask if it fits, otherwise leave the cover texture blank
if (empty != null && mask != null && Objects.equals(empty.getResourcePackName(), mask.getResourcePackName()) &&
alphaMask.getIconWidth() == width && alphaMask.getIconHeight() == height)
{
final int[][] oldPixels = sprite.getFrameTextureData(0);
final int[][] alphaPixels = alphaMask.getFrameTextureData(0);
for (int p = 0; p < width * height; p++)
{
final int alphaMultiplier = alphaPixels[0][p] >>> 24;
final int oldPixel = oldPixels[0][p];
final int oldPixelAlpha = oldPixel >>> 24;
final int newAlpha = oldPixelAlpha * alphaMultiplier / 0xFF;
pixels[0][p] = (oldPixel & 0xFFFFFF) + (newAlpha << 24);
}
}
}
catch (IOException e)
{
FMLLog.log.error("Failed to close resource", e);
}
this.clearFramesTextureData();
this.framesTextureData.add(pixels);
return false;
}
}
private static final class BakedDynBucketOverrideHandler extends ItemOverrideList
{
public static final BakedDynBucketOverrideHandler INSTANCE = new BakedDynBucketOverrideHandler();
private BakedDynBucketOverrideHandler()
{
super(ImmutableList.of());
}
@Override
public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
{
FluidStack fluidStack = FluidUtil.getFluidContained(stack);
// not a fluid item apparently
if (fluidStack == null)
{
// empty bucket
return originalModel;
}
BakedDynBucket model = (BakedDynBucket)originalModel;
Fluid fluid = fluidStack.getFluid();
String name = fluid.getName();
if (!model.cache.containsKey(name))
{
IModel parent = model.parent.process(ImmutableMap.of("fluid", name));
Function<ResourceLocation, TextureAtlasSprite> textureGetter;
textureGetter = location -> Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString());
IBakedModel bakedModel = parent.bake(new SimpleModelState(model.transforms), model.format, textureGetter);
model.cache.put(name, bakedModel);
return bakedModel;
}
return model.cache.get(name);
}
}
// the dynamic bucket is based on the empty bucket
private static final class BakedDynBucket extends BakedItemModel
{
private final ModelDynBucket parent;
private final Map<String, IBakedModel> cache; // contains all the baked models since they'll never change
private final VertexFormat format;
public BakedDynBucket(ModelDynBucket parent,
ImmutableList<BakedQuad> quads,
TextureAtlasSprite particle,
VertexFormat format,
ImmutableMap<TransformType, TRSRTransformation> transforms,
Map<String, IBakedModel> cache)
{
super(quads, particle, transforms, BakedDynBucketOverrideHandler.INSTANCE);
this.format = format;
this.parent = parent;
this.cache = cache;
}
}
}

View File

@@ -0,0 +1,510 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.function.Function;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector4f;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
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.client.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.fluids.BlockFluidBase;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
public final class ModelFluid implements IModel
{
public static final ModelFluid WATER = new ModelFluid(FluidRegistry.WATER);
public static final ModelFluid LAVA = new ModelFluid(FluidRegistry.LAVA);
private final Fluid fluid;
public ModelFluid(Fluid fluid)
{
this.fluid = fluid;
}
@Override
public Collection<ResourceLocation> getTextures()
{
return fluid.getOverlay() != null
? ImmutableSet.of(fluid.getStill(), fluid.getFlowing(), fluid.getOverlay())
: ImmutableSet.of(fluid.getStill(), fluid.getFlowing());
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
return new CachingBakedFluid(
state.apply(Optional.empty()),
PerspectiveMapWrapper.getTransforms(state),
format,
fluid.getColor(),
bakedTextureGetter.apply(fluid.getStill()),
bakedTextureGetter.apply(fluid.getFlowing()),
Optional.ofNullable(fluid.getOverlay()).map(bakedTextureGetter),
fluid.isLighterThanAir(),
Optional.empty()
);
}
public enum FluidLoader implements ICustomModelLoader
{
INSTANCE;
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {}
@Override
public boolean accepts(ResourceLocation modelLocation)
{
return modelLocation.getResourceDomain().equals(ForgeVersion.MOD_ID) && (
modelLocation.getResourcePath().equals("fluid") ||
modelLocation.getResourcePath().equals("models/block/fluid") ||
modelLocation.getResourcePath().equals("models/item/fluid"));
}
@Override
public IModel loadModel(ResourceLocation modelLocation)
{
return WATER;
}
}
private static final class CachingBakedFluid extends BakedFluid
{
private final LoadingCache<Long, BakedFluid> modelCache = CacheBuilder.newBuilder().maximumSize(200).build(new CacheLoader<Long, BakedFluid>()
{
@Override
public BakedFluid load(Long key)
{
boolean statePresent = (key & 1) != 0;
key >>>= 1;
int[] cornerRound = new int[4];
for (int i = 0; i < 4; i++)
{
cornerRound[i] = (int) (key & 0x3FF);
key >>>= 10;
}
int flowRound = (int) (key & 0x7FF) - 1024;
key >>>= 11;
boolean[] overlaySides = new boolean[4];
for (int i = 0; i < 4; i++)
{
overlaySides[i] = (key & 1) != 0;
key >>>= 1;
}
return new BakedFluid(transformation, transforms, format, color, still, flowing, overlay, gas, statePresent, cornerRound, flowRound, overlaySides);
}
});
public CachingBakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional<TextureAtlasSprite> overlay, boolean gas, Optional<IExtendedBlockState> stateOption)
{
super(transformation, transforms, format, color, still, flowing, overlay, gas, stateOption.isPresent(), getCorners(stateOption), getFlow(stateOption), getOverlay(stateOption));
}
/**
* Gets the quantized fluid levels for each corner.
*
* Each value is packed into 10 bits of the model key, so max range is [0,1024).
* The value is currently stored/interpreted as the closest multiple of 1/864.
* The divisor is chosen here to allows likely flow values to be exactly representable
* while also providing good use of the available value range.
* (For fluids with default quanta, this evenly divides the per-block intervals of 1/9 by 96)
*/
private static int[] getCorners(Optional<IExtendedBlockState> stateOption)
{
int[] cornerRound = {0, 0, 0, 0};
if (stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
for (int i = 0; i < 4; i++)
{
Float level = state.getValue(BlockFluidBase.LEVEL_CORNERS[i]);
cornerRound[i] = Math.round((level == null ? 8f / 9f : level) * 864);
}
}
return cornerRound;
}
/**
* Gets the quantized flow direction of the fluid.
*
* This value comprises 11 bits of the model key, and is signed, so the max range is [-1024,1024).
* The value is currently stored as the angle rounded to the nearest degree.
* A value of -1000 is used to signify no flow.
*/
private static int getFlow(Optional<IExtendedBlockState> stateOption)
{
Float flow = -1000f;
if (stateOption.isPresent())
{
flow = stateOption.get().getValue(BlockFluidBase.FLOW_DIRECTION);
if (flow == null) flow = -1000f;
}
int flowRound = (int) Math.round(Math.toDegrees(flow));
flowRound = MathHelper.clamp(flowRound, -1000, 1000);
return flowRound;
}
/**
* Gets the overlay texture flag for each side.
*
* This value determines if the fluid "overlay" texture should be used for that side,
* instead of the normal "flowing" texture (if applicable for that fluid).
* The sides are stored here by their regular horizontal index.
*/
private static boolean[] getOverlay(Optional<IExtendedBlockState> stateOption)
{
boolean[] overlaySides = new boolean[4];
if (stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
for (int i = 0; i < 4; i++)
{
Boolean overlay = state.getValue(BlockFluidBase.SIDE_OVERLAYS[i]);
if (overlay != null) overlaySides[i] = overlay;
}
}
return overlaySides;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
if (side != null && state instanceof IExtendedBlockState)
{
Optional<IExtendedBlockState> exState = Optional.of((IExtendedBlockState)state);
int[] cornerRound = getCorners(exState);
int flowRound = getFlow(exState);
boolean[] overlaySides = getOverlay(exState);
long key = 0L;
for (int i = 3; i >= 0; i--)
{
key <<= 1;
key |= overlaySides[i] ? 1 : 0;
}
key <<= 11;
key |= flowRound + 1024;
for (int i = 3; i >= 0; i--)
{
key <<= 10;
key |= cornerRound[i];
}
key <<= 1;
key |= 1;
return modelCache.getUnchecked(key).getQuads(state, side, rand);
}
return super.getQuads(state, side, rand);
}
}
private static class BakedFluid implements IBakedModel
{
private static final int x[] = { 0, 0, 1, 1 };
private static final int z[] = { 0, 1, 1, 0 };
private static final float eps = 1e-3f;
protected final Optional<TRSRTransformation> transformation;
protected final ImmutableMap<TransformType, TRSRTransformation> transforms;
protected final VertexFormat format;
protected final int color;
protected final TextureAtlasSprite still, flowing;
protected final Optional<TextureAtlasSprite> overlay;
protected final boolean gas;
protected final ImmutableMap<EnumFacing, ImmutableList<BakedQuad>> faceQuads;
public BakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional<TextureAtlasSprite> overlay, boolean gas, boolean statePresent, int[] cornerRound, int flowRound, boolean[] sideOverlays)
{
this.transformation = transformation;
this.transforms = transforms;
this.format = format;
this.color = color;
this.still = still;
this.flowing = flowing;
this.overlay = overlay;
this.gas = gas;
this.faceQuads = buildQuads(statePresent, cornerRound, flowRound, sideOverlays);
}
private ImmutableMap<EnumFacing, ImmutableList<BakedQuad>> buildQuads(boolean statePresent, int[] cornerRound, int flowRound, boolean[] sideOverlays)
{
EnumMap<EnumFacing, ImmutableList<BakedQuad>> faceQuads = new EnumMap<>(EnumFacing.class);
for (EnumFacing side : EnumFacing.values())
{
faceQuads.put(side, ImmutableList.of());
}
if (statePresent)
{
// y levels
float[] y = new float[4];
boolean fullVolume = true;
for (int i = 0; i < 4; i++)
{
float value = cornerRound[i] / 864f;
if (value < 1f) fullVolume = false;
y[i] = gas ? 1f - value : value;
}
// flow
boolean isFlowing = flowRound > -1000;
float flow = isFlowing ? (float) Math.toRadians(flowRound) : 0f;
TextureAtlasSprite topSprite = isFlowing ? flowing : still;
float scale = isFlowing ? 4f : 8f;
float c = MathHelper.cos(flow) * scale;
float s = MathHelper.sin(flow) * scale;
// top
EnumFacing top = gas ? EnumFacing.DOWN : EnumFacing.UP;
// base uv offset for flow direction
VertexParameter uv = i -> c * (x[i] * 2 - 1) + s * (z[i] * 2 - 1);
VertexParameter topX = i -> x[i];
VertexParameter topY = i -> y[i];
VertexParameter topZ = i -> z[i];
VertexParameter topU = i -> 8 + uv.get(i);
VertexParameter topV = i -> 8 + uv.get((i + 1) % 4);
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
builder.add(buildQuad(top, topSprite, gas, false, topX, topY, topZ, topU, topV));
if (!fullVolume) builder.add(buildQuad(top, topSprite, !gas, true, topX, topY, topZ, topU, topV));
faceQuads.put(top, builder.build());
}
// bottom
EnumFacing bottom = top.getOpposite();
faceQuads.put(bottom, ImmutableList.of(
buildQuad(bottom, still, gas, false,
i -> z[i],
i -> gas ? 1 : 0,
i -> x[i],
i -> z[i] * 16,
i -> x[i] * 16
)
));
// sides
for (int i = 0; i < 4; i++)
{
EnumFacing side = EnumFacing.getHorizontal((5 - i) % 4); // [W, S, E, N]
boolean useOverlay = overlay.isPresent() && sideOverlays[side.getHorizontalIndex()];
int si = i; // local var for lambda capture
VertexParameter sideX = j -> x[(si + x[j]) % 4];
VertexParameter sideY = j -> z[j] == 0 ? (gas ? 1 : 0) : y[(si + x[j]) % 4];
VertexParameter sideZ = j -> z[(si + x[j]) % 4];
VertexParameter sideU = j -> x[j] * 8;
VertexParameter sideV = j -> (gas ? sideY.get(j) : 1 - sideY.get(j)) * 8;
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
if (!useOverlay) builder.add(buildQuad(side, flowing, gas, true, sideX, sideY, sideZ, sideU, sideV));
builder.add(buildQuad(side, useOverlay ? overlay.get() : flowing, !gas, false, sideX, sideY, sideZ, sideU, sideV));
faceQuads.put(side, builder.build());
}
}
else
{
// inventory
faceQuads.put(EnumFacing.SOUTH, ImmutableList.of(
buildQuad(EnumFacing.UP, still, false, false,
i -> z[i],
i -> x[i],
i -> 0,
i -> z[i] * 16,
i -> x[i] * 16
)
));
}
return ImmutableMap.copyOf(faceQuads);
}
// maps vertex index to parameter value
private interface VertexParameter
{
float get(int index);
}
private BakedQuad buildQuad(EnumFacing side, TextureAtlasSprite texture, boolean flip, boolean offset, VertexParameter x, VertexParameter y, VertexParameter z, VertexParameter u, VertexParameter v)
{
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(texture);
builder.setQuadTint(0);
for (int i = 0; i < 4; i++)
{
int vertex = flip ? 3 - i : i;
putVertex(
builder, side, offset,
x.get(vertex), y.get(vertex), z.get(vertex),
texture.getInterpolatedU(u.get(vertex)),
texture.getInterpolatedV(v.get(vertex))
);
}
return builder.build();
}
private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, boolean offset, float x, float y, float z, float u, float v)
{
for(int e = 0; e < format.getElementCount(); e++)
{
switch(format.getElement(e).getUsage())
{
case POSITION:
float dx = offset ? side.getDirectionVec().getX() * eps : 0f;
float dy = offset ? side.getDirectionVec().getY() * eps : 0f;
float dz = offset ? side.getDirectionVec().getZ() * eps : 0f;
float[] data = { x - dx, y - dy, z - dz, 1f };
if(transformation.isPresent() && !transformation.get().isIdentity())
{
Vector4f vec = new Vector4f(data);
transformation.get().getMatrix().transform(vec);
vec.get(data);
}
builder.put(e, data);
break;
case COLOR:
builder.put(e,
((color >> 16) & 0xFF) / 255f,
((color >> 8) & 0xFF) / 255f,
(color & 0xFF) / 255f,
((color >> 24) & 0xFF) / 255f);
break;
case UV: if(format.getElement(e).getIndex() == 0)
{
builder.put(e, u, v, 0f, 1f);
break;
}
case NORMAL:
builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f);
break;
default:
builder.put(e);
break;
}
}
}
@Override
public boolean isAmbientOcclusion()
{
return true;
}
@Override
public boolean isGui3d()
{
return false;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return still;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
return side == null ? ImmutableList.of() : faceQuads.get(side);
}
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType type)
{
return PerspectiveMapWrapper.handlePerspective(this, transforms, type);
}
}
@Override
public ModelFluid process(ImmutableMap<String, String> customData)
{
if(!customData.containsKey("fluid")) return this;
String fluidStr = customData.get("fluid");
JsonElement e = new JsonParser().parse(fluidStr);
String fluid = e.getAsString();
if(!FluidRegistry.isFluidRegistered(fluid))
{
FMLLog.log.fatal("fluid '{}' not found", fluid);
return WATER;
}
return new ModelFluid(FluidRegistry.getFluid(fluid));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.Deque;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ModelLoader.VanillaLoader;
import net.minecraftforge.client.model.ModelLoader.VariantLoader;
import net.minecraftforge.client.model.b3d.B3DLoader;
import net.minecraftforge.client.model.obj.OBJLoader;
import net.minecraftforge.common.animation.ITimeValue;
import net.minecraftforge.common.model.animation.AnimationStateMachine;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
import net.minecraftforge.fml.common.FMLLog;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
/*
* Central hub for custom model loaders.
*/
public class ModelLoaderRegistry
{
private static final Set<ICustomModelLoader> loaders = Sets.newHashSet();
private static final Map<ResourceLocation, IModel> cache = Maps.newHashMap();
private static final Deque<ResourceLocation> loadingModels = Queues.newArrayDeque();
private static final Set<ResourceLocation> textures = Sets.newHashSet();
private static IResourceManager manager;
// Forge built-in loaders
static
{
registerLoader(B3DLoader.INSTANCE);
registerLoader(OBJLoader.INSTANCE);
registerLoader(ModelFluid.FluidLoader.INSTANCE);
registerLoader(ItemLayerModel.Loader.INSTANCE);
registerLoader(MultiLayerModel.Loader.INSTANCE);
registerLoader(ModelDynBucket.LoaderDynBucket.INSTANCE);
}
/*
* Makes system aware of your loader.
*/
public static void registerLoader(ICustomModelLoader loader)
{
loaders.add(loader);
((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener(loader);
}
public static boolean loaded(ResourceLocation location)
{
return cache.containsKey(location);
}
public static ResourceLocation getActualLocation(ResourceLocation location)
{
if(location instanceof ModelResourceLocation) return location;
if(location.getResourcePath().startsWith("builtin/")) return location;
return new ResourceLocation(location.getResourceDomain(), "models/" + location.getResourcePath());
}
/**
* Primary method to get IModel instances.
* ResourceLocation argument will be passed directly to the custom model loaders,
* ModelResourceLocation argument will be loaded through the blockstate system.
*/
public static IModel getModel(ResourceLocation location) throws Exception
{
IModel model;
if(cache.containsKey(location)) return cache.get(location);
for(ResourceLocation loading : loadingModels)
{
if(location.getClass() == loading.getClass() && location.equals(loading))
{
throw new LoaderException("circular model dependencies, stack: [" + Joiner.on(", ").join(loadingModels) + "]");
}
}
loadingModels.addLast(location);
try
{
ResourceLocation actual = getActualLocation(location);
ICustomModelLoader accepted = null;
for(ICustomModelLoader loader : loaders)
{
try
{
if(loader.accepts(actual))
{
if(accepted != null)
{
throw new LoaderException(String.format("2 loaders (%s and %s) want to load the same model %s", accepted, loader, location));
}
accepted = loader;
}
}
catch(Exception e)
{
throw new LoaderException(String.format("Exception checking if model %s can be loaded with loader %s, skipping", location, loader), e);
}
}
// no custom loaders found, try vanilla ones
if(accepted == null)
{
if(VariantLoader.INSTANCE.accepts(actual))
{
accepted = VariantLoader.INSTANCE;
}
else if(VanillaLoader.INSTANCE.accepts(actual))
{
accepted = VanillaLoader.INSTANCE;
}
}
if(accepted == null)
{
throw new LoaderException("no suitable loader found for the model " + location + ", skipping");
}
try
{
model = accepted.loadModel(actual);
}
catch(Exception e)
{
throw new LoaderException(String.format("Exception loading model %s with loader %s, skipping", location, accepted), e);
}
if(model == getMissingModel())
{
throw new LoaderException(String.format("Loader %s returned missing model while loading model %s", accepted, location));
}
if(model == null)
{
throw new LoaderException(String.format("Loader %s returned null while loading model %s", accepted, location));
}
textures.addAll(model.getTextures());
}
finally
{
ResourceLocation popLoc = loadingModels.removeLast();
if(popLoc != location)
{
throw new IllegalStateException("Corrupted loading model stack: " + popLoc + " != " + location);
}
}
cache.put(location, model);
for (ResourceLocation dep : model.getDependencies())
{
getModelOrMissing(dep);
}
return model;
}
/**
* Use this if you don't care about the exception and want some model anyway.
*/
public static IModel getModelOrMissing(ResourceLocation location)
{
try
{
return getModel(location);
}
catch(Exception e)
{
return getMissingModel(location, e);
}
}
/**
* Use this if you want the model, but need to log the error.
*/
public static IModel getModelOrLogError(ResourceLocation location, String error)
{
try
{
return getModel(location);
}
catch(Exception e)
{
FMLLog.log.error(error, e);
return getMissingModel(location, e);
}
}
public static IModel getMissingModel()
{
final ModelLoader loader = VanillaLoader.INSTANCE.getLoader();
if(loader == null)
{
throw new IllegalStateException("Using ModelLoaderRegistry too early.");
}
return loader.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;
cache.clear();
// putting the builtin models in
cache.put(new ResourceLocation("minecraft:builtin/generated"), ItemLayerModel.INSTANCE);
cache.put(new ResourceLocation("minecraft:block/builtin/generated"), ItemLayerModel.INSTANCE);
cache.put(new ResourceLocation("minecraft:item/builtin/generated"), ItemLayerModel.INSTANCE);
}
static Iterable<ResourceLocation> getTextures()
{
return textures;
}
public static class LoaderException extends Exception
{
public LoaderException(String message)
{
super(message);
}
public LoaderException(String message, Throwable cause)
{
super(message, cause);
}
private static final long serialVersionUID = 1L;
}
public static IAnimationStateMachine loadASM(ResourceLocation location, ImmutableMap<String, ITimeValue> customParameters)
{
return AnimationStateMachine.load(manager, location, customParameters);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import com.google.common.base.Objects;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import java.util.Optional;
public class ModelStateComposition implements IModelState
{
private final IModelState first;
private final IModelState second;
public ModelStateComposition(IModelState first, IModelState second)
{
this.first = first;
this.second = second;
}
@Override
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
Optional<TRSRTransformation> f = first.apply(part), s = second.apply(part);
if(f.isPresent() && s.isPresent())
{
return Optional.of(f.get().compose(s.get()));
}
if (f.isPresent()) {
return f;
}
return s;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
ModelStateComposition that = (ModelStateComposition) o;
return Objects.equal(first, that.first) && Objects.equal(second, that.second);
}
@Override
public int hashCode()
{
return Objects.hashCode(first, second);
}
}

View File

@@ -0,0 +1,225 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Function;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
public final class MultiLayerModel implements IModel
{
public static final MultiLayerModel INSTANCE = new MultiLayerModel(ImmutableMap.of());
private final ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models;
public MultiLayerModel(ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models)
{
this.models = models;
}
@Override
public Collection<ResourceLocation> getDependencies()
{
return ImmutableList.copyOf(models.values());
}
private static ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> buildModels(ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models, IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableMap.Builder<Optional<BlockRenderLayer>, IBakedModel> builder = ImmutableMap.builder();
for(Optional<BlockRenderLayer> key : models.keySet())
{
IModel model = ModelLoaderRegistry.getModelOrLogError(models.get(key), "Couldn't load MultiLayerModel dependency: " + models.get(key));
builder.put(key, model.bake(new ModelStateComposition(state, model.getDefaultState()), format, bakedTextureGetter));
}
return builder.build();
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
IModel missing = ModelLoaderRegistry.getMissingModel();
return new MultiLayerBakedModel(
buildModels(models, state, format, bakedTextureGetter),
missing.bake(missing.getDefaultState(), format, bakedTextureGetter),
PerspectiveMapWrapper.getTransforms(state)
);
}
@Override
public MultiLayerModel process(ImmutableMap<String, String> customData)
{
ImmutableMap.Builder<Optional<BlockRenderLayer>, ModelResourceLocation> builder = ImmutableMap.builder();
for(String key : customData.keySet())
{
if("base".equals(key))
{
builder.put(Optional.empty(), getLocation(customData.get(key)));
}
for(BlockRenderLayer layer : BlockRenderLayer.values())
{
if(layer.toString().equals(key))
{
builder.put(Optional.of(layer), getLocation(customData.get(key)));
}
}
}
ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models = builder.build();
if(models.isEmpty()) return INSTANCE;
return new MultiLayerModel(models);
}
private ModelResourceLocation getLocation(String json)
{
JsonElement e = new JsonParser().parse(json);
if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isString())
{
return new ModelResourceLocation(e.getAsString());
}
FMLLog.log.fatal("Expect ModelResourceLocation, got: {}", json);
return new ModelResourceLocation("builtin/missing", "missing");
}
private static final class MultiLayerBakedModel implements IBakedModel
{
private final ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> models;
private final ImmutableMap<TransformType, TRSRTransformation> cameraTransforms;
private final IBakedModel base;
private final IBakedModel missing;
public MultiLayerBakedModel(ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> models, IBakedModel missing, ImmutableMap<TransformType, TRSRTransformation> cameraTransforms)
{
this.models = models;
this.cameraTransforms = cameraTransforms;
this.missing = missing;
this.base = models.getOrDefault(Optional.empty(), missing);
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
BlockRenderLayer layer = MinecraftForgeClient.getRenderLayer();
if (layer == null)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
for (IBakedModel model : models.values())
{
builder.addAll(model.getQuads(state, side, rand));
}
return builder.build();
}
// assumes that child model will handle this state properly. FIXME?
return models.getOrDefault(Optional.of(layer), missing).getQuads(state, side, rand);
}
@Override
public boolean isAmbientOcclusion()
{
return base.isAmbientOcclusion();
}
@Override
public boolean isAmbientOcclusion(IBlockState state)
{
return base.isAmbientOcclusion(state);
}
@Override
public boolean isGui3d()
{
return base.isGui3d();
}
@Override
public boolean isBuiltInRenderer()
{
return base.isBuiltInRenderer();
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return base.getParticleTexture();
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
{
return PerspectiveMapWrapper.handlePerspective(this, cameraTransforms, cameraTransformType);
}
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
}
}
public static enum Loader implements ICustomModelLoader
{
INSTANCE;
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {}
@Override
public boolean accepts(ResourceLocation modelLocation)
{
return modelLocation.getResourceDomain().equals(ForgeVersion.MOD_ID) && (
modelLocation.getResourcePath().equals("multi-layer") ||
modelLocation.getResourcePath().equals("models/block/multi-layer") ||
modelLocation.getResourcePath().equals("models/item/multi-layer"));
}
@Override
public IModel loadModel(ResourceLocation modelLocation)
{
return MultiLayerModel.INSTANCE;
}
}
}

View File

@@ -0,0 +1,298 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import net.minecraft.block.state.IBlockState;
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.ItemCameraTransforms.TransformType;
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.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
// TODO: Switch to vanilla class, or to something similar
@Deprecated
public final class MultiModel implements IModel
{
private static final class Baked implements IBakedModel
{
private final ResourceLocation location;
@Nullable
private final IBakedModel base;
private final ImmutableMap<String, IBakedModel> parts;
private final IBakedModel internalBase;
private final ImmutableMap<TransformType, Pair<Baked, TRSRTransformation>> transforms;
private final ItemOverrideList overrides = new ItemOverrideList(Lists.newArrayList())
{
@Override
public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
{
if(originalModel != Baked.this)
{
return originalModel;
}
boolean dirty = false;
IBakedModel newBase = null;
if(base != null)
{
newBase = base.getOverrides().handleItemState(base, stack, world, entity);
if(base != newBase)
{
dirty = true;
}
}
ImmutableMap.Builder<String, IBakedModel> builder = ImmutableMap.builder();
for(Map.Entry<String, IBakedModel> entry : parts.entrySet())
{
IBakedModel newPart = entry.getValue().getOverrides().handleItemState(entry.getValue(), stack, world, entity);
builder.put(entry.getKey(), newPart);
if(entry.getValue() != newPart)
{
dirty = true;
}
}
if(dirty)
{
// TODO: caching?
return new Baked(location, true, newBase, builder.build());
}
return Baked.this;
}
};
public Baked(ResourceLocation location, boolean perspective, @Nullable IBakedModel base, ImmutableMap<String, IBakedModel> parts)
{
this.location = location;
this.base = base;
this.parts = parts;
if (base != null)
internalBase = base;
else
{
Iterator<IBakedModel> iter = parts.values().iterator();
if (iter.hasNext())
internalBase = iter.next();
else
throw new IllegalArgumentException("No base model or submodel provided for MultiModel.Baked " + location + ".");
}
// Only changes the base model based on perspective, may recurse for parts in the future.
if(base != null && perspective)
{
EnumMap<TransformType, Pair<Baked, TRSRTransformation>> map = new EnumMap<>(TransformType.class);
for(TransformType type : TransformType.values())
{
Pair<? extends IBakedModel, Matrix4f> p = base.handlePerspective(type);
IBakedModel newBase = p.getLeft();
Matrix4f matrix = p.getRight();
if (newBase != base || matrix != null)
{
map.put(type, Pair.of(new Baked(location, false, newBase, parts), new TRSRTransformation(matrix)));
}
}
transforms = ImmutableMap.copyOf(map);
}
else
{
transforms = ImmutableMap.of();
}
}
@Override
public boolean isAmbientOcclusion()
{
return internalBase.isAmbientOcclusion();
}
@Override
public boolean isAmbientOcclusion(IBlockState state)
{
return internalBase.isAmbientOcclusion(state);
}
@Override
public boolean isGui3d()
{
return internalBase.isGui3d();
}
@Override
public boolean isBuiltInRenderer()
{
return internalBase.isBuiltInRenderer();
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return internalBase.getParticleTexture();
}
@Override
public ItemCameraTransforms getItemCameraTransforms()
{
return internalBase.getItemCameraTransforms();
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
ImmutableList.Builder<BakedQuad> quads = ImmutableList.builder();
if (base != null)
{
quads.addAll(base.getQuads(state, side, rand));
}
for (IBakedModel bakedPart : parts.values())
{
quads.addAll(bakedPart.getQuads(state, side, rand));
}
return quads.build();
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
{
Pair<Baked, TRSRTransformation> p = transforms.get(cameraTransformType);
if (p == null) return Pair.of(this, null);
return Pair.of(p.getLeft(), p.getRight().getMatrix());
}
@Override
public ItemOverrideList getOverrides()
{
return overrides;
}
}
private final ResourceLocation location;
@Nullable
private final IModel base;
private final Map<String, Pair<IModel, IModelState>> parts;
// TODO 1.13 remove, kept for binary compatibility
@Deprecated
public MultiModel(ResourceLocation location, @Nullable IModel base, IModelState baseState, ImmutableMap<String, Pair<IModel, IModelState>> parts)
{
this(location, base, parts);
}
public MultiModel(ResourceLocation location, @Nullable IModel base, ImmutableMap<String, Pair<IModel, IModelState>> parts)
{
this.location = location;
this.base = base;
this.parts = parts;
}
// TODO 1.13 remove, kept for binary compatibility
@Deprecated
public MultiModel(ResourceLocation location, IModel base, IModelState baseState, Map<String, Pair<IModel, IModelState>> parts)
{
this(location, base, parts);
}
public MultiModel(ResourceLocation location, IModel base, Map<String, Pair<IModel, IModelState>> parts)
{
this(location, base, ImmutableMap.copyOf(parts));
}
@Override
public Collection<ResourceLocation> getDependencies()
{
Set<ResourceLocation> deps = Sets.newHashSet();
if (base != null)
deps.addAll(base.getDependencies());
for (Pair<IModel, IModelState> pair : parts.values())
deps.addAll(pair.getLeft().getDependencies());
return deps;
}
@Override
public Collection<ResourceLocation> getTextures()
{
Set<ResourceLocation> deps = Sets.newHashSet();
if (base != null)
deps.addAll(base.getTextures());
for (Pair<IModel, IModelState> pair : parts.values())
deps.addAll(pair.getLeft().getTextures());
return deps;
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
IBakedModel bakedBase = null;
if (base != null)
bakedBase = base.bake(state, format, bakedTextureGetter);
ImmutableMap.Builder<String, IBakedModel> mapBuilder = ImmutableMap.builder();
for (Entry<String, Pair<IModel, IModelState>> entry : parts.entrySet())
{
Pair<IModel, IModelState> pair = entry.getValue();
mapBuilder.put(entry.getKey(), pair.getLeft().bake(new ModelStateComposition(state, pair.getRight()), format, bakedTextureGetter));
}
if(bakedBase == null && parts.isEmpty())
{
FMLLog.log.error("MultiModel {} is empty (no base model or parts were provided/resolved)", location);
IModel missing = ModelLoaderRegistry.getMissingModel();
return missing.bake(missing.getDefaultState(), format, bakedTextureGetter);
}
return new Baked(location, true, bakedBase, mapBuilder.build());
}
}

View File

@@ -0,0 +1,156 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Objects;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public final class MultiModelState implements IModelState
{
private final ImmutableMap<MultiModelPart, ? extends IModelState> states;
public <M extends IModel, S extends IModelState> MultiModelState(ImmutableList<Pair<M, S>> states)
{
ImmutableMap.Builder<MultiModelPart, S> builder = ImmutableMap.builder();
for(int i = 0; i < states.size(); i++)
{
Pair<M, S> pair = states.get(i);
builder.put(new MultiModelPart(pair.getLeft(), i), pair.getRight());
}
this.states = builder.build();
}
public static IModelState getPartState(IModelState state, IModel model, int index)
{
if(state.apply(Optional.of(new MultiModelPart(model, index))).isPresent())
{
return new PartState(state, model, index);
}
return state;
}
@Override
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
if(part.isPresent())
{
if(part.get() instanceof MultiModelPart)
{
MultiModelPart key = (MultiModelPart)part.get();
if(states.containsKey(key))
{
return Optional.of(states.get(key).apply(Optional.empty()).orElse(TRSRTransformation.identity()));
}
}
else if(part.get() instanceof PartPart)
{
PartPart partPart = (PartPart)part.get();
MultiModelPart key = new MultiModelPart(partPart.model, partPart.index);
if(states.containsKey(key))
{
return states.get(key).apply(partPart.part);
}
}
}
return Optional.empty();
}
private static class PartState implements IModelState
{
private final IModelState state;
private final IModel model;
private final int index;
public PartState(IModelState state, IModel model, int index)
{
this.state = state;
this.model = model;
this.index = index;
}
@Override
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
Optional<TRSRTransformation> normal = state.apply(part);
Optional<TRSRTransformation> multi = state.apply(Optional.of(new PartPart(model, index, part)));
if(normal.isPresent() && multi.isPresent())
{
return Optional.of(normal.get().compose(multi.get()));
}
if (normal.isPresent()) {
return normal;
}
return multi;
}
}
private static class MultiModelPart implements IModelPart
{
private final IModel model;
private final int index;
public MultiModelPart(IModel model, int index)
{
this.model = model;
this.index = index;
}
@Override
public int hashCode()
{
return Objects.hashCode(model, index);
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MultiModelPart other = (MultiModelPart)obj;
return Objects.equal(this.model, other.model) && this.index == other.index;
}
}
private static class PartPart implements IModelPart
{
private final IModel model;
private final int index;
private final Optional<? extends IModelPart> part;
public PartPart(IModel model, int index, Optional<? extends IModelPart> part)
{
this.model = model;
this.index = index;
this.part = part;
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import java.util.EnumMap;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import net.minecraft.block.state.IBlockState;
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.util.EnumFacing;
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 java.util.List;
public class PerspectiveMapWrapper implements IBakedModel
{
private final IBakedModel parent;
private final ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms;
public PerspectiveMapWrapper(IBakedModel parent, ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms)
{
this.parent = parent;
this.transforms = transforms;
}
public PerspectiveMapWrapper(IBakedModel parent, IModelState state)
{
this(parent, getTransforms(state));
}
public static ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> getTransforms(IModelState state)
{
EnumMap<ItemCameraTransforms.TransformType, TRSRTransformation> map = new EnumMap<>(ItemCameraTransforms.TransformType.class);
for(ItemCameraTransforms.TransformType type : ItemCameraTransforms.TransformType.values())
{
Optional<TRSRTransformation> tr = state.apply(Optional.of(type));
if(tr.isPresent())
{
map.put(type, tr.get());
}
}
return ImmutableMap.copyOf(map);
}
@SuppressWarnings("deprecation")
public static ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> getTransforms(ItemCameraTransforms transforms)
{
EnumMap<ItemCameraTransforms.TransformType, TRSRTransformation> map = new EnumMap<>(ItemCameraTransforms.TransformType.class);
for(ItemCameraTransforms.TransformType type : ItemCameraTransforms.TransformType.values())
{
if (transforms.hasCustomTransform(type))
{
map.put(type, TRSRTransformation.blockCenterToCorner(TRSRTransformation.from(transforms.getTransform(type))));
}
}
return ImmutableMap.copyOf(map);
}
public static Pair<? extends IBakedModel, Matrix4f> handlePerspective(IBakedModel model, ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms, ItemCameraTransforms.TransformType cameraTransformType)
{
TRSRTransformation tr = transforms.getOrDefault(cameraTransformType, TRSRTransformation.identity());
if (!tr.isIdentity())
{
return Pair.of(model, TRSRTransformation.blockCornerToCenter(tr).getMatrix());
}
return Pair.of(model, null);
}
public static Pair<? extends IBakedModel, Matrix4f> handlePerspective(IBakedModel model, IModelState state, ItemCameraTransforms.TransformType cameraTransformType)
{
TRSRTransformation tr = state.apply(Optional.of(cameraTransformType)).orElse(TRSRTransformation.identity());
if (!tr.isIdentity())
{
return Pair.of(model, TRSRTransformation.blockCornerToCenter(tr).getMatrix());
}
return Pair.of(model, null);
}
@Override public boolean isAmbientOcclusion() { return parent.isAmbientOcclusion(); }
@Override public boolean isAmbientOcclusion(IBlockState state) { return parent.isAmbientOcclusion(state); }
@Override public boolean isGui3d() { return parent.isGui3d(); }
@Override public boolean isBuiltInRenderer() { return parent.isBuiltInRenderer(); }
@Override public TextureAtlasSprite getParticleTexture() { return parent.getParticleTexture(); }
@SuppressWarnings("deprecation")
@Override public ItemCameraTransforms getItemCameraTransforms() { return parent.getItemCameraTransforms(); }
@Override public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) { return parent.getQuads(state, side, rand); }
@Override public ItemOverrideList getOverrides() { return parent.getOverrides(); }
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
{
return handlePerspective(this, transforms, cameraTransformType);
}
}

View File

@@ -0,0 +1,210 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
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.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<BakedQuad> 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);
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;
}
/**
* Render a single character with the default.png font at current (posX,posY) location...
*/
@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.setTexture(sprite);
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.setTexture(sprite);
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.setTexture(sprite);
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);
}
/**
* Render a single Unicode character at current (posX,posY) location using one of the /font/glyph_XX.png files...
*/
@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()
{
}
@Override
protected void bindTexture(ResourceLocation location)
{
}
public ImmutableList<BakedQuad> build()
{
ImmutableList<BakedQuad> ret = builder.build();
builder = ImmutableList.builder();
return ret;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
/*
* Simple implementation of IModelState via a map and a default value.
*/
public final class SimpleModelState implements IModelState
{
private final ImmutableMap<? extends IModelPart, TRSRTransformation> map;
private final Optional<TRSRTransformation> def;
public SimpleModelState(ImmutableMap<? extends IModelPart, TRSRTransformation> map)
{
this(map, Optional.empty());
}
public SimpleModelState(ImmutableMap<? extends IModelPart, TRSRTransformation> map, Optional<TRSRTransformation> def)
{
this.map = map;
this.def = def;
}
@Override
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
if(!part.isPresent())
{
return def;
}
if(!map.containsKey(part.get()))
{
return Optional.empty();
}
return Optional.ofNullable(map.get(part.get()));
}
}

View File

@@ -0,0 +1,70 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.animation;
import net.minecraft.world.World;
import java.lang.ref.WeakReference;
public enum Animation
{
INSTANCE;
private float clientPartialTickTime;
private static long epochTime;
private static WeakReference<World> worldRef;
/**
* Get the global world time for the current tick, in seconds.
*/
public static float getWorldTime(World world)
{
return getWorldTime(world, 0);
}
/**
* Get the global world time for the current tick + partial tick progress, in seconds.
*/
public static float getWorldTime(World world, float tickProgress)
{
if (worldRef == null || worldRef.get() != world)
{
epochTime = world.getTotalWorldTime();
worldRef = new WeakReference<>(world);
}
return (world.getTotalWorldTime() - epochTime + tickProgress) / 20;
}
/**
* Get current partialTickTime.
*/
public static float getPartialTickTime()
{
return INSTANCE.clientPartialTickTime;
}
/**
* Internal hook, do not use.
*/
public static void setClientPartialTickTime(float clientPartialTickTime) {
Animation.INSTANCE.clientPartialTickTime = clientPartialTickTime;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.animation;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemOverride;
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.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelStateComposition;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.animation.CapabilityAnimation;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
import java.util.function.Function;
import javax.annotation.Nullable;
public final class AnimationItemOverrideList extends ItemOverrideList
{
private final IModel model;
private final IModelState state;
private final VertexFormat format;
private final Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter;
public AnimationItemOverrideList(IModel model, IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, ItemOverrideList overrides)
{
this(model, state, format, bakedTextureGetter, overrides.getOverrides().reverse());
}
public AnimationItemOverrideList(IModel model, IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, List<ItemOverride> overrides)
{
super(overrides);
this.model = model;
this.state = state;
this.format = format;
this.bakedTextureGetter = bakedTextureGetter;
}
@Override
public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
{
IAnimationStateMachine asm = stack.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null);
if (asm != null)
{
// TODO: caching?
if(world == null && entity != null)
{
world = entity.world;
}
if(world == null)
{
world = Minecraft.getMinecraft().world;
}
IModelState state = asm.apply(Animation.getWorldTime(world, Animation.getPartialTickTime())).getLeft();
return model.bake(new ModelStateComposition(state, this.state), format, bakedTextureGetter);
}
return super.handleItemState(originalModel, stack, world, entity);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.animation;
import java.util.List;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
import net.minecraftforge.client.model.pipeline.VertexBufferConsumer;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.animation.IEventHandler;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.animation.CapabilityAnimation;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
/**
* ModelBase that works with the Forge model system and animations.
* Some quirks are still left, deprecated for the moment.
*/
@Deprecated
public class AnimationModelBase<T extends Entity> extends ModelBase implements IEventHandler<T>
{
private final VertexLighterFlat lighter;
private final ResourceLocation modelLocation;
public AnimationModelBase(ResourceLocation modelLocation, VertexLighterFlat lighter)
{
this.modelLocation = modelLocation;
this.lighter = lighter;
}
/**
* Sets the models various rotation angles then renders the model.
*/
@SuppressWarnings("unchecked")
@Override
public void render(Entity entity, float limbSwing, float limbSwingSpeed, float timeAlive, float yawHead, float rotationPitch, float scale)
{
IAnimationStateMachine capability = entity.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null);
if (capability == null)
{
return;
}
Pair<IModelState, Iterable<Event>> pair = capability.apply(timeAlive / 20);
handleEvents((T)entity, timeAlive / 20, pair.getRight());
IModel model = ModelLoaderRegistry.getModelOrMissing(modelLocation);
IBakedModel bakedModel = model.bake(pair.getLeft(), DefaultVertexFormats.ITEM, ModelLoader.defaultTextureGetter());
BlockPos pos = new BlockPos(entity.posX, entity.posY + entity.height, entity.posZ);
RenderHelper.disableStandardItemLighting();
GlStateManager.pushMatrix();
GlStateManager.rotate(180, 0, 0, 1);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder builder = tessellator.getBuffer();
builder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
builder.setTranslation(-0.5, -1.5, -0.5);
lighter.setParent(new VertexBufferConsumer(builder));
lighter.setWorld(entity.world);
lighter.setState(Blocks.AIR.getDefaultState());
lighter.setBlockPos(pos);
boolean empty = true;
List<BakedQuad> quads = bakedModel.getQuads(null, null, 0);
if(!quads.isEmpty())
{
lighter.updateBlockInfo();
empty = false;
for(BakedQuad quad : quads)
{
quad.pipe(lighter);
}
}
for(EnumFacing side : EnumFacing.values())
{
quads = bakedModel.getQuads(null, side, 0);
if(!quads.isEmpty())
{
if(empty) lighter.updateBlockInfo();
empty = false;
for(BakedQuad quad : quads)
{
quad.pipe(lighter);
}
}
}
// debug quad
/*VertexBuffer.pos(0, 1, 0).color(0xFF, 0xFF, 0xFF, 0xFF).tex(0, 0).lightmap(240, 0).endVertex();
VertexBuffer.pos(0, 1, 1).color(0xFF, 0xFF, 0xFF, 0xFF).tex(0, 1).lightmap(240, 0).endVertex();
VertexBuffer.pos(1, 1, 1).color(0xFF, 0xFF, 0xFF, 0xFF).tex(1, 1).lightmap(240, 0).endVertex();
VertexBuffer.pos(1, 1, 0).color(0xFF, 0xFF, 0xFF, 0xFF).tex(1, 0).lightmap(240, 0).endVertex();*/
builder.setTranslation(0, 0, 0);
tessellator.draw();
GlStateManager.popMatrix();
RenderHelper.enableStandardItemLighting();
}
@Override
public void handleEvents(T instance, float time, Iterable<Event> pastEvents) {}
}

View File

@@ -0,0 +1,89 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.animation;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.animation.IEventHandler;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.animation.CapabilityAnimation;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.Properties;
import org.apache.commons.lang3.tuple.Pair;
/**
* Generic TileEntitySpecialRenderer that works with the Forge model system and animations.
*/
public class AnimationTESR<T extends TileEntity> extends FastTESR<T> implements IEventHandler<T>
{
protected static BlockRendererDispatcher blockRenderer;
@Override
public void renderTileEntityFast(T te, double x, double y, double z, float partialTick, int breakStage, float partial, BufferBuilder renderer)
{
if(!te.hasCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null))
{
return;
}
if(blockRenderer == null) blockRenderer = Minecraft.getMinecraft().getBlockRendererDispatcher();
BlockPos pos = te.getPos();
IBlockAccess world = MinecraftForgeClient.getRegionRenderCache(te.getWorld(), pos);
IBlockState state = world.getBlockState(pos);
if(state.getPropertyKeys().contains(Properties.StaticProperty))
{
state = state.withProperty(Properties.StaticProperty, false);
}
if(state instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
if(exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
float time = Animation.getWorldTime(getWorld(), partialTick);
IAnimationStateMachine capability = te.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null);
if (capability != null)
{
Pair<IModelState, Iterable<Event>> pair = capability.apply(time);
handleEvents(te, time, pair.getRight());
// TODO: caching?
IBakedModel model = blockRenderer.getBlockModelShapes().getModelForState(exState.getClean());
exState = exState.withProperty(Properties.AnimationProperty, pair.getLeft());
renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ());
blockRenderer.getBlockModelRenderer().renderModel(world, model, exState, pos, renderer, false);
}
}
}
}
@Override
public void handleEvents(T te, float time, Iterable<Event> pastEvents) {}
}

View File

@@ -0,0 +1,68 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.animation;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.tileentity.TileEntity;
public abstract class FastTESR<T extends TileEntity> extends TileEntitySpecialRenderer<T>
{
@Override
public final void render(T te, double x, double y, double z, float partialTicks, int destroyStage, float partial)
{
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
RenderHelper.disableStandardItemLighting();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.enableBlend();
GlStateManager.disableCull();
if (Minecraft.isAmbientOcclusionEnabled())
{
GlStateManager.shadeModel(GL11.GL_SMOOTH);
}
else
{
GlStateManager.shadeModel(GL11.GL_FLAT);
}
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, partial, buffer);
buffer.setTranslation(0, 0, 0);
tessellator.draw();
RenderHelper.enableStandardItemLighting();
}
@Override
public abstract void renderTileEntityFast(T te, double x, double y, double z, float partialTicks, int destroyStage, float partial, BufferBuilder buffer);
}

View File

@@ -0,0 +1,584 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.animation;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.TreeMap;
import javax.annotation.Nullable;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;
import net.minecraft.client.renderer.block.model.BlockPart;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.model.animation.ModelBlockAnimation.Parameter.Interpolation;
import net.minecraftforge.client.model.animation.ModelBlockAnimation.Parameter.Type;
import net.minecraftforge.client.model.animation.ModelBlockAnimation.Parameter.Variable;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.model.animation.IJoint;
import net.minecraftforge.common.model.animation.IJointClip;
import net.minecraftforge.common.model.animation.JointClips;
import net.minecraftforge.common.util.JsonUtils;
import net.minecraftforge.fml.common.FMLLog;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
public class ModelBlockAnimation
{
private final ImmutableMap<String, ImmutableMap<String, float[]>> joints;
private final ImmutableMap<String, MBClip> clips;
private transient ImmutableMultimap<Integer, MBJointWeight> jointIndexMap;
public ModelBlockAnimation(ImmutableMap<String, ImmutableMap<String, float[]>> joints, ImmutableMap<String, MBClip> clips)
{
this.joints = joints;
this.clips = clips;
}
public ImmutableMap<String, ? extends IClip> getClips()
{
return clips;
}
public ImmutableCollection<MBJointWeight> getJoint(int i)
{
if(jointIndexMap == null)
{
ImmutableMultimap.Builder<Integer, MBJointWeight> builder = ImmutableMultimap.builder();
for(Map.Entry<String, ImmutableMap<String, float[]>> info : joints.entrySet())
{
ImmutableMap.Builder<Integer, float[]> weightBuilder = ImmutableMap.builder();
for(Map.Entry<String, float[]> e : info.getValue().entrySet())
{
weightBuilder.put(Integer.parseInt(e.getKey()), e.getValue());
}
ImmutableMap<Integer, float[]> weightMap = weightBuilder.build();
for(Map.Entry<Integer, float[]> e : weightMap.entrySet())
{
builder.put(e.getKey(), new MBJointWeight(info.getKey(), weightMap));
}
}
jointIndexMap = builder.build();
}
return jointIndexMap.get(i);
}
protected static class MBVariableClip
{
private final Variable variable;
@SuppressWarnings("unused")
private final Type type;
private final Interpolation interpolation;
private final float[] samples;
public MBVariableClip(Variable variable, Type type, Interpolation interpolation, float[] samples)
{
this.variable = variable;
this.type = type;
this.interpolation = interpolation;
this.samples = samples;
}
}
protected static class MBClip implements IClip
{
private final boolean loop;
@SerializedName("joint_clips")
private final ImmutableMap<String, ImmutableList<MBVariableClip>> jointClipsFlat;
private transient ImmutableMap<String, MBJointClip> jointClips;
@SerializedName("events")
private final ImmutableMap<String, String> eventsRaw;
private transient TreeMap<Float, Event> events;
public MBClip(boolean loop, ImmutableMap<String, ImmutableList<MBVariableClip>> clips, ImmutableMap<String, String> events)
{
this.loop = loop;
this.jointClipsFlat = clips;
this.eventsRaw = events;
}
private void initialize()
{
if(jointClips == null)
{
ImmutableMap.Builder<String, MBJointClip> builder = ImmutableMap.builder();
for (Map.Entry<String, ImmutableList<MBVariableClip>> e : jointClipsFlat.entrySet())
{
builder.put(e.getKey(), new MBJointClip(loop, e.getValue()));
}
jointClips = builder.build();
events = Maps.newTreeMap();
if (!eventsRaw.isEmpty())
{
TreeMap<Float, String> times = Maps.newTreeMap();
for (String time : eventsRaw.keySet())
{
times.put(Float.parseFloat(time), time);
}
float lastTime = Float.POSITIVE_INFINITY;
if (loop)
{
lastTime = times.firstKey();
}
for (Map.Entry<Float, String> entry : times.descendingMap().entrySet())
{
float time = entry.getKey();
float offset = lastTime - time;
if (loop)
{
offset = 1 - (1 - offset) % 1;
}
events.put(time, new Event(eventsRaw.get(entry.getValue()), offset));
}
}
}
}
@Override
public IJointClip apply(IJoint joint)
{
initialize();
if(joint instanceof MBJoint)
{
MBJoint mbJoint = (MBJoint)joint;
//MBJointInfo = jointInfos.
MBJointClip clip = jointClips.get(mbJoint.getName());
if(clip != null) return clip;
}
return JointClips.IdentityJointClip.INSTANCE;
}
@Override
public Iterable<Event> pastEvents(final float lastPollTime, final float time)
{
initialize();
return new Iterable<Event>()
{
@Override
public Iterator<Event> iterator()
{
return new UnmodifiableIterator<Event>()
{
private Float curKey;
private Event firstEvent;
private float stopTime;
{
if(lastPollTime >= time)
{
curKey = null;
}
else
{
float fractTime = time - (float)Math.floor(time);
float fractLastTime = lastPollTime - (float)Math.floor(lastPollTime);
// swap if not in order
if(fractLastTime > fractTime)
{
float tmp = fractTime;
fractTime = fractLastTime;
fractLastTime = tmp;
}
// need to wrap around, swap again
if(fractTime - fractLastTime > .5f)
{
float tmp = fractTime;
fractTime = fractLastTime;
fractLastTime = tmp;
}
stopTime = fractLastTime;
curKey = events.floorKey(fractTime);
if(curKey == null && loop && !events.isEmpty())
{
curKey = events.lastKey();
}
if(curKey != null)
{
float checkCurTime = curKey;
float checkStopTime = stopTime;
if(checkCurTime >= fractTime) checkCurTime--;
if(checkStopTime >= fractTime) checkStopTime--;
float offset = fractTime - checkCurTime;
Event event = events.get(curKey);
if(checkCurTime < checkStopTime)
{
curKey = null;
}
else if(offset != event.offset())
{
firstEvent = new Event(event.event(), offset);
}
}
}
}
@Override
public boolean hasNext()
{
return curKey != null;
}
@Override
public Event next()
{
if(curKey == null)
{
throw new NoSuchElementException();
}
Event event;
if(firstEvent == null)
{
event = events.get(curKey);
}
else
{
event = firstEvent;
firstEvent = null;
}
curKey = events.lowerKey(curKey);
if(curKey == null && loop)
{
curKey = events.lastKey();
}
if(curKey != null)
{
float checkStopTime = stopTime;
while(curKey + events.get(curKey).offset() < checkStopTime) checkStopTime--;
while(curKey + events.get(curKey).offset() >= checkStopTime + 1) checkStopTime++;
if(curKey <= checkStopTime)
{
curKey = null;
}
}
return event;
}
};
}
};
}
protected static class MBJointClip implements IJointClip
{
private final boolean loop;
private final ImmutableList<MBVariableClip> variables;
public MBJointClip(boolean loop, ImmutableList<MBVariableClip> variables)
{
this.loop = loop;
this.variables = variables;
EnumSet<Variable> hadVar = Sets.newEnumSet(Collections.<Variable>emptyList(), Variable.class);
for(MBVariableClip var : variables)
{
if(hadVar.contains(var.variable))
{
throw new IllegalArgumentException("duplicate variable: " + var);
}
hadVar.add(var.variable);
}
}
@Override
public TRSRTransformation apply(float time)
{
time -= Math.floor(time);
Vector3f translation = new Vector3f(0, 0, 0);
Vector3f scale = new Vector3f(1, 1, 1);
Vector3f origin = new Vector3f(0, 0, 0);
AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 0);
for(MBVariableClip var : variables)
{
int length = loop ? var.samples.length : (var.samples.length - 1);
float timeScaled = time * length;
int s1 = MathHelper.clamp((int)Math.round(Math.floor(timeScaled)), 0, length - 1);
float progress = timeScaled - s1;
int s2 = s1 + 1;
if(s2 == length && loop) s2 = 0;
float value = 0;
switch(var.interpolation)
{
case LINEAR:
if(var.variable == Variable.ANGLE)
{
float v1 = var.samples[s1];
float v2 = var.samples[s2];
float diff = ((v2 - v1) % 360 + 540) % 360 - 180;
value = v1 + diff * progress;
}
else
{
value = var.samples[s1] * (1 - progress) + var.samples[s2] * progress;
}
break;
case NEAREST:
value = var.samples[progress < .5f ? s1 : s2];
break;
}
switch(var.variable)
{
case X:
translation.x = value;
break;
case Y:
translation.y = value;
break;
case Z:
translation.z = value;
break;
case XROT:
rotation.x = value;
break;
case YROT:
rotation.y = value;
break;
case ZROT:
rotation.z = value;
break;
case ANGLE:
rotation.angle = (float)Math.toRadians(value);
break;
case SCALE:
scale.x = scale.y = scale.z = value;
break;
case XS:
scale.x = value;
break;
case YS:
scale.y = value;
break;
case ZS:
scale.z = value;
break;
case XORIGIN:
origin.x = value - 0.5F;
break;
case YORIGIN:
origin.y = value - 0.5F;
break;
case ZORIGIN:
origin.z = value - 0.5F;
break;
}
}
Quat4f rot = new Quat4f();
rot.set(rotation);
TRSRTransformation base = new TRSRTransformation(translation, rot, scale, null);
Vector3f negOrigin = new Vector3f(origin);
negOrigin.negate();
base = new TRSRTransformation(origin, null, null, null).compose(base).compose(new TRSRTransformation(negOrigin, null, null, null));
return TRSRTransformation.blockCenterToCorner(base);
}
}
}
protected static class MBJoint implements IJoint
{
private final String name;
public MBJoint(String name)
{
this.name = name;
}
@Override
public TRSRTransformation getInvBindPose()
{
return TRSRTransformation.identity();
}
@Override
public Optional<? extends IJoint> getParent()
{
return Optional.empty();
}
public String getName()
{
return name;
}
}
protected static class MBJointWeight
{
private final String name;
private final ImmutableMap<Integer, float[]> weights;
public MBJointWeight(String name, ImmutableMap<Integer, float[]> weights)
{
this.name = name;
this.weights = weights;
}
public String getName()
{
return name;
}
public ImmutableMap<Integer, float[]> getWeights()
{
return weights;
}
}
protected static class Parameter
{
public static enum Variable
{
@SerializedName("offset_x")
X,
@SerializedName("offset_y")
Y,
@SerializedName("offset_z")
Z,
@SerializedName("axis_x")
XROT,
@SerializedName("axis_y")
YROT,
@SerializedName("axis_z")
ZROT,
@SerializedName("angle")
ANGLE,
@SerializedName("scale")
SCALE,
@SerializedName("scale_x")
XS,
@SerializedName("scale_y")
YS,
@SerializedName("scale_z")
ZS,
@SerializedName("origin_x")
XORIGIN,
@SerializedName("origin_y")
YORIGIN,
@SerializedName("origin_z")
ZORIGIN;
}
public static enum Type
{
@SerializedName("uniform")
UNIFORM;
}
public static enum Interpolation
{
@SerializedName("linear")
LINEAR,
@SerializedName("nearest")
NEAREST;
}
}
@Nullable
public TRSRTransformation getPartTransform(IModelState state, BlockPart part, int i)
{
ImmutableCollection<MBJointWeight> infos = getJoint(i);
if(!infos.isEmpty())
{
Matrix4f m = new Matrix4f(), tmp;
float weight = 0;
for(MBJointWeight info : infos)
{
if(info.getWeights().containsKey(i))
{
ModelBlockAnimation.MBJoint joint = new ModelBlockAnimation.MBJoint(info.getName());
Optional<TRSRTransformation> trOp = state.apply(Optional.of(joint));
if(trOp.isPresent() && !trOp.get().isIdentity())
{
float w = info.getWeights().get(i)[0];
tmp = trOp.get().getMatrix();
tmp.mul(w);
m.add(tmp);
weight += w;
}
}
}
if(weight > 1e-5)
{
m.mul(1f / weight);
return new TRSRTransformation(m);
}
}
return null;
}
/**
* Load armature associated with a vanilla model.
*/
public static ModelBlockAnimation loadVanillaAnimation(IResourceManager manager, ResourceLocation armatureLocation)
{
try
{
try (IResource resource = manager.getResource(armatureLocation))
{
ModelBlockAnimation mba = mbaGson.fromJson(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8), ModelBlockAnimation.class);
//String json = mbaGson.toJson(mba);
return mba;
}
catch(FileNotFoundException e)
{
// this is normal. FIXME: error reporting?
return defaultModelBlockAnimation;
}
}
catch(IOException | JsonParseException e)
{
FMLLog.log.error("Exception loading vanilla model animation {}, skipping", armatureLocation, e);
return defaultModelBlockAnimation;
}
}
private static final Gson mbaGson = new GsonBuilder()
.registerTypeAdapter(ImmutableList.class, JsonUtils.ImmutableListTypeAdapter.INSTANCE)
.registerTypeAdapter(ImmutableMap.class, JsonUtils.ImmutableMapTypeAdapter.INSTANCE)
.setPrettyPrinting()
.enableComplexMapKeySerialization()
.disableHtmlEscaping()
.create();
private static final ModelBlockAnimation defaultModelBlockAnimation = new ModelBlockAnimation(ImmutableMap.<String, ImmutableMap<String, float[]>>of(), ImmutableMap.<String, ModelBlockAnimation.MBClip>of());
}

View File

@@ -0,0 +1,25 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.client.model.animation;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,102 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.b3d;
import net.minecraftforge.client.model.b3d.B3DLoader.NodeJoint;
import net.minecraftforge.client.model.b3d.B3DModel.Key;
import net.minecraftforge.client.model.b3d.B3DModel.Node;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.model.animation.IJoint;
import net.minecraftforge.common.model.animation.IJointClip;
import net.minecraftforge.common.model.animation.JointClips;
import com.google.common.collect.ImmutableSet;
// FIXME: is this fast enough?
public enum B3DClip implements IClip
{
INSTANCE;
@Override
public IJointClip apply(final IJoint joint)
{
if(!(joint instanceof NodeJoint))
{
return JointClips.IdentityJointClip.INSTANCE;
}
return new NodeClip(((NodeJoint)joint).getNode());
}
@Override
public Iterable<Event> pastEvents(float lastPollTime, float time)
{
return ImmutableSet.of();
}
protected static class NodeClip implements IJointClip
{
private final Node<?> node;
public NodeClip(Node<?> node)
{
this.node = node;
}
@Override
public TRSRTransformation apply(float time)
{
TRSRTransformation ret = TRSRTransformation.identity();
if(node.getAnimation() == null)
{
return ret.compose(new TRSRTransformation(node.getPos(), node.getRot(), node.getScale(), null));
}
int start = Math.max(1, (int)Math.round(Math.floor(time)));
int end = Math.min(start + 1, (int)Math.round(Math.ceil(time)));
float progress = time - (float)Math.floor(time);
Key keyStart = node.getAnimation().getKeys().get(start, node);
Key keyEnd = node.getAnimation().getKeys().get(end, node);
TRSRTransformation startTr = keyStart == null ? null : new TRSRTransformation(keyStart.getPos(), keyStart.getRot(),keyStart.getScale(), null);
TRSRTransformation endTr = keyEnd == null ? null : new TRSRTransformation(keyEnd.getPos(), keyEnd.getRot(),keyEnd.getScale(), null);
if(keyStart == null)
{
if(keyEnd == null)
{
ret = ret.compose(new TRSRTransformation(node.getPos(), node.getRot(), node.getScale(), null));
}
// TODO animated TRSRTransformation for speed?
else
{
ret = ret.compose(endTr);
}
}
else if(progress < 1e-5 || keyEnd == null)
{
ret = ret.compose(startTr);
}
else
{
ret = ret.compose(startTr.slerp(endTr, progress));
}
return ret;
}
}
}

View File

@@ -0,0 +1,830 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.b3d;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
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.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.ModelStateComposition;
import net.minecraftforge.client.model.PerspectiveMapWrapper;
import net.minecraftforge.client.model.b3d.B3DModel.Animation;
import net.minecraftforge.client.model.b3d.B3DModel.Face;
import net.minecraftforge.client.model.b3d.B3DModel.Key;
import net.minecraftforge.client.model.b3d.B3DModel.Mesh;
import net.minecraftforge.client.model.b3d.B3DModel.Node;
import net.minecraftforge.client.model.b3d.B3DModel.Texture;
import net.minecraftforge.client.model.b3d.B3DModel.Vertex;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.model.animation.IJoint;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import java.util.function.Function;
import com.google.common.base.Objects;
import java.util.Optional;
import com.google.common.base.Predicate;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
/*
* Loader for Blitz3D models.
* To enable for your mod call instance.addDomain(modId).
* If you need more control over accepted resources - extend the class, and register a new instance with ModelLoaderRegistry.
*/
public enum B3DLoader implements ICustomModelLoader
{
INSTANCE;
private IResourceManager manager;
private final Set<String> enabledDomains = new HashSet<>();
private final Map<ResourceLocation, B3DModel> cache = new HashMap<>();
public void addDomain(String domain)
{
enabledDomains.add(domain.toLowerCase());
}
@Override
public void onResourceManagerReload(IResourceManager manager)
{
this.manager = manager;
cache.clear();
}
@Override
public boolean accepts(ResourceLocation modelLocation)
{
return enabledDomains.contains(modelLocation.getResourceDomain()) && modelLocation.getResourcePath().endsWith(".b3d");
}
@Override
@SuppressWarnings("unchecked")
public IModel loadModel(ResourceLocation modelLocation) throws Exception
{
ResourceLocation file = new ResourceLocation(modelLocation.getResourceDomain(), modelLocation.getResourcePath());
if(!cache.containsKey(file))
{
IResource resource = null;
try
{
try
{
resource = manager.getResource(file);
}
catch(FileNotFoundException e)
{
if(modelLocation.getResourcePath().startsWith("models/block/"))
resource = manager.getResource(new ResourceLocation(file.getResourceDomain(), "models/item/" + file.getResourcePath().substring("models/block/".length())));
else if(modelLocation.getResourcePath().startsWith("models/item/"))
resource = manager.getResource(new ResourceLocation(file.getResourceDomain(), "models/block/" + file.getResourcePath().substring("models/item/".length())));
else throw e;
}
B3DModel.Parser parser = new B3DModel.Parser(resource.getInputStream());
B3DModel model = parser.parse();
cache.put(file, model);
}
catch(IOException e)
{
cache.put(file, null);
throw e;
}
finally
{
IOUtils.closeQuietly(resource);
}
}
B3DModel model = cache.get(file);
if(model == null) throw new ModelLoaderRegistry.LoaderException("Error loading model previously: " + file);
if(!(model.getRoot().getKind() instanceof Mesh))
{
return new ModelWrapper(modelLocation, model, ImmutableSet.of(), true, true, 1);
}
return new ModelWrapper(modelLocation, model, ImmutableSet.of(model.getRoot().getName()), true, true, 1);
}
public static final class B3DState implements IModelState
{
@Nullable
private final Animation animation;
private final int frame;
private final int nextFrame;
private final float progress;
@Nullable
private final IModelState parent;
public B3DState(@Nullable Animation animation, int frame)
{
this(animation, frame, frame, 0);
}
public B3DState(@Nullable Animation animation, int frame, IModelState parent)
{
this(animation, frame, frame, 0, parent);
}
public B3DState(@Nullable Animation animation, int frame, int nextFrame, float progress)
{
this(animation, frame, nextFrame, progress, null);
}
public B3DState(@Nullable Animation animation, int frame, int nextFrame, float progress, @Nullable IModelState parent)
{
this.animation = animation;
this.frame = frame;
this.nextFrame = nextFrame;
this.progress = MathHelper.clamp(progress, 0, 1);
this.parent = getParent(parent);
}
@Nullable
private IModelState getParent(@Nullable IModelState parent)
{
if (parent == null) return null;
else if (parent instanceof B3DState) return ((B3DState)parent).parent;
return parent;
}
@Nullable
public Animation getAnimation()
{
return animation;
}
public int getFrame()
{
return frame;
}
public int getNextFrame()
{
return nextFrame;
}
public float getProgress()
{
return progress;
}
@Nullable
public IModelState getParent()
{
return parent;
}
@Override
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
// TODO make more use of Optional
if(!part.isPresent())
{
if(parent != null)
{
return parent.apply(part);
}
return Optional.empty();
}
if(!(part.get() instanceof NodeJoint))
{
return Optional.empty();
}
Node<?> node = ((NodeJoint)part.get()).getNode();
TRSRTransformation nodeTransform;
if(progress < 1e-5 || frame == nextFrame)
{
nodeTransform = getNodeMatrix(node, frame);
}
else if(progress > 1 - 1e-5)
{
nodeTransform = getNodeMatrix(node, nextFrame);
}
else
{
nodeTransform = getNodeMatrix(node, frame);
nodeTransform = nodeTransform.slerp(getNodeMatrix(node, nextFrame), progress);
}
if(parent != null && node.getParent() == null)
{
return Optional.of(parent.apply(part).orElse(TRSRTransformation.identity()).compose(nodeTransform));
}
return Optional.of(nodeTransform);
}
private static LoadingCache<Triple<Animation, Node<?>, Integer>, TRSRTransformation> cache = CacheBuilder.newBuilder()
.maximumSize(16384)
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new CacheLoader<Triple<Animation, Node<?>, Integer>, TRSRTransformation>()
{
@Override
public TRSRTransformation load(Triple<Animation, Node<?>, Integer> key) throws Exception
{
return getNodeMatrix(key.getLeft(), key.getMiddle(), key.getRight());
}
});
public TRSRTransformation getNodeMatrix(Node<?> node)
{
return getNodeMatrix(node, frame);
}
public TRSRTransformation getNodeMatrix(Node<?> node, int frame)
{
return cache.getUnchecked(Triple.of(animation, node, frame));
}
public static TRSRTransformation getNodeMatrix(@Nullable Animation animation, Node<?> node, int frame)
{
TRSRTransformation ret = TRSRTransformation.identity();
Key key = null;
if(animation != null) key = animation.getKeys().get(frame, node);
else if(node.getAnimation() != null) key = node.getAnimation().getKeys().get(frame, node);
if(key != null)
{
Node<?> parent = node.getParent();
if(parent != null)
{
// parent model-global current pose
TRSRTransformation pm = cache.getUnchecked(Triple.of(animation, node.getParent(), frame));
ret = ret.compose(pm);
// joint offset in the parent coords
ret = ret.compose(new TRSRTransformation(parent.getPos(), parent.getRot(), parent.getScale(), null));
}
// current node local pose
ret = ret.compose(new TRSRTransformation(key.getPos(), key.getRot(), key.getScale(), null));
// this part moved inside the model
// inverse bind of the current node
/*Matrix4f rm = new TRSRTransformation(node.getPos(), node.getRot(), node.getScale(), null).getMatrix();
rm.invert();
ret = ret.compose(new TRSRTransformation(rm));
if(parent != null)
{
// inverse bind of the parent
rm = new TRSRTransformation(parent.getPos(), parent.getRot(), parent.getScale(), null).getMatrix();
rm.invert();
ret = ret.compose(new TRSRTransformation(rm));
}*/
// TODO cache
TRSRTransformation invBind = new NodeJoint(node).getInvBindPose();
ret = ret.compose(invBind);
}
else
{
Node<?> parent = node.getParent();
if(parent != null)
{
// parent model-global current pose
TRSRTransformation pm = cache.getUnchecked(Triple.of(animation, node.getParent(), frame));
ret = ret.compose(pm);
// joint offset in the parent coords
ret = ret.compose(new TRSRTransformation(parent.getPos(), parent.getRot(), parent.getScale(), null));
}
ret = ret.compose(new TRSRTransformation(node.getPos(), node.getRot(), node.getScale(), null));
// TODO cache
TRSRTransformation invBind = new NodeJoint(node).getInvBindPose();
ret = ret.compose(invBind);
}
return ret;
}
}
static final class NodeJoint implements IJoint
{
private final Node<?> node;
public NodeJoint(Node<?> node)
{
this.node = node;
}
@Override
public TRSRTransformation getInvBindPose()
{
Matrix4f m = new TRSRTransformation(node.getPos(), node.getRot(), node.getScale(), null).getMatrix();
m.invert();
TRSRTransformation pose = new TRSRTransformation(m);
if(node.getParent() != null)
{
TRSRTransformation parent = new NodeJoint(node.getParent()).getInvBindPose();
pose = pose.compose(parent);
}
return pose;
}
@Override
public Optional<NodeJoint> getParent()
{
// FIXME cache?
if(node.getParent() == null) return Optional.empty();
return Optional.of(new NodeJoint(node.getParent()));
}
public Node<?> getNode()
{
return node;
}
@Override
public int hashCode()
{
return node.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
NodeJoint other = (NodeJoint) obj;
return Objects.equal(node, other.node);
}
}
private static final class ModelWrapper implements IModel
{
private final ResourceLocation modelLocation;
private final B3DModel model;
private final ImmutableSet<String> meshes;
private final ImmutableMap<String, ResourceLocation> textures;
private final boolean smooth;
private final boolean gui3d;
private final int defaultKey;
public ModelWrapper(ResourceLocation modelLocation, B3DModel model, ImmutableSet<String> meshes, boolean smooth, boolean gui3d, int defaultKey)
{
this(modelLocation, model, meshes, smooth, gui3d, defaultKey, buildTextures(model.getTextures()));
}
public ModelWrapper(ResourceLocation modelLocation, B3DModel model, ImmutableSet<String> meshes, boolean smooth, boolean gui3d, int defaultKey, ImmutableMap<String, ResourceLocation> textures)
{
this.modelLocation = modelLocation;
this.model = model;
this.meshes = meshes;
this.textures = textures;
this.smooth = smooth;
this.gui3d = gui3d;
this.defaultKey = defaultKey;
}
private static ImmutableMap<String, ResourceLocation> buildTextures(List<Texture> textures)
{
ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
for(Texture t : textures)
{
String path = t.getPath();
String location = getLocation(path);
if(!location.startsWith("#")) location = "#" + location;
builder.put(path, new ResourceLocation(location));
}
return builder.build();
}
private static String getLocation(String path)
{
if(path.endsWith(".png")) path = path.substring(0, path.length() - ".png".length());
return path;
}
@Override
public Collection<ResourceLocation> getTextures()
{
return Collections2.filter(textures.values(), loc -> !loc.getResourcePath().startsWith("#"));
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableMap.Builder<String, TextureAtlasSprite> builder = ImmutableMap.builder();
TextureAtlasSprite missing = bakedTextureGetter.apply(new ResourceLocation("missingno"));
for(Map.Entry<String, ResourceLocation> e : textures.entrySet())
{
if(e.getValue().getResourcePath().startsWith("#"))
{
FMLLog.log.fatal("unresolved texture '{}' for b3d model '{}'", e.getValue().getResourcePath(), modelLocation);
builder.put(e.getKey(), missing);
}
else
{
builder.put(e.getKey(), bakedTextureGetter.apply(e.getValue()));
}
}
builder.put("missingno", missing);
return new BakedWrapper(model.getRoot(), state, smooth, gui3d, format, meshes, builder.build());
}
@Override
public ModelWrapper retexture(ImmutableMap<String, String> textures)
{
ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
for(Map.Entry<String, ResourceLocation> e : this.textures.entrySet())
{
String path = e.getKey();
String loc = getLocation(path);
if(textures.containsKey(loc))
{
String newLoc = textures.get(loc);
if(newLoc == null) newLoc = getLocation(path);
builder.put(e.getKey(), new ResourceLocation(newLoc));
}
else
{
builder.put(e);
}
}
return new ModelWrapper(modelLocation, model, meshes, smooth, gui3d, defaultKey, builder.build());
}
@Override
public ModelWrapper process(ImmutableMap<String, String> data)
{
ImmutableSet<String> newMeshes = this.meshes;
int newDefaultKey = this.defaultKey;
boolean hasChanged = false;
if(data.containsKey("mesh"))
{
JsonElement e = new JsonParser().parse(data.get("mesh"));
if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isString())
{
return new ModelWrapper(modelLocation, model, ImmutableSet.of(e.getAsString()), smooth, gui3d, defaultKey, textures);
}
else if (e.isJsonArray())
{
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for(JsonElement s : e.getAsJsonArray())
{
if(s.isJsonPrimitive() && s.getAsJsonPrimitive().isString())
{
builder.add(s.getAsString());
}
else
{
FMLLog.log.fatal("unknown mesh definition '{}' in array for b3d model '{}'", s.toString(), modelLocation);
return this;
}
}
newMeshes = builder.build();
hasChanged = true;
}
else
{
FMLLog.log.fatal("unknown mesh definition '{}' for b3d model '{}'", e.toString(), modelLocation);
return this;
}
}
if(data.containsKey("key"))
{
JsonElement e = new JsonParser().parse(data.get("key"));
if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isNumber())
{
newDefaultKey = e.getAsNumber().intValue();
hasChanged = true;
}
else
{
FMLLog.log.fatal("unknown keyframe definition '{}' for b3d model '{}'", e.toString(), modelLocation);
return this;
}
}
return hasChanged ? new ModelWrapper(modelLocation, model, newMeshes, smooth, gui3d, newDefaultKey, textures) : this;
}
@Override
public Optional<IClip> getClip(String name)
{
if(name.equals("main"))
{
return Optional.of(B3DClip.INSTANCE);
}
return Optional.empty();
}
@Override
public IModelState getDefaultState()
{
return new B3DState(model.getRoot().getAnimation(), defaultKey, defaultKey, 0);
}
@Override
public ModelWrapper smoothLighting(boolean value)
{
if(value == smooth)
{
return this;
}
return new ModelWrapper(modelLocation, model, meshes, value, gui3d, defaultKey, textures);
}
@Override
public ModelWrapper gui3d(boolean value)
{
if(value == gui3d)
{
return this;
}
return new ModelWrapper(modelLocation, model, meshes, smooth, value, defaultKey, textures);
}
}
private static final class BakedWrapper implements IBakedModel
{
private final Node<?> node;
private final IModelState state;
private final boolean smooth;
private final boolean gui3d;
private final VertexFormat format;
private final ImmutableSet<String> meshes;
private final ImmutableMap<String, TextureAtlasSprite> textures;
private final LoadingCache<Integer, B3DState> cache;
private ImmutableList<BakedQuad> quads;
public BakedWrapper(final Node<?> node, final IModelState state, final boolean smooth, final boolean gui3d, final VertexFormat format, final ImmutableSet<String> meshes, final ImmutableMap<String, TextureAtlasSprite> textures)
{
this(node, state, smooth, gui3d, format, meshes, textures, CacheBuilder.newBuilder()
.maximumSize(128)
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new CacheLoader<Integer, B3DState>()
{
@Override
public B3DState load(Integer frame) throws Exception
{
IModelState parent = state;
Animation newAnimation = node.getAnimation();
if(parent instanceof B3DState)
{
B3DState ps = (B3DState)parent;
parent = ps.getParent();
}
return new B3DState(newAnimation, frame, frame, 0, parent);
}
}));
}
public BakedWrapper(Node<?> node, IModelState state, boolean smooth, boolean gui3d, VertexFormat format, ImmutableSet<String> meshes, ImmutableMap<String, TextureAtlasSprite> textures, LoadingCache<Integer, B3DState> cache)
{
this.node = node;
this.state = state;
this.smooth = smooth;
this.gui3d = gui3d;
this.format = format;
this.meshes = meshes;
this.textures = textures;
this.cache = cache;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
if(side != null) return ImmutableList.of();
IModelState modelState = this.state;
if(state instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
if(exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
// FIXME: should animation state handle the parent state, or should it remain here?
IModelState parent = this.state;
if(parent instanceof B3DState)
{
B3DState ps = (B3DState)parent;
parent = ps.getParent();
}
IModelState newState = exState.getValue(Properties.AnimationProperty);
if(newState != null)
{
if (parent == null)
{
modelState = newState;
}
else
{
modelState = new ModelStateComposition(parent, newState);
}
}
}
}
if(quads == null)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
generateQuads(builder, node, this.state, ImmutableList.of());
quads = builder.build();
}
// TODO: caching?
if(this.state != modelState)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
generateQuads(builder, node, modelState, ImmutableList.of());
return builder.build();
}
return quads;
}
private void generateQuads(ImmutableList.Builder<BakedQuad> builder, Node<?> node, final IModelState state, ImmutableList<String> path)
{
ImmutableList.Builder<String> pathBuilder = ImmutableList.builder();
pathBuilder.addAll(path);
pathBuilder.add(node.getName());
ImmutableList<String> newPath = pathBuilder.build();
for(Node<?> child : node.getNodes().values())
{
generateQuads(builder, child, state, newPath);
}
if(node.getKind() instanceof Mesh && meshes.contains(node.getName()) && !state.apply(Optional.of(Models.getHiddenModelPart(newPath))).isPresent())
{
Mesh mesh = (Mesh)node.getKind();
Collection<Face> faces = mesh.bake(new Function<Node<?>, Matrix4f>()
{
private final TRSRTransformation global = state.apply(Optional.empty()).orElse(TRSRTransformation.identity());
private final LoadingCache<Node<?>, TRSRTransformation> localCache = CacheBuilder.newBuilder()
.maximumSize(32)
.build(new CacheLoader<Node<?>, TRSRTransformation>()
{
@Override
public TRSRTransformation load(Node<?> node) throws Exception
{
return state.apply(Optional.of(new NodeJoint(node))).orElse(TRSRTransformation.identity());
}
});
@Override
public Matrix4f apply(Node<?> node)
{
return global.compose(localCache.getUnchecked(node)).getMatrix();
}
});
for(Face f : faces)
{
UnpackedBakedQuad.Builder quadBuilder = new UnpackedBakedQuad.Builder(format);
quadBuilder.setContractUVs(true);
quadBuilder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
List<Texture> textures = null;
if(f.getBrush() != null) textures = f.getBrush().getTextures();
TextureAtlasSprite sprite;
if(textures == null || textures.isEmpty()) sprite = this.textures.get("missingno");
else if(textures.get(0) == B3DModel.Texture.White) sprite = ModelLoader.White.INSTANCE;
else sprite = this.textures.get(textures.get(0).getPath());
quadBuilder.setTexture(sprite);
putVertexData(quadBuilder, f.getV1(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV2(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
builder.add(quadBuilder.build());
}
}
}
private final void putVertexData(UnpackedBakedQuad.Builder builder, Vertex v, Vector3f faceNormal, TextureAtlasSprite sprite)
{
// TODO handle everything not handled (texture transformations, bones, transformations, normals, e.t.c)
for(int e = 0; e < format.getElementCount(); e++)
{
switch(format.getElement(e).getUsage())
{
case POSITION:
builder.put(e, v.getPos().x, v.getPos().y, v.getPos().z, 1);
break;
case COLOR:
if(v.getColor() != null)
{
builder.put(e, v.getColor().x, v.getColor().y, v.getColor().z, v.getColor().w);
}
else
{
builder.put(e, 1, 1, 1, 1);
}
break;
case UV:
// TODO handle more brushes
if(format.getElement(e).getIndex() < v.getTexCoords().length)
{
builder.put(e,
sprite.getInterpolatedU(v.getTexCoords()[0].x * 16),
sprite.getInterpolatedV(v.getTexCoords()[0].y * 16),
0,
1
);
}
else
{
builder.put(e, 0, 0, 0, 1);
}
break;
case NORMAL:
if(v.getNormal() != null)
{
builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 0);
}
else
{
builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 0);
}
break;
default:
builder.put(e);
}
}
}
@Override
public boolean isAmbientOcclusion()
{
return smooth;
}
@Override
public boolean isGui3d()
{
return gui3d;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
// FIXME somehow specify particle texture in the model
return textures.values().asList().get(0);
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
{
return PerspectiveMapWrapper.handlePerspective(this, state, cameraTransformType);
}
@Override
public ItemOverrideList getOverrides()
{
// TODO handle items
return ItemOverrideList.NONE;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.client.model.b3d;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,117 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.obj;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.resources.IResource;
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.fml.common.FMLLog;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Level;
/*
* Loader for OBJ models.
* To enable your mod call instance.addDomain(modid).
* If you need more control over accepted resources - extend the class, and register a new instance with ModelLoaderRegistry.
*/
public enum OBJLoader implements ICustomModelLoader {
INSTANCE;
private IResourceManager manager;
private final Set<String> enabledDomains = new HashSet<>();
private final Map<ResourceLocation, OBJModel> cache = new HashMap<>();
private final Map<ResourceLocation, Exception> errors = new HashMap<>();
public void addDomain(String domain)
{
enabledDomains.add(domain.toLowerCase());
FMLLog.log.info("OBJLoader: Domain {} has been added.", domain.toLowerCase());
}
@Override
public void onResourceManagerReload(IResourceManager resourceManager)
{
this.manager = resourceManager;
cache.clear();
errors.clear();
}
@Override
public boolean accepts(ResourceLocation modelLocation)
{
return enabledDomains.contains(modelLocation.getResourceDomain()) && modelLocation.getResourcePath().endsWith(".obj");
}
@Override
public IModel loadModel(ResourceLocation modelLocation) throws Exception
{
ResourceLocation file = new ResourceLocation(modelLocation.getResourceDomain(), modelLocation.getResourcePath());
if (!cache.containsKey(file))
{
IResource resource = null;
try
{
try
{
resource = manager.getResource(file);
}
catch (FileNotFoundException e)
{
if (modelLocation.getResourcePath().startsWith("models/block/"))
resource = manager.getResource(new ResourceLocation(file.getResourceDomain(), "models/item/" + file.getResourcePath().substring("models/block/".length())));
else if (modelLocation.getResourcePath().startsWith("models/item/"))
resource = manager.getResource(new ResourceLocation(file.getResourceDomain(), "models/block/" + file.getResourcePath().substring("models/item/".length())));
else throw e;
}
OBJModel.Parser parser = new OBJModel.Parser(resource, manager);
OBJModel model = null;
try
{
model = parser.parse();
}
catch (Exception e)
{
errors.put(modelLocation, e);
}
finally
{
cache.put(modelLocation, model);
}
}
finally
{
IOUtils.closeQuietly(resource);
}
}
OBJModel model = cache.get(file);
if (model == null) throw new ModelLoaderRegistry.LoaderException("Error loading model previously: " + file, errors.get(modelLocation));
return model;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.client.model.obj;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,25 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.client.model;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,267 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
public class BlockInfo
{
private static final EnumFacing[] SIDES = EnumFacing.values();
private final BlockColors colors;
private IBlockAccess world;
private IBlockState state;
private BlockPos blockPos;
private final boolean[][][] t = new boolean[3][3][3];
private final int[][][] s = new int[3][3][3];
private final int[][][] b = new int[3][3][3];
private final float[][][][] skyLight = new float[3][2][2][2];
private final float[][][][] blockLight = new float[3][2][2][2];
private final float[][][] ao = new float[3][3][3];
private final int[] packed = new int[7];
private boolean full;
private float shx = 0, shy = 0, shz = 0;
private int cachedTint = -1;
private int cachedMultiplier = -1;
public BlockInfo(BlockColors colors)
{
this.colors = colors;
}
public int getColorMultiplier(int tint)
{
if(cachedTint == tint) return cachedMultiplier;
cachedTint = tint;
cachedMultiplier = colors.colorMultiplier(state, world, blockPos, tint);
return cachedMultiplier;
}
public void updateShift()
{
Vec3d offset = state.getOffset(world, blockPos);
shx = (float) offset.x;
shy = (float) offset.y;
shz = (float) offset.z;
}
public void setWorld(IBlockAccess world)
{
this.world = world;
cachedTint = -1;
cachedMultiplier = -1;
}
public void setState(IBlockState state)
{
this.state = state;
cachedTint = -1;
cachedMultiplier = -1;
}
public void setBlockPos(BlockPos blockPos)
{
this.blockPos = blockPos;
cachedTint = -1;
cachedMultiplier = -1;
shx = shy = shz = 0;
}
public void reset()
{
this.world = null;
this.state = null;
this.blockPos = null;
cachedTint = -1;
cachedMultiplier = -1;
shx = shy = shz = 0;
}
private float combine(int c, int s1, int s2, int s3, boolean t0, boolean t1, boolean t2, boolean t3)
{
if (c == 0 && !t0) c = Math.max(0, Math.max(s1, s2) - 1);
if (s1 == 0 && !t1) s1 = Math.max(0, c - 1);
if (s2 == 0 && !t2) s2 = Math.max(0, c - 1);
if (s3 == 0 && !t3) s3 = Math.max(0, Math.max(s1, s2) - 1);
return (float) (c + s1 + s2 + s3) * 0x20 / (4 * 0xFFFF);
}
public void updateLightMatrix()
{
for(int x = 0; x <= 2; x++)
{
for(int y = 0; y <= 2; y++)
{
for(int z = 0; z <= 2; z++)
{
BlockPos pos = blockPos.add(x - 1, y - 1, z - 1);
IBlockState state = world.getBlockState(pos);
t[x][y][z] = state.getLightOpacity(world, pos) < 15;
int brightness = state.getPackedLightmapCoords(world, pos);
s[x][y][z] = (brightness >> 0x14) & 0xF;
b[x][y][z] = (brightness >> 0x04) & 0xF;
ao[x][y][z] = state.getAmbientOcclusionLightValue();
}
}
}
for(EnumFacing side : SIDES)
{
if(!state.doesSideBlockRendering(world, blockPos, side))
{
int x = side.getFrontOffsetX() + 1;
int y = side.getFrontOffsetY() + 1;
int z = side.getFrontOffsetZ() + 1;
s[x][y][z] = Math.max(s[1][1][1] - 1, s[x][y][z]);
b[x][y][z] = Math.max(b[1][1][1] - 1, b[x][y][z]);
}
}
for(int x = 0; x < 2; x++)
{
for(int y = 0; y < 2; y++)
{
for(int z = 0; z < 2; z++)
{
int x1 = x * 2;
int y1 = y * 2;
int z1 = z * 2;
int sxyz = s[x1][y1][z1];
int bxyz = b[x1][y1][z1];
boolean txyz = t[x1][y1][z1];
int sxz = s[x1][1][z1], sxy = s[x1][y1][1], syz = s[1][y1][z1];
int bxz = b[x1][1][z1], bxy = b[x1][y1][1], byz = b[1][y1][z1];
boolean txz = t[x1][1][z1], txy = t[x1][y1][1], tyz = t[1][y1][z1];
int sx = s[x1][1][1], sy = s[1][y1][1], sz = s[1][1][z1];
int bx = b[x1][1][1], by = b[1][y1][1], bz = b[1][1][z1];
boolean tx = t[x1][1][1], ty = t[1][y1][1], tz = t[1][1][z1];
skyLight [0][x][y][z] = combine(sx, sxz, sxy, txz || txy ? sxyz : sx,
tx, txz, txy, txz || txy ? txyz : tx);
blockLight[0][x][y][z] = combine(bx, bxz, bxy, txz || txy ? bxyz : bx,
tx, txz, txy, txz || txy ? txyz : tx);
skyLight [1][x][y][z] = combine(sy, sxy, syz, txy || tyz ? sxyz : sy,
ty, txy, tyz, txy || tyz ? txyz : ty);
blockLight[1][x][y][z] = combine(by, bxy, byz, txy || tyz ? bxyz : by,
ty, txy, tyz, txy || tyz ? txyz : ty);
skyLight [2][x][y][z] = combine(sz, syz, sxz, tyz || txz ? sxyz : sz,
tz, tyz, txz, tyz || txz ? txyz : tz);
blockLight[2][x][y][z] = combine(bz, byz, bxz, tyz || txz ? bxyz : bz,
tz, tyz, txz, tyz || txz ? txyz : tz);
}
}
}
}
public void updateFlatLighting()
{
full = state.isFullCube();
packed[0] = state.getPackedLightmapCoords(world, blockPos);
for (EnumFacing side : SIDES)
{
int i = side.ordinal() + 1;
packed[i] = state.getPackedLightmapCoords(world, blockPos.offset(side));
}
}
public IBlockAccess getWorld()
{
return world;
}
public IBlockState getState()
{
return state;
}
public BlockPos getBlockPos()
{
return blockPos;
}
public boolean[][][] getTranslucent()
{
return t;
}
public float[][][][] getSkyLight()
{
return skyLight;
}
public float[][][][] getBlockLight()
{
return blockLight;
}
public float[][][] getAo()
{
return ao;
}
public int[] getPackedLight()
{
return packed;
}
public boolean isFullCube()
{
return full;
}
public float getShx()
{
return shx;
}
public float getShy()
{
return shy;
}
public float getShz()
{
return shz;
}
public int getCachedTint()
{
return cachedTint;
}
public int getCachedMultiplier()
{
return cachedMultiplier;
}
}

View File

@@ -0,0 +1,128 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import java.util.List;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.BlockModelRenderer;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.ForgeModContainer;
public class ForgeBlockModelRenderer extends BlockModelRenderer
{
private final ThreadLocal<VertexLighterFlat> lighterFlat;
private final ThreadLocal<VertexLighterSmoothAo> lighterSmooth;
private final ThreadLocal<VertexBufferConsumer> wrFlat = new ThreadLocal<>();
private final ThreadLocal<VertexBufferConsumer> wrSmooth = new ThreadLocal<>();
private final ThreadLocal<BufferBuilder> lastRendererFlat = new ThreadLocal<>();
private final ThreadLocal<BufferBuilder> lastRendererSmooth = new ThreadLocal<>();
public ForgeBlockModelRenderer(BlockColors colors)
{
super(colors);
lighterFlat = ThreadLocal.withInitial(() -> new VertexLighterFlat(colors));
lighterSmooth = ThreadLocal.withInitial(() -> new VertexLighterSmoothAo(colors));
}
@Override
public boolean renderModelFlat(IBlockAccess world, IBakedModel model, IBlockState state, BlockPos pos, BufferBuilder buffer, boolean checkSides, long rand)
{
if(ForgeModContainer.forgeLightPipelineEnabled)
{
if(buffer != lastRendererFlat.get())
{
lastRendererFlat.set(buffer);
VertexBufferConsumer newCons = new VertexBufferConsumer(buffer);
wrFlat.set(newCons);
lighterFlat.get().setParent(newCons);
}
wrFlat.get().setOffset(pos);
return render(lighterFlat.get(), world, model, state, pos, buffer, checkSides, rand);
}
else
{
return super.renderModelFlat(world, model, state, pos, buffer, checkSides, rand);
}
}
@Override
public boolean renderModelSmooth(IBlockAccess world, IBakedModel model, IBlockState state, BlockPos pos, BufferBuilder buffer, boolean checkSides, long rand)
{
if(ForgeModContainer.forgeLightPipelineEnabled)
{
if(buffer != lastRendererSmooth.get())
{
lastRendererSmooth.set(buffer);
VertexBufferConsumer newCons = new VertexBufferConsumer(buffer);
wrSmooth.set(newCons);
lighterSmooth.get().setParent(newCons);
}
wrSmooth.get().setOffset(pos);
return render(lighterSmooth.get(), world, model, state, pos, buffer, checkSides, rand);
}
else
{
return super.renderModelSmooth(world, model, state, pos, buffer, checkSides, rand);
}
}
public static boolean render(VertexLighterFlat lighter, IBlockAccess world, IBakedModel model, IBlockState state, BlockPos pos, BufferBuilder wr, boolean checkSides, long rand)
{
lighter.setWorld(world);
lighter.setState(state);
lighter.setBlockPos(pos);
boolean empty = true;
List<BakedQuad> quads = model.getQuads(state, null, rand);
if(!quads.isEmpty())
{
lighter.updateBlockInfo();
empty = false;
for(BakedQuad quad : quads)
{
quad.pipe(lighter);
}
}
for(EnumFacing side : EnumFacing.values())
{
quads = model.getQuads(state, side, rand);
if(!quads.isEmpty())
{
if(!checkSides || state.shouldSideBeRendered(world, pos, side))
{
if(empty) lighter.updateBlockInfo();
empty = false;
for(BakedQuad quad : quads)
{
quad.pipe(lighter);
}
}
}
}
lighter.resetBlockInfo();
return !empty;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
/**
* Assumes that the data length is not less than e.getElementCount().
* Also assumes that element index passed will increment from 0 to format.getElementCount() - 1.
* Normal, Color and UV are assumed to be in 0-1 range.
*/
public interface IVertexConsumer
{
/**
* @return the format that should be used for passed data.
*/
VertexFormat getVertexFormat();
void setQuadTint(int tint);
void setQuadOrientation(EnumFacing orientation);
void setApplyDiffuseLighting(boolean diffuse);
void setTexture(TextureAtlasSprite texture);
void put(int element, float... data);
}

View File

@@ -0,0 +1,29 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
public interface IVertexProducer
{
/**
* @param consumer consumer to receive the vertex data this producer can provide
*/
void pipe(IVertexConsumer consumer);
}

View File

@@ -0,0 +1,345 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.ForgeHooksClient;
import org.apache.commons.lang3.tuple.Pair;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class LightUtil
{
public static float diffuseLight(float x, float y, float z)
{
return Math.min(x * x * 0.6f + y * y * ((3f + y) / 4f) + z * z * 0.8f, 1f);
}
public static float diffuseLight(EnumFacing side)
{
switch(side)
{
case DOWN:
return .5f;
case UP:
return 1f;
case NORTH:
case SOUTH:
return .8f;
default:
return .6f;
}
}
public static EnumFacing toSide(float x, float y, float z)
{
if(Math.abs(x) > Math.abs(y))
{
if(Math.abs(x) > Math.abs(z))
{
if(x < 0) return EnumFacing.WEST;
return EnumFacing.EAST;
}
else
{
if(z < 0) return EnumFacing.NORTH;
return EnumFacing.SOUTH;
}
}
else
{
if(Math.abs(y) > Math.abs(z))
{
if(y < 0) return EnumFacing.DOWN;
return EnumFacing.UP;
}
else
{
if(z < 0) return EnumFacing.NORTH;
return EnumFacing.SOUTH;
}
}
}
private static final ConcurrentMap<Pair<VertexFormat, VertexFormat>, int[]> formatMaps = new ConcurrentHashMap<>();
public static void putBakedQuad(IVertexConsumer consumer, BakedQuad quad)
{
consumer.setTexture(quad.getSprite());
consumer.setQuadOrientation(quad.getFace());
if(quad.hasTintIndex())
{
consumer.setQuadTint(quad.getTintIndex());
}
consumer.setApplyDiffuseLighting(quad.shouldApplyDiffuseLighting());
float[] data = new float[4];
VertexFormat formatFrom = consumer.getVertexFormat();
VertexFormat formatTo = quad.getFormat();
int countFrom = formatFrom.getElementCount();
int countTo = formatTo.getElementCount();
int[] eMap = mapFormats(formatFrom, formatTo);
for(int v = 0; v < 4; v++)
{
for(int e = 0; e < countFrom; e++)
{
if(eMap[e] != countTo)
{
unpack(quad.getVertexData(), data, formatTo, v, eMap[e]);
consumer.put(e, data);
}
else
{
consumer.put(e);
}
}
}
}
public static int[] mapFormats(VertexFormat from, VertexFormat to)
{
return formatMaps.computeIfAbsent(Pair.of(from, to), pair -> generateMapping(pair.getLeft(), pair.getRight()));
}
private static int[] generateMapping(VertexFormat from, VertexFormat to)
{
int fromCount = from.getElementCount();
int toCount = to.getElementCount();
int[] eMap = new int[fromCount];
for(int e = 0; e < fromCount; e++)
{
VertexFormatElement expected = from.getElement(e);
int e2;
for(e2 = 0; e2 < toCount; e2++)
{
VertexFormatElement current = to.getElement(e2);
if(expected.getUsage() == current.getUsage() && expected.getIndex() == current.getIndex())
{
break;
}
}
eMap[e] = e2;
}
return eMap;
}
public static void unpack(int[] from, float[] to, VertexFormat formatFrom, int v, int e)
{
int length = 4 < to.length ? 4 : to.length;
VertexFormatElement element = formatFrom.getElement(e);
int vertexStart = v * formatFrom.getNextOffset() + formatFrom.getOffset(e);
int count = element.getElementCount();
VertexFormatElement.EnumType type = element.getType();
int size = type.getSize();
int mask = (256 << (8 * (size - 1))) - 1;
for(int i = 0; i < length; i++)
{
if(i < count)
{
int pos = vertexStart + size * i;
int index = pos >> 2;
int offset = pos & 3;
int bits = from[index];
bits = bits >>> (offset * 8);
if((pos + size - 1) / 4 != index)
{
bits |= from[index + 1] << ((4 - offset) * 8);
}
bits &= mask;
if(type == VertexFormatElement.EnumType.FLOAT)
{
to[i] = Float.intBitsToFloat(bits);
}
else if(type == VertexFormatElement.EnumType.UBYTE || type == VertexFormatElement.EnumType.USHORT)
{
to[i] = (float)bits / mask;
}
else if(type == VertexFormatElement.EnumType.UINT)
{
to[i] = (float)((double)(bits & 0xFFFFFFFFL) / 0xFFFFFFFFL);
}
else if(type == VertexFormatElement.EnumType.BYTE)
{
to[i] = ((float)(byte)bits) / (mask >> 1);
}
else if(type == VertexFormatElement.EnumType.SHORT)
{
to[i] = ((float)(short)bits) / (mask >> 1);
}
else if(type == VertexFormatElement.EnumType.INT)
{
to[i] = (float)((double)(bits & 0xFFFFFFFFL) / (0xFFFFFFFFL >> 1));
}
}
else
{
to[i] = 0;
}
}
}
public static void pack(float[] from, int[] to, VertexFormat formatTo, int v, int e)
{
VertexFormatElement element = formatTo.getElement(e);
int vertexStart = v * formatTo.getNextOffset() + formatTo.getOffset(e);
int count = element.getElementCount();
VertexFormatElement.EnumType type = element.getType();
int size = type.getSize();
int mask = (256 << (8 * (size - 1))) - 1;
for(int i = 0; i < 4; i++)
{
if(i < count)
{
int pos = vertexStart + size * i;
int index = pos >> 2;
int offset = pos & 3;
int bits = 0;
float f = i < from.length ? from[i] : 0;
if(type == VertexFormatElement.EnumType.FLOAT)
{
bits = Float.floatToRawIntBits(f);
}
else if(
type == VertexFormatElement.EnumType.UBYTE ||
type == VertexFormatElement.EnumType.USHORT ||
type == VertexFormatElement.EnumType.UINT
)
{
bits = Math.round(f * mask);
}
else
{
bits = Math.round(f * (mask >> 1));
}
to[index] &= ~(mask << (offset * 8));
to[index] |= (((bits & mask) << (offset * 8)));
// TODO handle overflow into to[index + 1]
}
}
}
private static IVertexConsumer tessellator = null;
public static IVertexConsumer getTessellator()
{
if(tessellator == null)
{
Tessellator tes = Tessellator.getInstance();
BufferBuilder wr = tes.getBuffer();
tessellator = new VertexBufferConsumer(wr);
}
return tessellator;
}
private static ItemConsumer itemConsumer = null;
public static ItemConsumer getItemConsumer()
{
if(itemConsumer == null)
{
itemConsumer = new ItemConsumer(getTessellator());
}
return itemConsumer;
}
// renders quad in any Vertex Format, but is slower
public static void renderQuadColorSlow(BufferBuilder wr, BakedQuad quad, int auxColor)
{
ItemConsumer cons;
if(wr == Tessellator.getInstance().getBuffer())
{
cons = getItemConsumer();
}
else
{
cons = new ItemConsumer(new VertexBufferConsumer(wr));
}
float b = (float)(auxColor & 0xFF) / 0xFF;
float g = (float)((auxColor >>> 8) & 0xFF) / 0xFF;
float r = (float)((auxColor >>> 16) & 0xFF) / 0xFF;
float a = (float)((auxColor >>> 24) & 0xFF) / 0xFF;
cons.setAuxColor(r, g, b, a);
quad.pipe(cons);
}
public static void renderQuadColor(BufferBuilder wr, BakedQuad quad, int auxColor)
{
if (quad.getFormat().equals(wr.getVertexFormat()))
{
wr.addVertexData(quad.getVertexData());
ForgeHooksClient.putQuadColor(wr, quad, auxColor);
}
else
{
renderQuadColorSlow(wr, quad, auxColor);
}
}
public static class ItemConsumer extends VertexTransformer
{
private int vertices = 0;
private float[] auxColor = new float[]{1, 1, 1, 1};
private float[] buf = new float[4];
public ItemConsumer(IVertexConsumer parent)
{
super(parent);
}
public void setAuxColor(float... auxColor)
{
System.arraycopy(auxColor, 0, this.auxColor, 0, this.auxColor.length);
}
@Override
public void put(int element, float... data)
{
if(getVertexFormat().getElement(element).getUsage() == EnumUsage.COLOR)
{
System.arraycopy(auxColor, 0, buf, 0, buf.length);
for(int i = 0; i < 4; i++)
{
buf[i] *= data[i];
}
super.put(element, buf);
}
else
{
super.put(element, data);
}
if(element == getVertexFormat().getElementCount() - 1)
{
vertices++;
if(vertices == 4)
{
vertices = 0;
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.vertex.VertexFormat;
public abstract class QuadGatheringTransformer implements IVertexConsumer
{
protected IVertexConsumer parent;
protected VertexFormat format;
protected int vertices = 0;
protected byte[] dataLength = null;
protected float[][][] quadData = null;
public void setParent(IVertexConsumer parent)
{
this.parent = parent;
}
public void setVertexFormat(VertexFormat format)
{
this.format = format;
dataLength = new byte[format.getElementCount()];
quadData = new float[format.getElementCount()][4][4];
}
@Override
public VertexFormat getVertexFormat()
{
return format;
}
@Override
public void put(int element, float... data)
{
System.arraycopy(data, 0, quadData[element][vertices], 0, data.length);
if(element == getVertexFormat().getElementCount() - 1) vertices++;
if(vertices == 0)
{
dataLength[element] = (byte)data.length;
}
else if(vertices == 4)
{
vertices = 0;
processQuad();
}
}
protected abstract void processQuad();
}

View File

@@ -0,0 +1,46 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.vertex.VertexFormat;
public abstract class TransformerConsumer implements IVertexConsumer {
private IVertexConsumer parent;
protected TransformerConsumer(IVertexConsumer parent)
{
this.parent = parent;
}
@Override
public VertexFormat getVertexFormat()
{
return parent.getVertexFormat();
}
@Override
public void put(int element, float... data)
{
float[] newData = transform(element, data);
parent.put(element, newData);
}
protected abstract float[] transform(int element, float... data);
}

View File

@@ -0,0 +1,238 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.util.EnumFacing;
// advantages: non-fixed-length vertex format, no overhead of packing and unpacking attributes to transform the model
// disadvantages: (possibly) larger memory footprint, overhead on packing the attributes at the final rendering stage
public class UnpackedBakedQuad extends BakedQuad
{
protected final float[][][] unpackedData;
protected final VertexFormat format;
protected boolean packed = false;
public UnpackedBakedQuad(float[][][] unpackedData, int tint, EnumFacing orientation, TextureAtlasSprite texture, boolean applyDiffuseLighting, VertexFormat format)
{
super(new int[format.getNextOffset() /* / 4 * 4 */], tint, orientation, texture, applyDiffuseLighting, format);
this.unpackedData = unpackedData;
this.format = format;
}
@Override
public int[] getVertexData()
{
if(!packed)
{
packed = true;
for(int v = 0; v < 4; v++)
{
for(int e = 0; e < format.getElementCount(); e++)
{
LightUtil.pack(unpackedData[v][e], vertexData, format, v, e);
}
}
}
return vertexData;
}
@Override
public void pipe(IVertexConsumer consumer)
{
int[] eMap = LightUtil.mapFormats(consumer.getVertexFormat(), format);
if(hasTintIndex())
{
consumer.setQuadTint(getTintIndex());
}
consumer.setTexture(sprite);
consumer.setApplyDiffuseLighting(applyDiffuseLighting);
consumer.setQuadOrientation(getFace());
for(int v = 0; v < 4; v++)
{
for(int e = 0; e < consumer.getVertexFormat().getElementCount(); e++)
{
if(eMap[e] != format.getElementCount())
{
consumer.put(e, unpackedData[v][eMap[e]]);
}
else
{
consumer.put(e);
}
}
}
}
public static class Builder implements IVertexConsumer
{
private final VertexFormat format;
private final float[][][] unpackedData;
private int tint = -1;
private EnumFacing orientation;
private TextureAtlasSprite texture;
private boolean applyDiffuseLighting = true;
private int vertices = 0;
private int elements = 0;
private boolean full = false;
private boolean contractUVs = false;
public Builder(VertexFormat format)
{
this.format = format;
unpackedData = new float[4][format.getElementCount()][4];
}
@Override
public VertexFormat getVertexFormat()
{
return format;
}
public void setContractUVs(boolean value)
{
this.contractUVs = value;
}
@Override
public void setQuadTint(int tint)
{
this.tint = tint;
}
@Override
public void setQuadOrientation(EnumFacing orientation)
{
this.orientation = orientation;
}
// FIXME: move (or at least add) into constructor
@Override
public void setTexture(TextureAtlasSprite texture)
{
this.texture = texture;
}
@Override
public void setApplyDiffuseLighting(boolean diffuse)
{
this.applyDiffuseLighting = diffuse;
}
@Override
public void put(int element, float... data)
{
for(int i = 0; i < 4; i++)
{
if(i < data.length)
{
unpackedData[vertices][element][i] = data[i];
}
else
{
unpackedData[vertices][element][i] = 0;
}
}
elements++;
if(elements == format.getElementCount())
{
vertices++;
elements = 0;
}
if(vertices == 4)
{
full = true;
}
}
private final float eps = 1f / 0x100;
public UnpackedBakedQuad build()
{
if(!full)
{
throw new IllegalStateException("not enough data");
}
if(texture == null)
{
throw new IllegalStateException("texture not set");
}
if(contractUVs)
{
float tX = texture.getIconWidth() / (texture.getMaxU() - texture.getMinU());
float tY = texture.getIconHeight() / (texture.getMaxV() - texture.getMinV());
float tS = tX > tY ? tX : tY;
float ep = 1f / (tS * 0x100);
int uve = 0;
while(uve < format.getElementCount())
{
VertexFormatElement e = format.getElement(uve);
if(e.getUsage() == VertexFormatElement.EnumUsage.UV && e.getIndex() == 0)
{
break;
}
uve++;
}
if(uve == format.getElementCount())
{
throw new IllegalStateException("Can't contract UVs: format doesn't contain UVs");
}
float[] uvc = new float[4];
for(int v = 0; v < 4; v++)
{
for(int i = 0; i < 4; i++)
{
uvc[i] += unpackedData[v][uve][i] / 4;
}
}
for(int v = 0; v < 4; v++)
{
for (int i = 0; i < 4; i++)
{
float uo = unpackedData[v][uve][i];
float un = uo * (1 - eps) + uvc[i] * eps;
float ud = uo - un;
float aud = ud;
if(aud < 0) aud = -aud;
if(aud < ep) // not moving a fraction of a pixel
{
float udc = uo - uvc[i];
if(udc < 0) udc = -udc;
if(udc < 2 * ep) // center is closer than 2 fractions of a pixel, don't move too close
{
un = (uo + uvc[i]) / 2;
}
else // move at least by a fraction
{
un = uo + (ud < 0 ? ep : -ep);
}
}
unpackedData[v][uve][i] = un;
}
}
}
return new UnpackedBakedQuad(unpackedData, tint, orientation, texture, applyDiffuseLighting, format);
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement.EnumUsage;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
/**
* Assumes VertexFormatElement is present in the BufferBuilder's vertex format.
*/
public class VertexBufferConsumer implements IVertexConsumer
{
private static final float[] dummyColor = new float[]{ 1, 1, 1, 1 };
private final BufferBuilder renderer;
private final int[] quadData;
private int v = 0;
private BlockPos offset = BlockPos.ORIGIN;
public VertexBufferConsumer(BufferBuilder renderer)
{
super();
this.renderer = renderer;
quadData = new int[renderer.getVertexFormat().getNextOffset()/* / 4 * 4 */];
}
@Override
public VertexFormat getVertexFormat()
{
return renderer.getVertexFormat();
}
@Override
public void put(int e, float... data)
{
VertexFormat format = getVertexFormat();
if(renderer.isColorDisabled() && format.getElement(e).getUsage() == EnumUsage.COLOR)
{
data = dummyColor;
}
LightUtil.pack(data, quadData, format, v, e);
if(e == format.getElementCount() - 1)
{
v++;
if(v == 4)
{
renderer.addVertexData(quadData);
renderer.putPosition(offset.getX(), offset.getY(), offset.getZ());
//Arrays.fill(quadData, 0);
v = 0;
}
}
}
public void setOffset(BlockPos offset)
{
this.offset = new BlockPos(offset);
}
@Override
public void setQuadTint(int tint) {}
@Override
public void setQuadOrientation(EnumFacing orientation) {}
@Override
public void setApplyDiffuseLighting(boolean diffuse) {}
@Override
public void setTexture(TextureAtlasSprite texture ) {}
}

View File

@@ -0,0 +1,315 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import javax.vecmath.Vector3f;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import java.util.Objects;
public class VertexLighterFlat extends QuadGatheringTransformer
{
protected static final VertexFormatElement NORMAL_4F = new VertexFormatElement(0, VertexFormatElement.EnumType.FLOAT, VertexFormatElement.EnumUsage.NORMAL, 4);
protected final BlockInfo blockInfo;
private int tint = -1;
private boolean diffuse = true;
protected int posIndex = -1;
protected int normalIndex = -1;
protected int colorIndex = -1;
protected int lightmapIndex = -1;
protected VertexFormat baseFormat;
public VertexLighterFlat(BlockColors colors)
{
this.blockInfo = new BlockInfo(colors);
}
@Override
public void setParent(IVertexConsumer parent)
{
super.setParent(parent);
setVertexFormat(parent.getVertexFormat());
}
private void updateIndices()
{
for(int i = 0; i < getVertexFormat().getElementCount(); i++)
{
switch(getVertexFormat().getElement(i).getUsage())
{
case POSITION:
posIndex = i;
break;
case NORMAL:
normalIndex = i;
break;
case COLOR:
colorIndex = i;
break;
case UV:
if(getVertexFormat().getElement(i).getIndex() == 1)
{
lightmapIndex = i;
}
break;
default:
}
}
if(posIndex == -1)
{
throw new IllegalArgumentException("vertex lighter needs format with position");
}
if(lightmapIndex == -1)
{
throw new IllegalArgumentException("vertex lighter needs format with lightmap");
}
if(colorIndex == -1)
{
throw new IllegalArgumentException("vertex lighter needs format with color");
}
}
@Override
public void setVertexFormat(VertexFormat format)
{
if (Objects.equals(format, baseFormat)) return;
baseFormat = format;
super.setVertexFormat(withNormal(format));
updateIndices();
}
private static VertexFormat withNormal(VertexFormat format)
{
if (format == null || format.hasNormal()) return format;
return new VertexFormat(format).addElement(NORMAL_4F);
}
@Override
protected void processQuad()
{
float[][] position = quadData[posIndex];
float[][] normal = null;
float[][] lightmap = quadData[lightmapIndex];
float[][] color = quadData[colorIndex];
if (dataLength[normalIndex] >= 3
&& (quadData[normalIndex][0][0] != -1
|| quadData[normalIndex][0][1] != -1
|| quadData[normalIndex][0][2] != -1))
{
normal = quadData[normalIndex];
}
else // normals must be generated
{
normal = new float[4][4];
Vector3f v1 = new Vector3f(position[3]);
Vector3f t = new Vector3f(position[1]);
Vector3f v2 = new Vector3f(position[2]);
v1.sub(t);
t.set(position[0]);
v2.sub(t);
v1.cross(v2, v1);
v1.normalize();
for(int v = 0; v < 4; v++)
{
normal[v][0] = v1.x;
normal[v][1] = v1.y;
normal[v][2] = v1.z;
normal[v][3] = 0;
}
}
int multiplier = -1;
if(tint != -1)
{
multiplier = blockInfo.getColorMultiplier(tint);
}
VertexFormat format = parent.getVertexFormat();
int count = format.getElementCount();
for(int v = 0; v < 4; v++)
{
position[v][0] += blockInfo.getShx();
position[v][1] += blockInfo.getShy();
position[v][2] += blockInfo.getShz();
float x = position[v][0] - .5f;
float y = position[v][1] - .5f;
float z = position[v][2] - .5f;
//if(blockInfo.getBlock().isFullCube())
{
x += normal[v][0] * .5f;
y += normal[v][1] * .5f;
z += normal[v][2] * .5f;
}
float blockLight = lightmap[v][0], skyLight = lightmap[v][1];
updateLightmap(normal[v], lightmap[v], x, y, z);
if(dataLength[lightmapIndex] > 1)
{
if(blockLight > lightmap[v][0]) lightmap[v][0] = blockLight;
if(skyLight > lightmap[v][1]) lightmap[v][1] = skyLight;
}
updateColor(normal[v], color[v], x, y, z, tint, multiplier);
if(diffuse)
{
float d = LightUtil.diffuseLight(normal[v][0], normal[v][1], normal[v][2]);
for(int i = 0; i < 3; i++)
{
color[v][i] *= d;
}
}
if(EntityRenderer.anaglyphEnable)
{
applyAnaglyph(color[v]);
}
// no need for remapping cause all we could've done is add 1 element to the end
for(int e = 0; e < count; e++)
{
VertexFormatElement element = format.getElement(e);
switch(element.getUsage())
{
case POSITION:
// position adding moved to VertexBufferConsumer due to x and z not fitting completely into a float
/*float[] pos = new float[4];
System.arraycopy(position[v], 0, pos, 0, position[v].length);
pos[0] += blockInfo.getBlockPos().getX();
pos[1] += blockInfo.getBlockPos().getY();
pos[2] += blockInfo.getBlockPos().getZ();*/
parent.put(e, position[v]);
break;
case NORMAL: if(normalIndex != -1)
{
parent.put(e, normal[v]);
break;
}
case COLOR:
parent.put(e, color[v]);
break;
case UV: if(element.getIndex() == 1)
{
parent.put(e, lightmap[v]);
break;
}
default:
parent.put(e, quadData[e][v]);
}
}
}
tint = -1;
}
protected void applyAnaglyph(float[] color)
{
float r = color[0];
color[0] = (r * 30 + color[1] * 59 + color[2] * 11) / 100;
color[1] = (r * 3 + color[1] * 7) / 10;
color[2] = (r * 3 + color[2] * 7) / 10;
}
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z)
{
final float e1 = 1f - 1e-2f;
final float e2 = 0.95f;
boolean full = blockInfo.isFullCube();
EnumFacing side = null;
if((full || y < -e1) && normal[1] < -e2) side = EnumFacing.DOWN;
else if((full || y > e1) && normal[1] > e2) side = EnumFacing.UP;
else if((full || z < -e1) && normal[2] < -e2) side = EnumFacing.NORTH;
else if((full || z > e1) && normal[2] > e2) side = EnumFacing.SOUTH;
else if((full || x < -e1) && normal[0] < -e2) side = EnumFacing.WEST;
else if((full || x > e1) && normal[0] > e2) side = EnumFacing.EAST;
int i = side == null ? 0 : side.ordinal() + 1;
int brightness = blockInfo.getPackedLight()[i];
lightmap[0] = ((float)((brightness >> 0x04) & 0xF) * 0x20) / 0xFFFF;
lightmap[1] = ((float)((brightness >> 0x14) & 0xF) * 0x20) / 0xFFFF;
}
protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier)
{
if(tint != -1)
{
color[0] *= (float)(multiplier >> 0x10 & 0xFF) / 0xFF;
color[1] *= (float)(multiplier >> 0x8 & 0xFF) / 0xFF;
color[2] *= (float)(multiplier & 0xFF) / 0xFF;
}
}
@Override
public void setQuadTint(int tint)
{
this.tint = tint;
}
@Override
public void setQuadOrientation(EnumFacing orientation) {}
public void setQuadCulled() {}
@Override
public void setTexture(TextureAtlasSprite texture) {}
@Override
public void setApplyDiffuseLighting(boolean diffuse)
{
this.diffuse = diffuse;
}
public void setWorld(IBlockAccess world)
{
blockInfo.setWorld(world);
}
public void setState(IBlockState state)
{
blockInfo.setState(state);
}
public void setBlockPos(BlockPos blockPos)
{
blockInfo.setBlockPos(blockPos);
}
public void resetBlockInfo()
{
blockInfo.reset();
}
public void updateBlockInfo()
{
blockInfo.updateShift();
blockInfo.updateFlatLighting();
}
}

View File

@@ -0,0 +1,180 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.util.math.MathHelper;
public class VertexLighterSmoothAo extends VertexLighterFlat
{
public VertexLighterSmoothAo(BlockColors colors)
{
super(colors);
}
@Override
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z)
{
lightmap[0] = calcLightmap(blockInfo.getBlockLight(), x, y, z);
lightmap[1] = calcLightmap(blockInfo.getSkyLight(), x, y, z);
}
@Override
protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier)
{
super.updateColor(normal, color, x, y, z, tint, multiplier);
float a = getAo(x, y, z);
color[0] *= a;
color[1] *= a;
color[2] *= a;
}
protected float calcLightmap(float[][][][] light, float x, float y, float z)
{
x *= 2;
y *= 2;
z *= 2;
float l2 = x * x + y * y + z * z;
if(l2 > 6 - 2e-2f)
{
float s = (float)Math.sqrt((6 - 2e-2f) / l2);
x *= s;
y *= s;
z *= s;
}
float ax = x > 0 ? x : -x;
float ay = y > 0 ? y : -y;
float az = z > 0 ? z : -z;
float e1 = 1 + 1e-4f;
if(ax > 2 - 1e-4f && ay <= e1 && az <= e1)
{
x = x < 0 ? -2 + 1e-4f : 2 - 1e-4f;
}
else if(ay > 2 - 1e-4f && az <= e1 && ax <= e1)
{
y = y < 0 ? -2 + 1e-4f : 2 - 1e-4f;
}
else if(az > 2 - 1e-4f && ax <= e1 && ay <= e1)
{
z = z < 0 ? -2 + 1e-4f : 2 - 1e-4f;
}
ax = x > 0 ? x : -x;
ay = y > 0 ? y : -y;
az = z > 0 ? z : -z;
if(ax <= e1 && ay + az > 3f - 1e-4f)
{
float s = (3f - 1e-4f) / (ay + az);
y *= s;
z *= s;
}
else if(ay <= e1 && az + ax > 3f - 1e-4f)
{
float s = (3f - 1e-4f) / (az + ax);
z *= s;
x *= s;
}
else if(az <= e1 && ax + ay > 3f - 1e-4f)
{
float s = (3f - 1e-4f) / (ax + ay);
x *= s;
y *= s;
}
else if(ax + ay + az > 4 - 1e-4f)
{
float s = (4 - 1e-4f) / (ax + ay + az);
x *= s;
y *= s;
z *= s;
}
float l = 0;
float s = 0;
for(int ix = 0; ix <= 1; ix++)
{
for(int iy = 0; iy <= 1; iy++)
{
for(int iz = 0; iz <= 1; iz++)
{
float vx = x * (1 - ix * 2);
float vy = y * (1 - iy * 2);
float vz = z * (1 - iz * 2);
float s3 = vx + vy + vz + 4;
float sx = vy + vz + 3;
float sy = vz + vx + 3;
float sz = vx + vy + 3;
float bx = (2 * vx + vy + vz + 6) / (s3 * sy * sz * (vx + 2));
s += bx;
l += bx * light[0][ix][iy][iz];
float by = (2 * vy + vz + vx + 6) / (s3 * sz * sx * (vy + 2));
s += by;
l += by * light[1][ix][iy][iz];
float bz = (2 * vz + vx + vy + 6) / (s3 * sx * sy * (vz + 2));
s += bz;
l += bz * light[2][ix][iy][iz];
}
}
}
l /= s;
if(l > 15f * 0x20 / 0xFFFF) l = 15f * 0x20 / 0xFFFF;
if(l < 0) l = 0;
return l;
}
protected float getAo(float x, float y, float z)
{
int sx = x < 0 ? 1 : 2;
int sy = y < 0 ? 1 : 2;
int sz = z < 0 ? 1 : 2;
if(x < 0) x++;
if(y < 0) y++;
if(z < 0) z++;
float a = 0;
float[][][] ao = blockInfo.getAo();
a += ao[sx - 1][sy - 1][sz - 1] * (1 - x) * (1 - y) * (1 - z);
a += ao[sx - 1][sy - 1][sz - 0] * (1 - x) * (1 - y) * (0 + z);
a += ao[sx - 1][sy - 0][sz - 1] * (1 - x) * (0 + y) * (1 - z);
a += ao[sx - 1][sy - 0][sz - 0] * (1 - x) * (0 + y) * (0 + z);
a += ao[sx - 0][sy - 1][sz - 1] * (0 + x) * (1 - y) * (1 - z);
a += ao[sx - 0][sy - 1][sz - 0] * (0 + x) * (1 - y) * (0 + z);
a += ao[sx - 0][sy - 0][sz - 1] * (0 + x) * (0 + y) * (1 - z);
a += ao[sx - 0][sy - 0][sz - 0] * (0 + x) * (0 + y) * (0 + z);
a = MathHelper.clamp(a, 0, 1);
return a;
}
@Override
public void updateBlockInfo()
{
blockInfo.updateShift();
blockInfo.updateLightMatrix();
}
}

View File

@@ -0,0 +1,70 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
public class VertexTransformer implements IVertexConsumer
{
protected final IVertexConsumer parent;
public VertexTransformer(IVertexConsumer parent)
{
this.parent = parent;
}
@Override
public VertexFormat getVertexFormat()
{
return parent.getVertexFormat();
}
@Override
public void setQuadTint(int tint)
{
parent.setQuadTint(tint);
}
@Override
public void setTexture(TextureAtlasSprite texture)
{
parent.setTexture(texture);
}
@Override
public void setQuadOrientation(EnumFacing orientation)
{
parent.setQuadOrientation(orientation);
}
@Override
public void setApplyDiffuseLighting(boolean diffuse)
{
parent.setApplyDiffuseLighting(diffuse);
}
@Override
public void put(int element, float... data)
{
parent.put(element, data);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.client.model.pipeline;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,31 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.resource;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/**
* Represents a generic type of reloadable resource. Used for resource reload filtering.
*/
@SideOnly(Side.CLIENT)
public interface IResourceType
{
}

Some files were not shown because too many files have changed in this diff Show More