mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-08 12:34:46 +00:00
Add Custom Renderer API for Fluids (#1059)
This commit is contained in:
1 parent
a105a60a1a
commit
c020ecbf1f
7 files changed
+151
-65
No files matched your search
@@ -361,7 +361,7 @@ public class PositionedStack implements Cloneable {
|
||||
return;
|
||||
}
|
||||
|
||||
FluidDrawer.drawFluid(
|
||||
FluidDrawer.drawTank(
|
||||
this.relx,
|
||||
this.rely,
|
||||
this.width,
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@@ -33,6 +34,7 @@ import codechicken.nei.SubsetWidget.SubsetTag;
|
||||
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.config.Option;
|
||||
import codechicken.nei.item.FluidDrawer;
|
||||
import codechicken.nei.recipe.CatalystInfo;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
@@ -375,6 +377,16 @@ public class API {
|
||||
StackInfo.stackStringifyHandlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the default rendering of a fluid in NEI fluid display slots and fluid tanks.
|
||||
*
|
||||
* @param fluid the fluid to render
|
||||
* @param renderer the renderer, or null to restore the default rendering
|
||||
*/
|
||||
public static void registerFluidRenderer(Fluid fluid, IFluidRenderer renderer) {
|
||||
FluidDrawer.registerFluidRenderer(fluid, renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an association between an ingredient and what it can craft. (i.e. Furnace ItemStack -> Smelting and Fuel
|
||||
* Recipes) Allows players to see what ingredient they need to craft in order to make recipes from a recipe
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
/**
|
||||
* Custom renderer for a fluid, used by NEI fluid display slots and fluid tanks in recipes.
|
||||
* <p>
|
||||
* Register with {@link API#registerFluidRenderer(Fluid, IFluidRenderer)}.
|
||||
*/
|
||||
public interface IFluidRenderer {
|
||||
|
||||
/**
|
||||
* Renders one 16x16 tile of the fluid. Partial tiles of a tank are clipped by NEI. The amount overlay is drawn by
|
||||
* NEI.
|
||||
*/
|
||||
void renderFluid(Fluid fluid, int x, int y);
|
||||
}
|
||||
@@ -2,15 +2,14 @@ package codechicken.nei.item;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.client.IItemRenderer;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.item.FluidDrawer.FillDirection;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
@@ -34,37 +33,13 @@ public class FluidDisplayRenderer implements IItemRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
renderIcon(item, fluidDisplay);
|
||||
renderIcon(item);
|
||||
renderAmountOverlay(item, fluidDisplay);
|
||||
}
|
||||
|
||||
private void renderIcon(ItemStack item, ItemFluidDisplay fluidDisplay) {
|
||||
final IIcon icon = fluidDisplay.getIconIndex(item);
|
||||
if (icon == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int color = fluidDisplay.getColorFromItemStack(item, 0);
|
||||
final float red = (color >> 16 & 0xFF) / 255F;
|
||||
final float green = (color >> 8 & 0xFF) / 255F;
|
||||
final float blue = (color & 0xFF) / 255F;
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
|
||||
GL11.glColor4f(red, green, blue, 1F);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
OpenGlHelper
|
||||
.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.addVertexWithUV(0, 16, 0, icon.getMinU(), icon.getMaxV());
|
||||
tessellator.addVertexWithUV(16, 16, 0, icon.getMaxU(), icon.getMaxV());
|
||||
tessellator.addVertexWithUV(16, 0, 0, icon.getMaxU(), icon.getMinV());
|
||||
tessellator.addVertexWithUV(0, 0, 0, icon.getMinU(), icon.getMinV());
|
||||
tessellator.draw();
|
||||
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
private void renderIcon(ItemStack item) {
|
||||
final Fluid fluid = FluidRegistry.getFluid(item.getItemDamage());
|
||||
FluidDrawer.drawFluidArea(fluid != null ? fluid : FluidRegistry.WATER, 0, 0, 16, 16, false, FillDirection.UP);
|
||||
}
|
||||
|
||||
private void renderAmountOverlay(ItemStack item, ItemFluidDisplay fluidDisplay) {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
package codechicken.nei.item;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.api.IFluidRenderer;
|
||||
import codechicken.nei.scroll.GuiHelper;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@@ -27,28 +34,32 @@ public class FluidDrawer {
|
||||
return this == LEFT || this == RIGHT;
|
||||
}
|
||||
|
||||
public FillDirection resolve(FluidStack fluidStack) {
|
||||
public FillDirection resolve(Fluid fluid) {
|
||||
if (this != AUTO) return this;
|
||||
return fluidStack.getFluid().isGaseous(fluidStack) ? DOWN : UP;
|
||||
return fluid.isGaseous() ? DOWN : UP;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Fluid, IFluidRenderer> fluidRenderers = new IdentityHashMap<>();
|
||||
|
||||
private FluidDrawer() {}
|
||||
|
||||
public static void drawFluid(int x, int y, int width, int height, int capacity, FluidStack fluidStack,
|
||||
boolean flowing, FillDirection direction) {
|
||||
final Fluid fluid = fluidStack.getFluid();
|
||||
IIcon icon = flowing ? fluid.getFlowingIcon() : null;
|
||||
|
||||
if (icon == null) {
|
||||
icon = fluid.getIcon(fluidStack);
|
||||
public static void registerFluidRenderer(Fluid fluid, IFluidRenderer renderer) {
|
||||
if (renderer == null) {
|
||||
fluidRenderers.remove(fluid);
|
||||
} else {
|
||||
fluidRenderers.put(fluid, renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (icon == null || fluidStack.amount <= 0) {
|
||||
public static void drawTank(int x, int y, int width, int height, int capacity, FluidStack fluidStack,
|
||||
boolean flowing, FillDirection direction) {
|
||||
if (fluidStack.amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
direction = direction.resolve(fluidStack);
|
||||
final Fluid fluid = fluidStack.getFluid();
|
||||
direction = direction.resolve(fluid);
|
||||
|
||||
final int tankSize = direction.isHorizontal() ? width : height;
|
||||
final int tankCapacity = capacity > 0 ? capacity : fluidStack.amount;
|
||||
@@ -59,38 +70,109 @@ public class FluidDrawer {
|
||||
final int fillHeight = direction.isHorizontal() ? height : Math.min(fillSize, height);
|
||||
final int fillX = direction == FillDirection.LEFT ? x + width - fillWidth : x;
|
||||
final int fillY = direction == FillDirection.DOWN ? y : y + height - fillHeight;
|
||||
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
drawFluidArea(fluid, fillX, fillY, fillWidth, fillHeight, flowing, direction);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the area with fluid tiles. The tiles start from the side the fluid fills from, so a partial tile ends up at
|
||||
* the fluid surface.
|
||||
*/
|
||||
public static void drawFluidArea(Fluid fluid, int x, int y, int width, int height, boolean flowing,
|
||||
FillDirection direction) {
|
||||
direction = direction.resolve(fluid);
|
||||
final boolean anchorRight = direction == FillDirection.LEFT;
|
||||
final boolean anchorBottom = direction != FillDirection.DOWN;
|
||||
final IFluidRenderer renderer = fluidRenderers.get(fluid);
|
||||
|
||||
final int color = fluid.getColor(fluidStack);
|
||||
if (renderer == null) {
|
||||
drawDefaultFluid(fluid, x, y, width, height, flowing, anchorRight, anchorBottom);
|
||||
} else {
|
||||
drawCustomFluid(renderer, fluid, x, y, width, height, anchorRight, anchorBottom);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawCustomFluid(IFluidRenderer renderer, Fluid fluid, int x, int y, int width, int height,
|
||||
boolean anchorRight, boolean anchorBottom) {
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
|
||||
final boolean clip = width % 16 != 0 || height % 16 != 0;
|
||||
|
||||
if (clip) {
|
||||
GuiHelper.pushScissorFrame(x, y, width, height);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int tx = 0; tx < width; tx += 16) {
|
||||
final int tileX = anchorRight ? x + width - tx - 16 : x + tx;
|
||||
|
||||
for (int ty = 0; ty < height; ty += 16) {
|
||||
final int tileY = anchorBottom ? y + height - ty - 16 : y + ty;
|
||||
renderer.renderFluid(fluid, tileX, tileY);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (clip) {
|
||||
GuiHelper.popScissorFrame();
|
||||
}
|
||||
|
||||
GL11.glPopAttrib();
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawDefaultFluid(Fluid fluid, int x, int y, int width, int height, boolean flowing,
|
||||
boolean anchorRight, boolean anchorBottom) {
|
||||
IIcon icon = flowing ? fluid.getFlowingIcon() : null;
|
||||
|
||||
if (icon == null) {
|
||||
icon = fluid.getStillIcon();
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
icon = FluidRegistry.WATER.getStillIcon();
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int color = fluid.getColor();
|
||||
final float red = (color >> 16 & 0xFF) / 255F;
|
||||
final float green = (color >> 8 & 0xFF) / 255F;
|
||||
final float blue = (color & 0xFF) / 255F;
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
|
||||
GL11.glColor3f(red, green, blue);
|
||||
GL11.glColor4f(red, green, blue, 1F);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
OpenGlHelper
|
||||
.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
final double iconMinU = icon.getMinU();
|
||||
final double iconMaxU = icon.getMaxU();
|
||||
final double iconMinV = icon.getMinV();
|
||||
final double iconMaxV = icon.getMaxV();
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
for (int tx = 0; tx < fillWidth; tx += 16) {
|
||||
final int tileWidth = Math.min(16, fillWidth - tx);
|
||||
for (int tx = 0; tx < width; tx += 16) {
|
||||
final int tileWidth = Math.min(16, width - tx);
|
||||
// a cropped tile keeps the texture edge that stays seamless against the neighbouring tile
|
||||
final double uSize = (icon.getMaxU() - icon.getMinU()) * tileWidth / 16D;
|
||||
final double uMin = anchorRight ? icon.getMaxU() - uSize : icon.getMinU();
|
||||
final double uMax = anchorRight ? icon.getMaxU() : icon.getMinU() + uSize;
|
||||
final int leftX = anchorRight ? fillX + fillWidth - tx - tileWidth : fillX + tx;
|
||||
final double uSize = (iconMaxU - iconMinU) * tileWidth / 16D;
|
||||
final double uMin = anchorRight ? iconMaxU - uSize : iconMinU;
|
||||
final double uMax = anchorRight ? iconMaxU : iconMinU + uSize;
|
||||
final int leftX = anchorRight ? x + width - tx - tileWidth : x + tx;
|
||||
final int rightX = leftX + tileWidth;
|
||||
|
||||
for (int ty = 0; ty < fillHeight; ty += 16) {
|
||||
final int tileHeight = Math.min(16, fillHeight - ty);
|
||||
final double vSize = (icon.getMaxV() - icon.getMinV()) * tileHeight / 16D;
|
||||
final double vMin = anchorBottom ? icon.getMaxV() - vSize : icon.getMinV();
|
||||
final double vMax = anchorBottom ? icon.getMaxV() : icon.getMinV() + vSize;
|
||||
final int bottomY = anchorBottom ? fillY + fillHeight - ty : fillY + ty + tileHeight;
|
||||
for (int ty = 0; ty < height; ty += 16) {
|
||||
final int tileHeight = Math.min(16, height - ty);
|
||||
final double vSize = (iconMaxV - iconMinV) * tileHeight / 16D;
|
||||
final double vMin = anchorBottom ? iconMaxV - vSize : iconMinV;
|
||||
final double vMax = anchorBottom ? iconMaxV : iconMinV + vSize;
|
||||
final int bottomY = anchorBottom ? y + height - ty : y + ty + tileHeight;
|
||||
final int topY = bottomY - tileHeight;
|
||||
|
||||
tessellator.addVertexWithUV(leftX, bottomY, 0, uMin, vMax);
|
||||
@@ -102,7 +184,6 @@ public class FluidDrawer {
|
||||
tessellator.draw();
|
||||
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class ItemFluidDisplay extends Item implements IFluidContainerItem {
|
||||
@Override
|
||||
public FluidStack getFluid(ItemStack stack) {
|
||||
|
||||
if (stack == null || !(stack.getItem() instanceof ItemFluidDisplay) || !stack.hasTagCompound()) {
|
||||
if (stack == null || !(stack.getItem() instanceof ItemFluidDisplay)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class ItemFluidDisplay extends Item implements IFluidContainerItem {
|
||||
final NBTTagCompound nbTag = stack.getTagCompound();
|
||||
final FluidStack fluidStack = new FluidStack(
|
||||
fluid,
|
||||
(int) Math.min(nbTag.getLong("neiFluidDisplayAmount"), Integer.MAX_VALUE));
|
||||
(int) Math.min(nbTag != null ? nbTag.getLong("neiFluidDisplayAmount") : 0, Integer.MAX_VALUE));
|
||||
|
||||
return fluidStack;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import cpw.mods.fml.relauncher.SideOnly;
|
||||
public class GuiHelper {
|
||||
|
||||
private static final Stack<int[]> scissorFrameStack = new Stack<>();
|
||||
private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
public static void useScissor(int x, int y, int width, int height, Runnable codeBlock) {
|
||||
pushScissorFrame(x, y, width, height);
|
||||
@@ -36,7 +37,7 @@ public class GuiHelper {
|
||||
return currentTopFrame;
|
||||
}
|
||||
|
||||
private static void pushScissorFrame(int x, int y, int width, int height) {
|
||||
public static void pushScissorFrame(int x, int y, int width, int height) {
|
||||
int[] matrix = prepareMatrix(x, y, width, height);
|
||||
int[] parentScissor = peekFirstScissorOrFullScreen();
|
||||
int parentX = parentScissor[0];
|
||||
@@ -110,7 +111,7 @@ public class GuiHelper {
|
||||
|
||||
private static int[] prepareMatrix(int x, int y, int w, int h) {
|
||||
// Get the current translation component to support the Gui being drawn at any position on the screen
|
||||
final FloatBuffer matBuf = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer matBuf = MATRIX_BUFFER;
|
||||
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matBuf);
|
||||
final float xScale = matBuf.get(0);
|
||||
final float yScale = matBuf.get(5);
|
||||
|
||||
Reference in new issue
Block a user