package net.minecraftforge.fml.client; import static org.lwjgl.opengl.GL11.GL_ALPHA_TEST; import static org.lwjgl.opengl.GL11.GL_BLEND; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; import static org.lwjgl.opengl.GL11.GL_GREATER; import static org.lwjgl.opengl.GL11.GL_LEQUAL; import static org.lwjgl.opengl.GL11.GL_LIGHTING; import static org.lwjgl.opengl.GL11.GL_MODELVIEW; import static org.lwjgl.opengl.GL11.GL_NEAREST; import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA; import static org.lwjgl.opengl.GL11.GL_PROJECTION; import static org.lwjgl.opengl.GL11.GL_QUADS; import static org.lwjgl.opengl.GL11.GL_RENDERER; import static org.lwjgl.opengl.GL11.GL_RGBA; import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA; import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER; import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER; import static org.lwjgl.opengl.GL11.GL_VENDOR; import static org.lwjgl.opengl.GL11.GL_VERSION; import static org.lwjgl.opengl.GL11.glAlphaFunc; import static org.lwjgl.opengl.GL11.glBegin; import static org.lwjgl.opengl.GL11.glBindTexture; import static org.lwjgl.opengl.GL11.glBlendFunc; import static org.lwjgl.opengl.GL11.glClear; import static org.lwjgl.opengl.GL11.glClearColor; import static org.lwjgl.opengl.GL11.glColor3ub; import static org.lwjgl.opengl.GL11.glDeleteTextures; import static org.lwjgl.opengl.GL11.glDepthFunc; import static org.lwjgl.opengl.GL11.glDisable; import static org.lwjgl.opengl.GL11.glEnable; import static org.lwjgl.opengl.GL11.glEnd; import static org.lwjgl.opengl.GL11.glGenTextures; import static org.lwjgl.opengl.GL11.glGetString; import static org.lwjgl.opengl.GL11.glLoadIdentity; import static org.lwjgl.opengl.GL11.glMatrixMode; import static org.lwjgl.opengl.GL11.glOrtho; import static org.lwjgl.opengl.GL11.glPopMatrix; import static org.lwjgl.opengl.GL11.glPushMatrix; import static org.lwjgl.opengl.GL11.glRotatef; import static org.lwjgl.opengl.GL11.glScalef; import static org.lwjgl.opengl.GL11.glTexCoord2f; import static org.lwjgl.opengl.GL11.glTexImage2D; import static org.lwjgl.opengl.GL11.glTexParameteri; import static org.lwjgl.opengl.GL11.glTexSubImage2D; import static org.lwjgl.opengl.GL11.glTranslatef; import static org.lwjgl.opengl.GL11.glVertex2f; import static org.lwjgl.opengl.GL11.glViewport; import static org.lwjgl.opengl.GL12.GL_BGRA; import static org.lwjgl.opengl.GL12.GL_UNSIGNED_INT_8_8_8_8_REV; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.lang.Thread.UncaughtExceptionHandler; import java.nio.IntBuffer; import java.util.Iterator; import java.util.Properties; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.resources.FileResourcePack; import net.minecraft.client.resources.FolderResourcePack; import net.minecraft.client.resources.IResourcePack; import net.minecraft.crash.CrashReport; import net.minecraft.launchwrapper.Launch; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.ICrashCallable; import net.minecraftforge.fml.common.ProgressManager; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; import net.minecraftforge.fml.common.asm.FMLSanityChecker; import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.Level; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Drawable; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.SharedDrawable; import org.lwjgl.util.glu.GLU; /** * @deprecated not a stable API, will break, don't use this yet */ @Deprecated public class SplashProgress { private static Drawable d; private static volatile boolean pause = false; private static volatile boolean done = false; private static Thread thread; private static volatile Throwable threadError; private static int angle = 0; private static final Lock lock = new ReentrantLock(true); private static SplashFontRenderer fontRenderer; private static final IResourcePack mcPack = Minecraft.getMinecraft().mcDefaultResourcePack; private static final IResourcePack fmlPack = createResourcePack(FMLSanityChecker.fmlLocation); private static IResourcePack miscPack; private static Texture fontTexture; private static Texture logoTexture; private static Texture forgeTexture; private static Properties config; private static boolean enabled; private static boolean rotate; private static int logoOffset; private static int backgroundColor; private static int fontColor; private static int barBorderColor; private static int barColor; private static int barBackgroundColor; private static String getString(String name, String def) { String value = config.getProperty(name, def); config.setProperty(name, value); return value; } private static boolean getBool(String name, boolean def) { return Boolean.parseBoolean(getString(name, Boolean.toString(def))); } private static int getInt(String name, int def) { return Integer.decode(getString(name, Integer.toString(def))); } private static int getHex(String name, int def) { return Integer.decode(getString(name, "0x" + Integer.toString(def, 16).toUpperCase())); } public static void start() { File configFile = new File(Minecraft.getMinecraft().mcDataDir, "config/splash.properties"); FileReader r = null; config = new Properties(); try { r = new FileReader(configFile); config.load(r); } catch(IOException e) { FMLLog.info("Could not load splash.properties, will create a default one"); } finally { IOUtils.closeQuietly(r); } // Enable if we have the flag, and there's either no optifine, or optifine has added a key to the blackboard ("optifine.ForgeSplashCompatible") // Optifine authors - add this key to the blackboard if you feel your modifications are now compatible with this code. enabled = getBool("enabled", true) && ( (!FMLClientHandler.instance().hasOptifine()) || Launch.blackboard.containsKey("optifine.ForgeSplashCompatible")); rotate = getBool("rotate", false); logoOffset = getInt("logoOffset", 0); backgroundColor = getHex("background", 0xFFFFFF); fontColor = getHex("font", 0x000000); barBorderColor = getHex("barBorder", 0xC0C0C0); barColor = getHex("bar", 0xCB3D35); barBackgroundColor = getHex("barBackground", 0xFFFFFF); final ResourceLocation fontLoc = new ResourceLocation(getString("fontTexture", "textures/font/ascii.png")); final ResourceLocation logoLoc = new ResourceLocation(getString("logoTexture", "textures/gui/title/mojang.png")); final ResourceLocation forgeLoc = new ResourceLocation(getString("forgeTexture", "fml:textures/gui/forge.gif")); File miscPackFile = new File(Minecraft.getMinecraft().mcDataDir, getString("resourcePackPath", "resources")); FileWriter w = null; try { w = new FileWriter(configFile); config.store(w, "Splash screen properties"); } catch(IOException e) { FMLLog.log(Level.ERROR, e, "Could not save the splash.properties file"); } finally { IOUtils.closeQuietly(w); } miscPack = createResourcePack(miscPackFile); if(!enabled) return; // getting debug info out of the way, while we still can FMLCommonHandler.instance().registerCrashCallable(new ICrashCallable() { public String call() throws Exception { return "' Vendor: '" + glGetString(GL_VENDOR) + "' Version: '" + glGetString(GL_VERSION) + "' Renderer: '" + glGetString(GL_RENDERER) + "'"; } public String getLabel() { return "GL info"; } }); CrashReport report = CrashReport.makeCrashReport(new Throwable(), "Loading screen debug info"); System.out.println(report.getCompleteReport()); try { d = new SharedDrawable(Display.getDrawable()); Display.getDrawable().releaseContext(); d.makeCurrent(); } catch (LWJGLException e) { e.printStackTrace(); throw new RuntimeException(e); } Thread mainThread = Thread.currentThread(); thread = new Thread(new Runnable() { private final int barWidth = 400; private final int barHeight = 20; private final int textHeight2 = 20; private final int barOffset = 55; public void run() { setGL(); fontTexture = new Texture(fontLoc); logoTexture = new Texture(logoLoc); forgeTexture = new Texture(forgeLoc); glEnable(GL_TEXTURE_2D); fontRenderer = new SplashFontRenderer(); glDisable(GL_TEXTURE_2D); while(!done) { ProgressBar first = null, penult = null, last = null; Iterator<ProgressBar> i = ProgressManager.barIterator(); while(i.hasNext()) { if(first == null) first = i.next(); else { penult = last; last = i.next(); } } glClear(GL_COLOR_BUFFER_BIT); // matrix setup int w = Display.getWidth(); int h = Display.getHeight(); glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(320 - w/2, 320 + w/2, 240 + h/2, 240 - h/2, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // mojang logo setColor(backgroundColor); glEnable(GL_TEXTURE_2D); logoTexture.bind(); glBegin(GL_QUADS); logoTexture.texCoord(0, 0, 0); glVertex2f(320 - 256, 240 - 256); logoTexture.texCoord(0, 0, 1); glVertex2f(320 - 256, 240 + 256); logoTexture.texCoord(0, 1, 1); glVertex2f(320 + 256, 240 + 256); logoTexture.texCoord(0, 1, 0); glVertex2f(320 + 256, 240 - 256); glEnd(); glDisable(GL_TEXTURE_2D); // bars if(first != null) { glPushMatrix(); glTranslatef(320 - (float)barWidth / 2, 310, 0); drawBar(first); if(penult != null) { glTranslatef(0, barOffset, 0); drawBar(penult); } if(last != null) { glTranslatef(0, barOffset, 0); drawBar(last); } glPopMatrix(); } angle += 1; // forge logo setColor(backgroundColor); float fw = (float)forgeTexture.getWidth() / 2; float fh = (float)forgeTexture.getHeight() / 2; if(rotate) { float sh = Math.max(fw, fh); glTranslatef(320 + w/2 - sh - logoOffset, 240 + h/2 - sh - logoOffset, 0); glRotatef(angle, 0, 0, 1); } else { glTranslatef(320 + w/2 - fw - logoOffset, 240 + h/2 - fh - logoOffset, 0); } int f = (angle / 10) % forgeTexture.getFrames(); glEnable(GL_TEXTURE_2D); forgeTexture.bind(); glBegin(GL_QUADS); forgeTexture.texCoord(f, 0, 0); glVertex2f(-fw, -fh); forgeTexture.texCoord(f, 0, 1); glVertex2f(-fw, fh); forgeTexture.texCoord(f, 1, 1); glVertex2f(fw, fh); forgeTexture.texCoord(f, 1, 0); glVertex2f(fw, -fh); glEnd(); glDisable(GL_TEXTURE_2D); Display.update(); if(pause) { clearGL(); setGL(); } Display.sync(100); } clearGL(); } private void setColor(int color) { glColor3ub((byte)((color >> 16) & 0xFF), (byte)((color >> 8) & 0xFF), (byte)(color & 0xFF)); } private void drawBox(int w, int h) { glBegin(GL_QUADS); glVertex2f(0, 0); glVertex2f(0, h); glVertex2f(w, h); glVertex2f(w, 0); glEnd(); } private void drawBar(ProgressBar b) { glPushMatrix(); // title - message setColor(fontColor); glScalef(2, 2, 1); glEnable(GL_TEXTURE_2D); fontRenderer.drawString(b.getTitle() + " - " + b.getMessage(), 0, 0, 0x000000); glDisable(GL_TEXTURE_2D); glPopMatrix(); // border glPushMatrix(); glTranslatef(0, textHeight2, 0); setColor(barBorderColor); drawBox(barWidth, barHeight); // interior setColor(barBackgroundColor); glTranslatef(1, 1, 0); drawBox(barWidth - 2, barHeight - 2); // slidy part setColor(barColor); drawBox((barWidth - 2) * b.getStep() / b.getSteps(), barHeight - 2); // progress text String progress = "" + b.getStep() + "/" + b.getSteps(); glTranslatef(((float)barWidth - 2) / 2 - fontRenderer.getStringWidth(progress), 2, 0); setColor(fontColor); glScalef(2, 2, 1); glEnable(GL_TEXTURE_2D); fontRenderer.drawString(progress, 0, 0, 0x000000); glPopMatrix(); } private void setGL() { lock.lock(); try { Display.getDrawable().makeCurrent(); } catch (LWJGLException e) { e.printStackTrace(); throw new RuntimeException(e); } glClearColor((float)((backgroundColor >> 16) & 0xFF) / 0xFF, (float)((backgroundColor >> 8) & 0xFF) / 0xFF, (float)(backgroundColor & 0xFF) / 0xFF, 1); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } private void clearGL() { Minecraft mc = Minecraft.getMinecraft(); mc.displayWidth = Display.getWidth(); mc.displayHeight = Display.getHeight(); mc.resize(mc.displayWidth, mc.displayHeight); glClearColor(1, 1, 1, 1); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, .1f); try { Display.getDrawable().releaseContext(); } catch (LWJGLException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { lock.unlock(); } } }); thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { FMLLog.log(Level.ERROR, e, "Splash thread Exception"); threadError = e; } }); thread.start(); checkThreadState(); } private static void checkThreadState() { if(thread.getState() == Thread.State.TERMINATED || threadError != null) { throw new IllegalStateException("Splash thread", threadError); } } /** * Call before you need to explicitly modify GL context state during loading. * Resource loading doesn't usually require this call. * Call {@link #resume()} when you're done. * @deprecated not a stable API, will break, don't use this yet */ @Deprecated public static void pause() { if(!enabled) return; checkThreadState(); pause = true; lock.lock(); try { d.releaseContext(); Display.getDrawable().makeCurrent(); } catch (LWJGLException e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * @deprecated not a stable API, will break, don't use this yet */ @Deprecated public static void resume() { if(!enabled) return; checkThreadState(); pause = false; try { Display.getDrawable().releaseContext(); d.makeCurrent(); } catch (LWJGLException e) { e.printStackTrace(); throw new RuntimeException(e); } lock.unlock(); } public static void finish() { if(!enabled) return; checkThreadState(); try { done = true; thread.join(); d.releaseContext(); Display.getDrawable().makeCurrent(); fontTexture.delete(); logoTexture.delete(); forgeTexture.delete(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } private static IResourcePack createResourcePack(File file) { if(file.isDirectory()) { return new FolderResourcePack(file); } else { return new FileResourcePack(file); } } private static final IntBuffer buf = BufferUtils.createIntBuffer(4 * 1024 * 1024); private static class Texture { private final ResourceLocation location; private final int name; private final int width; private final int height; private final int frames; private final int size; public Texture(ResourceLocation location) { InputStream s = null; try { this.location = location; s = open(location); ImageInputStream stream = ImageIO.createImageInputStream(s); Iterator<ImageReader> readers = ImageIO.getImageReaders(stream); if(!readers.hasNext()) throw new IOException("No suitable reader found for image" + location); ImageReader reader = readers.next(); reader.setInput(stream); frames = reader.getNumImages(true); BufferedImage[] images = new BufferedImage[frames]; for(int i = 0; i < frames; i++) { images[i] = reader.read(i); } reader.dispose(); int size = 1; width = images[0].getWidth(); height = images[0].getHeight(); while((size / width) * (size / height) < frames) size *= 2; this.size = size; glEnable(GL_TEXTURE_2D); synchronized(SplashProgress.class) { name = glGenTextures(); glBindTexture(GL_TEXTURE_2D, name); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (IntBuffer)null); checkGLError("Texture creation"); for(int i = 0; i * (size / width) < frames; i++) { for(int j = 0; i * (size / width) + j < frames && j < size / width; j++) { buf.clear(); BufferedImage image = images[i * (size / width) + j]; for(int k = 0; k < height; k++) { for(int l = 0; l < width; l++) { buf.put(image.getRGB(l, k)); } } buf.position(0).limit(width * height); glTexSubImage2D(GL_TEXTURE_2D, 0, j * width, i * height, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buf); checkGLError("Texture uploading"); } } glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } catch(IOException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { IOUtils.closeQuietly(s); } } public ResourceLocation getLocation() { return location; } public int getName() { return name; } public int getWidth() { return width; } public int getHeight() { return height; } public int getFrames() { return frames; } public int getSize() { return size; } public void bind() { glBindTexture(GL_TEXTURE_2D, name); } public void delete() { glDeleteTextures(name); } public float getU(int frame, float u) { return width * (frame % (size / width) + u) / size; } public float getV(int frame, float v) { return height * (frame / (size / width) + v) / size; } public void texCoord(int frame, float u, float v) { glTexCoord2f(getU(frame, u), getV(frame, v)); } } private static class SplashFontRenderer extends FontRenderer { public SplashFontRenderer() { super(Minecraft.getMinecraft().gameSettings, fontTexture.getLocation(), null, false); super.onResourceManagerReload(null); } @Override protected void bindTexture(ResourceLocation location) { if(location != locationFontTexture) throw new IllegalArgumentException(); fontTexture.bind(); } @Override protected InputStream getResourceInputStream(ResourceLocation location) throws IOException { return Minecraft.getMinecraft().mcDefaultResourcePack.getInputStream(location); } } public static void drawVanillaScreen(TextureManager renderEngine) throws LWJGLException { if(!enabled) { Minecraft.getMinecraft().drawSplashScreen(renderEngine); } } public static void clearVanillaResources(TextureManager renderEngine, ResourceLocation mojangLogo) { if(!enabled) { renderEngine.deleteTexture(mojangLogo); } } public static void checkGLError(String where) { int err = GL11.glGetError(); if (err != 0) { throw new IllegalStateException(where + ": " + GLU.gluErrorString(err)); } } private static InputStream open(ResourceLocation loc) throws IOException { if(miscPack.resourceExists(loc)) { return miscPack.getInputStream(loc); } else if(fmlPack.resourceExists(loc)) { return fmlPack.getInputStream(loc); } return mcPack.getInputStream(loc); } }