package com.prupe.mcpatcher.hd;
import com.prupe.mcpatcher.BlendMethod;
import com.prupe.mcpatcher.Config;
import com.prupe.mcpatcher.InputHandler;
import com.prupe.mcpatcher.MCLogger;
import com.prupe.mcpatcher.MCPatcherUtils;
import com.prupe.mcpatcher.TexturePackAPI;
import com.prupe.mcpatcher.hd.FancyDial$Layer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.WeakHashMap;
import javax.imageio.ImageIO;
import net.minecraft.src.Icon;
import net.minecraft.src.ResourceLocation;
import net.minecraft.src.TextureAtlasSprite;
import net.minecraft.src.TextureClock;
import net.minecraft.src.TextureCompass;
import net.minecraft.src.TextureMap;
import net.minecraft.src.TextureObject;
import org.lwjgl.opengl.EXTFramebufferObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.glu.GLU;
public class FancyDial {
private static final MCLogger logger = MCLogger.getLogger("Custom Animations", "Animation");
private static final ResourceLocation ITEMS_PNG = new ResourceLocation("textures/atlas/items.png");
private static final double ANGLE_UNSET = Double.MAX_VALUE;
private static final int NUM_SCRATCH_TEXTURES = 3;
private static final boolean fboSupported = GLContext.getCapabilities().GL_EXT_framebuffer_object;
private static final boolean gl13Supported = GLContext.getCapabilities().OpenGL13;
private static final boolean enableCompass = Config.getBoolean("Extended HD", "fancyCompass", true);
private static final boolean enableClock = Config.getBoolean("Extended HD", "fancyClock", true);
private static final boolean useGL13 = gl13Supported && Config.getBoolean("Extended HD", "useGL13", true);
private static final boolean useScratchTexture = Config.getBoolean("Extended HD", "useScratchTexture", true);
private static final int glAttributes;
private static boolean initialized;
private static final int drawList = GL11.glGenLists(1);
private static final Map<TextureAtlasSprite, ResourceLocation> setupInfo = new WeakHashMap();
private static final Map<TextureAtlasSprite, FancyDial> instances = new WeakHashMap();
private final TextureAtlasSprite icon;
private final ResourceLocation resource;
private final String name;
private final int x0;
private final int y0;
private final int width;
private final int height;
private final int itemsTexture;
private final int[] scratchTexture = new int[4];
private final ByteBuffer scratchBuffer;
private final int[] frameBuffer = new int[4];
private int scratchIndex;
private Map<Double, ByteBuffer> itemFrames = new TreeMap();
private int outputFrames;
private boolean ok;
private double lastAngle = Double.MAX_VALUE;
private boolean lastItemFrameRenderer;
private final List<FancyDial$Layer> layers = new ArrayList();
private InputHandler keyboard;
private static final float STEP = 0.01F;
private float scaleXDelta;
private float scaleYDelta;
private float offsetXDelta;
private float offsetYDelta;
public static void setup(TextureAtlasSprite icon) {
if (fboSupported) {
String name = icon.getIconName();
if ("compass".equals(icon.getIconName())) {
if (!enableCompass) {
return;
}
} else {
if (!"clock".equals(icon.getIconName())) {
logger.warning("ignoring custom animation for %s not compass or clock", new Object[] {icon.getIconName()});
return;
}
if (!enableClock) {
return;
}
}
ResourceLocation resource = TexturePackAPI.newMCPatcherResourceLocation("dial/" + name + ".properties");
if (TexturePackAPI.hasResource(resource)) {
logger.fine("found custom %s (%s)", new Object[] {name, resource});
setupInfo.put(icon, resource);
}
}
}
public static boolean update(TextureAtlasSprite icon, boolean itemFrameRenderer) {
if (!initialized) {
logger.finer("deferring %s update until initialization finishes", new Object[] {icon.getIconName()});
return false;
} else {
FancyDial instance = (FancyDial)instances.get(icon);
if (instance == null) {
instance = getInstance(icon);
if (instance == null) {
return false;
}
}
return instance.render(itemFrameRenderer);
}
}
static void clearAll() {
logger.finer("FancyDial.clearAll", new Object[0]);
Iterator i$ = instances.values().iterator();
while (i$.hasNext()) {
FancyDial instance = (FancyDial)i$.next();
if (instance != null) {
instance.finish();
}
}
instances.clear();
initialized = true;
}
static void registerAnimations() {
TextureObject texture = TexturePackAPI.getTextureObject(ITEMS_PNG);
if (texture instanceof TextureMap) {
List animations = ((TextureMap)texture).listAnimatedSprites;
Iterator i$ = instances.values().iterator();
while (i$.hasNext()) {
FancyDial instance = (FancyDial)i$.next();
instance.registerAnimation(animations);
}
}
}
void registerAnimation(List<TextureAtlasSprite> animations) {
if (!animations.contains(this.icon)) {
animations.add(this.icon);
if (this.icon.framesTextureData == null) {
this.icon.framesTextureData = new ArrayList();
}
if (this.icon.framesTextureData.isEmpty()) {
int[] dummyRGB = new int[this.width * this.height];
Arrays.fill(dummyRGB, -65281);
this.icon.framesTextureData.add(dummyRGB);
}
logger.fine("registered %s animation", new Object[] {this.name});
}
}
private static FancyDial getInstance(TextureAtlasSprite icon) {
ResourceLocation resource = (ResourceLocation)setupInfo.get(icon);
Properties properties = TexturePackAPI.getProperties(resource);
setupInfo.remove(icon);
if (properties == null) {
return null;
} else {
try {
FancyDial e = new FancyDial(icon, resource, properties);
if (e.ok) {
instances.put(icon, e);
return e;
}
e.finish();
} catch (Throwable var4) {
var4.printStackTrace();
}
return null;
}
}
private FancyDial(TextureAtlasSprite icon, ResourceLocation resource, Properties properties) {
this.icon = icon;
this.resource = resource;
this.name = icon.getIconName();
this.x0 = icon.getOriginX();
this.y0 = icon.getOriginY();
this.width = icon.getIconWidth();
this.height = icon.getIconHeight();
this.scratchBuffer = ByteBuffer.allocateDirect(4 * this.width * this.height);
this.itemsTexture = TexturePackAPI.getTextureIfLoaded(ITEMS_PNG);
if (this.itemsTexture < 0) {
logger.severe("could not get items texture", new Object[0]);
} else {
if (useScratchTexture) {
logger.fine("rendering %s to %dx%d scratch texture", new Object[] {this.name, Integer.valueOf(this.width), Integer.valueOf(this.height)});
} else {
logger.fine("rendering %s directly to %s", new Object[] {this.name, ITEMS_PNG});
}
for (int debug = 0; debug < this.scratchTexture.length; ++debug) {
this.scratchTexture[debug] = debug == 3 ? this.itemsTexture : this.setupScratchTexture(debug);
this.frameBuffer[debug] = this.setupFrameBuffer(this.scratchTexture[debug]);
}
boolean var7 = false;
int glError = 0;
while (true) {
FancyDial$Layer layer = this.newLayer(resource, properties, "." + glError);
if (layer == null) {
if (glError > 0) {
this.keyboard = new InputHandler(this.name, var7);
if (this.layers.size() < 2) {
logger.error("custom %s needs at least two layers defined", new Object[] {this.name});
return;
}
this.outputFrames = MCPatcherUtils.getIntProperty(properties, "outputFrames", 0);
glError = GL11.glGetError();
if (glError != 0) {
logger.severe("%s during %s setup", new Object[] {GLU.gluErrorString(glError), this.name});
return;
}
this.ok = true;
return;
}
} else {
this.layers.add(layer);
var7 |= layer.debug;
logger.fine(" new %s", new Object[] {layer});
}
++glError;
}
}
}
private int setupScratchTexture(int i) {
int targetTexture;
if (useScratchTexture) {
targetTexture = GL11.glGenTextures();
MipmapHelper.setupTexture(targetTexture, this.width, this.height, TexturePackAPI.transformResourceLocation(this.resource, ".properties", "_scratch" + i).getResourcePath());
} else {
targetTexture = -1;
}
return targetTexture;
}
private int setupFrameBuffer(int texture) {
if (texture < 0) {
return -1;
} else {
int frameBuffer = EXTFramebufferObject.glGenFramebuffersEXT();
if (frameBuffer < 0) {
logger.severe("could not get framebuffer object", new Object[0]);
this.ok = false;
return -1;
} else {
EXTFramebufferObject.glBindFramebufferEXT(36160, frameBuffer);
EXTFramebufferObject.glFramebufferTexture2DEXT(36160, 36064, 3553, texture, 0);
EXTFramebufferObject.glBindFramebufferEXT(36160, 0);
return frameBuffer;
}
}
}
private boolean render(boolean itemFrameRenderer) {
if (!this.ok) {
return false;
} else {
if (!itemFrameRenderer) {
boolean angle = true;
if (!this.keyboard.isEnabled()) {
angle = false;
} else if (this.keyboard.isKeyPressed(80)) {
this.scaleYDelta -= 0.01F;
} else if (this.keyboard.isKeyPressed(72)) {
this.scaleYDelta += 0.01F;
} else if (this.keyboard.isKeyPressed(75)) {
this.scaleXDelta -= 0.01F;
} else if (this.keyboard.isKeyPressed(77)) {
this.scaleXDelta += 0.01F;
} else if (this.keyboard.isKeyPressed(208)) {
this.offsetYDelta += 0.01F;
} else if (this.keyboard.isKeyPressed(200)) {
this.offsetYDelta -= 0.01F;
} else if (this.keyboard.isKeyPressed(203)) {
this.offsetXDelta -= 0.01F;
} else if (this.keyboard.isKeyPressed(205)) {
this.offsetXDelta += 0.01F;
} else if (this.keyboard.isKeyPressed(55)) {
this.scaleXDelta = this.scaleYDelta = this.offsetXDelta = this.offsetYDelta = 0.0F;
} else {
angle = false;
}
if (angle) {
logger.info("", new Object[0]);
logger.info("scaleX %+f", new Object[] {Float.valueOf(this.scaleXDelta)});
logger.info("scaleY %+f", new Object[] {Float.valueOf(this.scaleYDelta)});
logger.info("offsetX %+f", new Object[] {Float.valueOf(this.offsetXDelta)});
logger.info("offsetY %+f", new Object[] {Float.valueOf(this.offsetYDelta)});
this.lastAngle = Double.MAX_VALUE;
}
if (this.outputFrames > 0) {
this.writeCustomImage();
this.outputFrames = 0;
}
}
double var5 = getAngle(this.icon);
int var6;
if (!useScratchTexture) {
if (var5 != this.lastAngle) {
this.renderToItems(var5);
this.lastAngle = var5;
}
} else if (itemFrameRenderer) {
ByteBuffer glError = (ByteBuffer)this.itemFrames.get(Double.valueOf(var5));
if (glError == null) {
logger.fine("rendering %s at angle %f for item frame", new Object[] {this.name, Double.valueOf(var5)});
glError = ByteBuffer.allocateDirect(this.width * this.height * 4);
this.renderToItems(var5);
this.readTextureToBuffer(glError);
this.itemFrames.put(Double.valueOf(var5), glError);
} else {
this.copyBufferToItemsTexture(glError);
}
this.lastItemFrameRenderer = true;
} else if (this.lastAngle == Double.MAX_VALUE) {
for (var6 = 0; var6 < 3; ++var6) {
this.renderToFB(var5, this.frameBuffer[var6]);
}
this.readTextureToBuffer(this.scratchTexture[0], this.scratchBuffer);
this.copyBufferToItemsTexture(this.scratchBuffer);
this.lastAngle = var5;
this.scratchIndex = 0;
} else if (this.lastItemFrameRenderer || var5 != this.lastAngle) {
var6 = (this.scratchIndex + 1) % 3;
if (var5 != this.lastAngle) {
this.renderToFB(var5, this.frameBuffer[var6]);
this.readTextureToBuffer(this.scratchTexture[this.scratchIndex], this.scratchBuffer);
}
this.copyBufferToItemsTexture(this.scratchBuffer);
this.lastAngle = var5;
this.scratchIndex = var6;
this.lastItemFrameRenderer = false;
}
var6 = GL11.glGetError();
if (var6 != 0) {
logger.severe("%s during %s update", new Object[] {GLU.gluErrorString(var6), this.name});
this.ok = false;
}
return this.ok;
}
}
private void writeCustomImage() {
try {
BufferedImage e = new BufferedImage(this.width, this.outputFrames * this.height, 2);
IntBuffer intBuffer = this.scratchBuffer.asIntBuffer();
int[] argb = new int[this.width * this.height];
File path = MCPatcherUtils.getGamePath(new String[] {"custom_" + this.name + ".png"});
logger.info("generating %d %s frames", new Object[] {Integer.valueOf(this.outputFrames), this.name});
for (int i = 0; i < this.outputFrames; ++i) {
this.renderToItems((double)i * (360.0D / (double)this.outputFrames));
this.readTextureToBuffer(this.scratchBuffer);
intBuffer.position(0);
for (int j = 0; j < argb.length; ++j) {
argb[j] = Integer.rotateRight(intBuffer.get(j), 8);
}
e.setRGB(0, i * this.height, this.width, this.height, argb, 0, this.width);
}
ImageIO.write(e, "png", path);
logger.info("wrote %dx%d %s", new Object[] {Integer.valueOf(e.getWidth()), Integer.valueOf(e.getHeight()), path.getPath()});
} catch (Throwable var7) {
var7.printStackTrace();
}
}
private void renderToItems(double angle) {
GL11.glPushAttrib(glAttributes);
GL11.glViewport(this.x0, this.y0, this.width, this.height);
GL11.glEnable(GL11.GL_SCISSOR_TEST);
GL11.glScissor(this.x0, this.y0, this.width, this.height);
EXTFramebufferObject.glBindFramebufferEXT(36160, this.frameBuffer[3]);
this.renderImpl(angle);
}
private void renderToFB(double angle, int bindFB) {
GL11.glPushAttrib(glAttributes);
GL11.glViewport(0, 0, this.width, this.height);
EXTFramebufferObject.glBindFramebufferEXT(36160, bindFB);
this.renderImpl(angle);
}
private void renderImpl(double angle) {
boolean lightmapEnabled = false;
if (gl13Supported) {
GL13.glActiveTexture(GL13.GL_TEXTURE1);
lightmapEnabled = GL11.glIsEnabled(GL11.GL_TEXTURE_2D);
if (lightmapEnabled) {
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
GL13.glActiveTexture(GL13.GL_TEXTURE0);
}
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_LIGHTING);
if (useGL13) {
GL11.glDisable(GL13.GL_MULTISAMPLE);
}
GL11.glClearColor(0.0F, 0.0F, 0.0F, 0.0F);
GL11.glClear(16384);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();
GL11.glLoadIdentity();
GL11.glOrtho(-1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPushMatrix();
GL11.glLoadIdentity();
Iterator i$ = this.layers.iterator();
while (i$.hasNext()) {
FancyDial$Layer layer = (FancyDial$Layer)i$.next();
layer.blendMethod.applyBlending();
GL11.glPushMatrix();
TexturePackAPI.bindTexture(layer.textureName);
float offsetX = layer.offsetX;
float offsetY = layer.offsetY;
float scaleX = layer.scaleX;
float scaleY = layer.scaleY;
if (layer.debug) {
offsetX += this.offsetXDelta;
offsetY += this.offsetYDelta;
scaleX += this.scaleXDelta;
scaleY += this.scaleYDelta;
}
GL11.glTranslatef(offsetX, offsetY, 0.0F);
GL11.glScalef(scaleX, scaleY, 1.0F);
float layerAngle = (float)(angle * (double)layer.rotationMultiplier + (double)layer.rotationOffset);
GL11.glRotatef(layerAngle, 0.0F, 0.0F, 1.0F);
GL11.glCallList(drawList);
GL11.glPopMatrix();
}
EXTFramebufferObject.glBindFramebufferEXT(36160, 0);
GL11.glPopAttrib();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPopMatrix();
if (lightmapEnabled) {
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
}
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
}
private void readTextureToBuffer(int texture, ByteBuffer buffer) {
TexturePackAPI.bindTexture(texture);
buffer.position(0);
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
}
private void readTextureToBuffer(ByteBuffer buffer) {
EXTFramebufferObject.glBindFramebufferEXT(36160, this.frameBuffer[3]);
buffer.position(0);
GL11.glReadPixels(this.x0, this.y0, this.width, this.height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
EXTFramebufferObject.glBindFramebufferEXT(36160, 0);
}
private void copyBufferToItemsTexture(ByteBuffer buffer) {
TexturePackAPI.bindTexture(this.itemsTexture);
buffer.position(0);
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, this.x0, this.y0, this.width, this.height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
}
private static void drawBox() {
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(0.0F, 0.0F);
GL11.glVertex3f(-1.0F, -1.0F, 0.0F);
GL11.glTexCoord2f(1.0F, 0.0F);
GL11.glVertex3f(1.0F, -1.0F, 0.0F);
GL11.glTexCoord2f(1.0F, 1.0F);
GL11.glVertex3f(1.0F, 1.0F, 0.0F);
GL11.glTexCoord2f(0.0F, 1.0F);
GL11.glVertex3f(-1.0F, 1.0F, 0.0F);
GL11.glEnd();
}
private void finish() {
for (int i = 0; i < this.frameBuffer.length; ++i) {
if (this.frameBuffer[i] >= 0) {
EXTFramebufferObject.glDeleteFramebuffersEXT(this.frameBuffer[i]);
this.frameBuffer[i] = -1;
}
if (i < 3 && this.scratchTexture[i] >= 0) {
TexturePackAPI.deleteTexture(this.scratchTexture[i]);
this.scratchTexture[i] = -1;
}
}
this.itemFrames.clear();
this.layers.clear();
this.ok = false;
}
public String toString() {
return String.format("FancyDial{%s, %dx%d @ %d,%d}", new Object[] {this.name, Integer.valueOf(this.width), Integer.valueOf(this.height), Integer.valueOf(this.x0), Integer.valueOf(this.y0)});
}
protected void finalize() throws Throwable {
this.finish();
super.finalize();
}
private static double getAngle(Icon icon) {
return icon instanceof TextureCompass ? ((TextureCompass)icon).currentAngle * 180.0D / Math.PI : (icon instanceof TextureClock ? ((TextureClock)icon).field_94239_h * 360.0D : 0.0D);
}
FancyDial$Layer newLayer(ResourceLocation resource, Properties properties, String suffix) {
String textureName = MCPatcherUtils.getStringProperty(properties, "source" + suffix, "");
if (textureName.isEmpty()) {
return null;
} else {
ResourceLocation textureResource = TexturePackAPI.parseResourceLocation(resource, textureName);
if (textureResource == null) {
return null;
} else if (!TexturePackAPI.hasResource(textureResource)) {
logger.error("%s: could not read %s", new Object[] {resource, textureResource});
return null;
} else {
float scaleX = MCPatcherUtils.getFloatProperty(properties, "scaleX" + suffix, 1.0F);
float scaleY = MCPatcherUtils.getFloatProperty(properties, "scaleY" + suffix, 1.0F);
float offsetX = MCPatcherUtils.getFloatProperty(properties, "offsetX" + suffix, 0.0F);
float offsetY = MCPatcherUtils.getFloatProperty(properties, "offsetY" + suffix, 0.0F);
float angleMultiplier = MCPatcherUtils.getFloatProperty(properties, "rotationSpeed" + suffix, 0.0F);
float angleOffset = MCPatcherUtils.getFloatProperty(properties, "rotationOffset" + suffix, 0.0F);
String blend = MCPatcherUtils.getStringProperty(properties, "blend" + suffix, "alpha");
BlendMethod blendMethod = BlendMethod.parse(blend);
if (blendMethod == null) {
logger.error("%s: unknown blend method %s", new Object[] {resource, blend});
return null;
} else {
boolean debug = MCPatcherUtils.getBooleanProperty(properties, "debug" + suffix, false);
return new FancyDial$Layer(this, textureResource, scaleX, scaleY, offsetX, offsetY, angleMultiplier, angleOffset, blendMethod, debug);
}
}
}
}
static {
logger.config("fbo: supported=%s", new Object[] {Boolean.valueOf(fboSupported)});
logger.config("GL13: supported=%s, enabled=%s", new Object[] {Boolean.valueOf(gl13Supported), Boolean.valueOf(useGL13)});
int bits = 527702;
if (useGL13) {
bits |= 536870912;
}
glAttributes = bits;
GL11.glNewList(drawList, GL11.GL_COMPILE);
drawBox();
GL11.glEndList();
}
}