Java tutorial
/* * This class was created by: RANKSHANK as a part of the Arbitraria mod. * Full source can be found @ https://github.com/RANKSHANK/Arbitraria * * Arbitraria is provided in binary and source forms under the Arbitraria License. * Arbitraria License can be found in the repository @ https://github.com/RANKSHANK/Arbitraria */ package mod.rankshank.arbitraria.client.texture; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import mod.rankshank.arbitraria.client.vars.ClientReflectionHandles; import mod.rankshank.arbitraria.common.init.Arbitraria; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.texture.*; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.ICrashReportDetail; import net.minecraft.util.ReportedException; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.common.ProgressManager; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.io.IOUtils; import javax.annotation.Nonnull; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.function.Function; import static com.google.common.collect.Lists.newArrayList; @SideOnly(Side.CLIENT) public class CustomTextureMap extends AbstractTexture implements ITickableTextureObject { public final ResourceLocation TEXTURE_KEY; private int mipmapLevels = 4; private final Map<String, TextureAtlasSprite> mapUploadedSprites; private final Map<String, TextureAtlasSprite> mapRegisteredSprites; private final List<TextureAtlasSprite> animationList; private final TextureAtlasSprite missing; private final ImmutableList<IIconRegistrar> registrars; public CustomTextureMap(ResourceLocation texture_key, IIconRegistrar... registrars) { this.registrars = ImmutableList.copyOf(registrars); TEXTURE_KEY = texture_key; mapUploadedSprites = Maps.newHashMap(); mapRegisteredSprites = Maps.newHashMap(); animationList = newArrayList(); missing = makeAtlas(new ResourceLocation("missingno")); } private void initMissingImage() { int[] textureData = TextureUtil.MISSING_TEXTURE_DATA; this.missing.setIconWidth(16); this.missing.setIconHeight(16); int[][] mipData = new int[this.mipmapLevels + 1][]; mipData[0] = textureData; this.missing.setFramesTextureData(newArrayList(new int[][][] { mipData })); } @Override public void loadTexture(IResourceManager resourceManager) throws IOException { this.deleteGlTexture(); initMissingImage(); int i = Minecraft.getGLMaximumTextureSize(); Stitcher stitcher = new Stitcher(i, i, 0, this.mipmapLevels); this.mapUploadedSprites.clear(); this.mapRegisteredSprites.clear(); animationList.clear(); registrars.forEach(icon -> icon.onRegister(loc -> registerSprite(loc))); int j = Integer.MAX_VALUE; int k = 1 << this.mipmapLevels; ProgressManager.ProgressBar bar = ProgressManager.push("Texture" + TEXTURE_KEY.toString() + "stitching", this.mapRegisteredSprites.size()); for (Map.Entry<String, TextureAtlasSprite> entry : this.mapRegisteredSprites.entrySet()) { TextureAtlasSprite textureatlassprite = entry.getValue(); ResourceLocation resourcelocation = this.getResourceLocation(textureatlassprite); bar.step(resourcelocation.getResourcePath()); IResource iresource = null; if (textureatlassprite.hasCustomLoader(resourceManager, resourcelocation)) { if (textureatlassprite.load(resourceManager, resourcelocation)) { continue; } } else try { PngSizeInfo pngsizeinfo = PngSizeInfo .makeFromResource(resourceManager.getResource(resourcelocation)); iresource = resourceManager.getResource(resourcelocation); boolean flag = iresource.getMetadata("animation") != null; textureatlassprite.loadSprite(pngsizeinfo, flag); } catch (RuntimeException runtimeexception) { //LOGGER.error("Unable to parse metadata from {}", new Object[] {resourcelocation, runtimeexception}); FMLClientHandler.instance().trackBrokenTexture(resourcelocation, runtimeexception.getMessage()); continue; } catch (IOException ioexception) { //LOGGER.error("Using missing texture, unable to load {}", new Object[] {resourcelocation, ioexception}); FMLClientHandler.instance().trackMissingTexture(resourcelocation); continue; } finally { IOUtils.closeQuietly(iresource); } j = Math.min(j, Math.min(textureatlassprite.getIconWidth(), textureatlassprite.getIconHeight())); int squarCheck = Math.min(Integer.lowestOneBit(textureatlassprite.getIconWidth()), Integer.lowestOneBit(textureatlassprite.getIconHeight())); if (squarCheck < k) { Arbitraria.log("TEXTURE DERP @" + textureatlassprite.getIconName()); } stitcher.addSprite(textureatlassprite); } ProgressManager.pop(bar); int l = Math.min(j, k); int i1 = MathHelper.log2(l); missing.generateMipmaps(this.mipmapLevels); stitcher.addSprite(missing); bar = ProgressManager.push("Texture creation", 2); bar.step("Stitching"); stitcher.doStitch(); bar.step("Allocating GL texture"); TextureUtil.allocateTextureImpl(this.getGlTextureId(), this.mipmapLevels, stitcher.getCurrentWidth(), stitcher.getCurrentHeight()); Map<String, TextureAtlasSprite> map = Maps.newHashMap(this.mapRegisteredSprites); ProgressManager.pop(bar); bar = ProgressManager.push("Texture mipmap and upload", stitcher.getStichSlots().size()); for (TextureAtlasSprite stitchingSprite : stitcher.getStichSlots()) { bar.step(stitchingSprite.getIconName()); if (stitchingSprite == this.missing || this.generateMipmaps(resourceManager, stitchingSprite)) { String s = stitchingSprite.getIconName(); map.remove(s); this.mapUploadedSprites.put(s, stitchingSprite); try { TextureUtil.uploadTextureMipmap(stitchingSprite.getFrameTextureData(0), stitchingSprite.getIconWidth(), stitchingSprite.getIconHeight(), stitchingSprite.getOriginX(), stitchingSprite.getOriginY(), false, false); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Stitching texture atlas"); CrashReportCategory crashreportcategory = crashreport .makeCategory("Texture being stitched together"); crashreportcategory.addCrashSection("Atlas path", this.TEXTURE_KEY); crashreportcategory.addCrashSection("Sprite", stitchingSprite); throw new ReportedException(crashreport); } if (stitchingSprite.hasAnimationMetadata()) { this.animationList.add(stitchingSprite); } } } } private boolean generateMipmaps(IResourceManager resourceManager, TextureAtlasSprite texture) { ResourceLocation resourcelocation = this.getResourceLocation(texture); IResource iresource = null; loadFrames: { boolean flag; if (texture.hasCustomLoader(resourceManager, resourcelocation)) break loadFrames; try { iresource = resourceManager.getResource(resourcelocation); texture.loadSpriteFrames(iresource, this.mipmapLevels + 1); break loadFrames; } catch (RuntimeException runtimeexception) { flag = false; } catch (IOException ioexception) { return false; } finally { IOUtils.closeQuietly(iresource); } return flag; } try { texture.generateMipmaps(this.mipmapLevels); return true; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Applying mipmap"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Sprite being mipmapped"); crashreportcategory.setDetail("Sprite name", new ICrashReportDetail<String>() { @Override public String call() throws Exception { return texture.getIconName(); } }); crashreportcategory.setDetail("Sprite size", new ICrashReportDetail<String>() { @Override public String call() throws Exception { return texture.getIconWidth() + " x " + texture.getIconHeight(); } }); crashreportcategory.setDetail("Sprite frames", new ICrashReportDetail<String>() { @Override public String call() throws Exception { return texture.getFrameCount() + " frames"; } }); crashreportcategory.addCrashSection("Mipmap levels", this.mipmapLevels); throw new ReportedException(crashreport); } } public TextureAtlasSprite registerSprite(@Nonnull ResourceLocation location) { TextureAtlasSprite textureatlassprite = mapRegisteredSprites.get(location.toString()); if (textureatlassprite == null) { textureatlassprite = makeAtlas(location); this.mapRegisteredSprites.put(location.toString(), textureatlassprite); } return textureatlassprite; } private ResourceLocation getResourceLocation(TextureAtlasSprite sprite) { ResourceLocation resourcelocation = new ResourceLocation(sprite.getIconName()); return new ResourceLocation(resourcelocation.getResourceDomain(), String.format("%s/%s%s", "textures", resourcelocation.getResourcePath(), ".png")); } public TextureAtlasSprite getAtlasSprite(String iconName) { TextureAtlasSprite textureatlassprite = this.mapUploadedSprites.get(iconName); if (textureatlassprite == null) textureatlassprite = missing; return textureatlassprite; } private TextureAtlasSprite makeAtlas(@Nonnull ResourceLocation location) { TextureAtlasSprite sprite = null; try { sprite = (TextureAtlasSprite) ClientReflectionHandles.TextureAtlasSprite_MakeAtlasSprite.invoke(null, location); } catch (Exception ex) { Arbitraria.error(new Throwable(String.format("ISSUE CREATING TEXTURE ATLAS from %s for %s", location.toString(), TEXTURE_KEY.toString())), ex); } return sprite; } @Override public void tick() { GlStateManager.bindTexture(this.getGlTextureId()); animationList.forEach(TextureAtlasSprite::updateAnimation); } public interface IIconRegistrar { void onRegister(Function<ResourceLocation, TextureAtlasSprite> register); } }