/
ClientUtil.java
249 lines (231 loc) · 8.75 KB
/
ClientUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package knightminer.inspirations.library.client;
import knightminer.inspirations.Inspirations;
import knightminer.inspirations.library.InspirationsRegistry;
import knightminer.inspirations.library.ItemMetaKey;
import knightminer.inspirations.library.util.TextureBlockUtil;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.apache.commons.lang3.math.NumberUtils;
import org.lwjgl.opengl.GL11;
import slimeknights.mantle.client.ModelHelper;
import javax.annotation.Nullable;
import java.awt.Color;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@SideOnly(Side.CLIENT)
public final class ClientUtil {
public static final String TAG_TEXTURE_PATH = "texture_path";
private static final Minecraft mc = Minecraft.getMinecraft();
private ClientUtil() {}
private static Map<ItemMetaKey, Integer> colorCache = new HashMap<>();
/**
* Gets the color for an ItemStack based on its item and metadata
* @param stack Input stack
* @return Color for the stack
*/
public static int getStackColor(ItemStack stack) {
return colorCache.computeIfAbsent(new ItemMetaKey(stack), ClientUtil::getStackColor);
}
/**
* Gets the color for an item stack, used internally by colorCache. Licensed under http://www.apache.org/licenses/LICENSE-2.0
* @param key Item meta cache combination
* @return Color for the item meta combination
* @author InsomniaKitten
*/
private static Integer getStackColor(ItemMetaKey key) {
IBakedModel model = mc.getRenderItem().getItemModelWithOverrides(key.makeItemStack(), null, null);
if(model == null) {
return -1;
}
TextureAtlasSprite sprite = model.getParticleTexture();
if(sprite == null) {
return -1;
}
int[] pixels = sprite.getFrameTextureData(0)[0];
float r = 0, g = 0, b = 0, count = 0;
float[] hsb = new float[3];
for (int argb : pixels) {
int ca = argb >> 24 & 0xFF;
int cr = argb >> 16 & 0xFF;
int cg = argb >> 8 & 0xFF;
int cb = argb & 0xFF;
if (ca > 0x7F && NumberUtils.max(cr, cg, cb) > 0x1F) {
Color.RGBtoHSB(ca, cr, cg, hsb);
float weight = hsb[1];
r += cr * weight;
g += cg * weight;
b += cb * weight;
count += weight;
}
}
if (count > 0) {
r /= count;
g /= count;
b /= count;
}
return 0xFF000000 | (int) r << 16 | (int) g << 8 | (int) b;
}
/**
* Called on resource reload to clear any resource based cache
* @param manager
*/
public static void onResourceReload(IResourceManager manager) {
colorCache.clear();
}
/**
* Gets the sprite for the given texture location, or null if no sprite is found
* @param location
* @return
*/
public static TextureAtlasSprite getSprite(ResourceLocation location) {
TextureMap textureMapBlocks = mc.getTextureMapBlocks();
TextureAtlasSprite sprite = null;
if(location != null) {
sprite = textureMapBlocks.getTextureExtry(location.toString());
}
if (sprite == null) {
sprite = textureMapBlocks.getMissingSprite();
}
return sprite;
}
public static void renderFilledSprite(TextureAtlasSprite sprite, final int x, final int y, final int size, final int filled) {
double uMin = sprite.getMinU();
double uMax = sprite.getMaxU();
double vMin = sprite.getMinV();
double vMax = sprite.getMaxV();
uMax = uMax - (16 - size) / 16.0 * (uMax - uMin);
vMax = vMax - (16 - filled) / 16.0 * (vMax - vMin);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
bufferBuilder.pos(x, y + size, 100).tex(uMin, vMax).endVertex();
bufferBuilder.pos(x + size, y + size, 100).tex(uMax, vMax).endVertex();
bufferBuilder.pos(x + size, y + size - filled, 100).tex(uMax, vMin).endVertex();
bufferBuilder.pos(x, y + size - filled, 100).tex(uMin, vMin).endVertex();
tessellator.draw();
}
/**
* Gets the cached texture from the TileEntity, or stores it from the texture stack if none is cached
* @param te Tile Entity
* @return String of texture path, or empty string if none found
*/
public static String getTexturePath(TileEntity te) {
String texture = te.getTileData().getString(TAG_TEXTURE_PATH);
if(texture.isEmpty()) {
// load it from saved block
ItemStack stack = new ItemStack(te.getTileData().getCompoundTag(TextureBlockUtil.TAG_TEXTURE));
if(!stack.isEmpty()) {
Block block = Block.getBlockFromItem(stack.getItem());
texture = ModelHelper.getTextureFromBlock(block, stack.getItem().getMetadata(stack)).getIconName();
te.getTileData().setString(TAG_TEXTURE_PATH, texture);
}
}
return texture;
}
/**
* Writes the default extended blockstate for a texture block
* @param world World
* @param pos Pos
* @param state State
* @return The extended block state
*/
public static IBlockState writeTextureBlockState(IBlockAccess world, BlockPos pos, IBlockState state) {
TileEntity te = world.getTileEntity(pos);
if(te != null) {
String texture = getTexturePath(te);
if(!texture.isEmpty()) {
state = ((IExtendedBlockState)state).withProperty(TextureBlockUtil.TEXTURE_PROP, texture);
}
}
return state;
}
/** Any items which have blockColors methods that throw an exception */
private static Set<Item> unsafe = new HashSet<>();
/**
* Gets the block colors for a block from an itemstack, logging an exception if it fails. Use this to get block colors when the implementation is unknown
* @param stack Stack to use
* @param world World
* @param pos Pos
* @param index Tint index
* @return color, or -1 for undefined
*/
public static int getStackBlockColorsSafe(ItemStack stack, @Nullable IBlockAccess world, @Nullable BlockPos pos, int index) {
if(stack.isEmpty()) {
return -1;
}
// do not try if it failed before
Item item = stack.getItem();
if(!ClientUtil.unsafe.contains(item)) {
try {
return ClientUtil.getStackBlockColors(stack, world, pos, index);
} catch (Exception e) {
// catch and log possible exceptions. Most likely exception is ClassCastException if they do not perform safety checks
Inspirations.log.error(String.format("Caught exception getting block colors for %s", item.getRegistryName()), e);
ClientUtil.unsafe.add(item);
}
}
// fallback to item colors
return mc.getItemColors().colorMultiplier(stack, index);
}
/**
* Gets the block colors from an item stack
* @param stack Stack to check
* @param world World
* @param pos Pos
* @param index Tint index
* @return color, or -1 for undefined
*/
public static int getStackBlockColors(ItemStack stack, @Nullable IBlockAccess world, @Nullable BlockPos pos, int index) {
if(stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
return -1;
}
ItemBlock item = (ItemBlock) stack.getItem();
IBlockState iblockstate = item.getBlock().getStateFromMeta(item.getMetadata(stack));
return mc.getBlockColors().colorMultiplier(iblockstate, world, pos, index);
}
/**
* Renders a colored sprite to display in JEI as cauldron contents
* @param mc Minecraft instance
* @param x Sprite X position
* @param y Sprite Y position
* @param location Sprite resource location
* @param color Sprite color
* @param level Cauldron level
*/
public static void renderJEICauldronFluid(Minecraft mc, int x, int y, ResourceLocation location, float[] color, int level) {
GlStateManager.enableBlend();
mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
GlStateManager.color(color[0], color[1], color[2]);
// 0 means JEI ingredient list
TextureAtlasSprite sprite = ClientUtil.getSprite(location);
if(level == 0) {
ClientUtil.renderFilledSprite(sprite, x, y, 16, 16);
} else {
int height = ((10 * level) / InspirationsRegistry.getCauldronMax());
ClientUtil.renderFilledSprite(sprite, x, y, 10, height);
}
GlStateManager.color(1, 1, 1);
GlStateManager.disableBlend();
}
}