PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/newcode/src/com/prupe/mcpatcher/hd/CustomAnimation.java

https://bitbucket.org/Freso/mcpatcher
Java | 328 lines | 303 code | 25 blank | 0 comment | 69 complexity | e7996118ca99071c23d942a291f1f231 MD5 | raw file
  1. package com.prupe.mcpatcher.hd;
  2. import com.prupe.mcpatcher.*;
  3. import net.minecraft.src.ResourceLocation;
  4. import org.lwjgl.opengl.GL11;
  5. import org.lwjgl.util.glu.GLU;
  6. import java.awt.*;
  7. import java.awt.image.BufferedImage;
  8. import java.nio.ByteBuffer;
  9. import java.util.*;
  10. import java.util.List;
  11. public class CustomAnimation implements Comparable<CustomAnimation> {
  12. private static final MCLogger logger = MCLogger.getLogger(MCPatcherUtils.CUSTOM_ANIMATIONS, "Animation");
  13. private static final boolean enable = Config.getBoolean(MCPatcherUtils.EXTENDED_HD, "animations", true);
  14. private static final Map<ResourceLocation, Properties> pending = new HashMap<ResourceLocation, Properties>();
  15. private static final List<CustomAnimation> animations = new ArrayList<CustomAnimation>();
  16. private final ResourceLocation propertiesName;
  17. private final ResourceLocation dstName;
  18. private final ResourceLocation srcName;
  19. private final int mipmapLevel;
  20. private final ByteBuffer imageData;
  21. private final int x;
  22. private final int y;
  23. private final int w;
  24. private final int h;
  25. private int currentFrame;
  26. private int currentDelay;
  27. private int numFrames;
  28. private int[] tileOrder;
  29. private int[] tileDelay;
  30. private final int numTiles;
  31. private boolean error;
  32. static {
  33. TexturePackChangeHandler.register(new TexturePackChangeHandler(MCPatcherUtils.EXTENDED_HD, 1) {
  34. @Override
  35. public void beforeChange() {
  36. if (!pending.isEmpty()) {
  37. logger.fine("%d animations were never registered:", pending.size());
  38. for (ResourceLocation resource : pending.keySet()) {
  39. logger.fine(" %s", resource);
  40. }
  41. pending.clear();
  42. }
  43. animations.clear();
  44. MipmapHelper.reset();
  45. FancyDial.refresh();
  46. }
  47. @Override
  48. public void afterChange() {
  49. if (enable) {
  50. for (ResourceLocation resource : TexturePackAPI.listResources(TexturePackAPI.MCPATCHER_SUBDIR + "anim", ".properties", true, false, false)) {
  51. Properties properties = TexturePackAPI.getProperties(resource);
  52. if (properties != null) {
  53. pending.put(resource, properties);
  54. }
  55. }
  56. }
  57. }
  58. });
  59. }
  60. public static void updateAll() {
  61. FancyDial.updateAll();
  62. if (!pending.isEmpty()) {
  63. try {
  64. checkPendingAnimations();
  65. } catch (Throwable e) {
  66. e.printStackTrace();
  67. logger.error("%d remaining animations cleared", pending.size());
  68. pending.clear();
  69. }
  70. }
  71. for (CustomAnimation animation : animations) {
  72. animation.update();
  73. }
  74. FancyDial.postUpdateAll();
  75. }
  76. private static void checkPendingAnimations() {
  77. List<ResourceLocation> done = new ArrayList<ResourceLocation>();
  78. for (Map.Entry<ResourceLocation, Properties> entry : pending.entrySet()) {
  79. ResourceLocation name = entry.getKey();
  80. Properties properties = entry.getValue();
  81. ResourceLocation textureName = TexturePackAPI.parseResourceLocation(name, MCPatcherUtils.getStringProperty(properties, "to", ""));
  82. if (TexturePackAPI.isTextureLoaded(textureName)) {
  83. addStrip(name, properties);
  84. done.add(name);
  85. }
  86. }
  87. if (!done.isEmpty()) {
  88. for (ResourceLocation name : done) {
  89. pending.remove(name);
  90. }
  91. Collections.sort(animations);
  92. }
  93. }
  94. private static void addStrip(ResourceLocation propertiesName, Properties properties) {
  95. ResourceLocation dstName = TexturePackAPI.parseResourceLocation(propertiesName, properties.getProperty("to", ""));
  96. if (dstName == null) {
  97. logger.error("%s: missing to= property");
  98. return;
  99. }
  100. ResourceLocation srcName = TexturePackAPI.parseResourceLocation(propertiesName, properties.getProperty("from", ""));
  101. if (srcName == null) {
  102. logger.error("%s: missing from= property");
  103. return;
  104. }
  105. BufferedImage srcImage = TexturePackAPI.getImage(srcName);
  106. if (srcImage == null) {
  107. logger.error("%s: image %s not found in texture pack", propertiesName, srcName);
  108. return;
  109. }
  110. int x = MCPatcherUtils.getIntProperty(properties, "x", 0);
  111. int y = MCPatcherUtils.getIntProperty(properties, "y", 0);
  112. int w = MCPatcherUtils.getIntProperty(properties, "w", 0);
  113. int h = MCPatcherUtils.getIntProperty(properties, "h", 0);
  114. if (dstName.toString().startsWith("minecraft:textures/atlas/")) {
  115. logger.error("%s: animations cannot have a target of %s", dstName);
  116. return;
  117. }
  118. if (x < 0 || y < 0 || w <= 0 || h <= 0) {
  119. logger.error("%s: %s has invalid dimensions x=%d,y=%d,w=%d,h=%d", propertiesName, srcName, x, y, w, h);
  120. return;
  121. }
  122. TexturePackAPI.bindTexture(dstName);
  123. int dstWidth = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
  124. int dstHeight = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT);
  125. int levels = MipmapHelper.getMipmapLevelsForCurrentTexture();
  126. if (x + w > dstWidth || y + h > dstHeight) {
  127. logger.error("%s: %s dimensions x=%d,y=%d,w=%d,h=%d exceed %s size %dx%d",
  128. propertiesName, srcName, x, y, w, h, dstName, dstWidth, dstHeight
  129. );
  130. return;
  131. }
  132. int width = srcImage.getWidth();
  133. int height = srcImage.getHeight();
  134. if (width != w) {
  135. srcImage = resizeImage(srcImage, w);
  136. width = srcImage.getWidth();
  137. height = srcImage.getHeight();
  138. }
  139. if (width != w || height < h) {
  140. logger.error("%s: %s dimensions %dx%d do not match %dx%d", propertiesName, srcName, width, height, w, h);
  141. return;
  142. }
  143. ByteBuffer imageData = ByteBuffer.allocateDirect(4 * width * height);
  144. int[] argb = new int[width * height];
  145. byte[] rgba = new byte[4 * width * height];
  146. srcImage.getRGB(0, 0, width, height, argb, 0, width);
  147. ARGBtoRGBA(argb, rgba);
  148. imageData.put(rgba).flip();
  149. for (int mipmapLevel = 0; mipmapLevel <= levels; mipmapLevel++) {
  150. add(new CustomAnimation(propertiesName, srcName, dstName, mipmapLevel, x, y, w, h, imageData, height / h, properties));
  151. if (((x | y | w | h) & 0x1) != 0 || w <= 0 || h <= 0) {
  152. break;
  153. }
  154. ByteBuffer newImage = ByteBuffer.allocateDirect(width * height);
  155. MipmapHelper.scaleHalf(imageData.asIntBuffer(), width, height, newImage.asIntBuffer(), 0);
  156. imageData = newImage;
  157. width >>= 1;
  158. height >>= 1;
  159. x >>= 1;
  160. y >>= 1;
  161. w >>= 1;
  162. h >>= 1;
  163. }
  164. }
  165. private static void add(CustomAnimation animation) {
  166. if (animation != null) {
  167. animations.add(animation);
  168. if (animation.mipmapLevel == 0) {
  169. logger.fine("new %s", animation);
  170. }
  171. }
  172. }
  173. private CustomAnimation(ResourceLocation propertiesName, ResourceLocation srcName, ResourceLocation dstName, int mipmapLevel, int x, int y, int w, int h, ByteBuffer imageData, int numFrames, Properties properties) {
  174. this.propertiesName = propertiesName;
  175. this.srcName = srcName;
  176. this.dstName = dstName;
  177. this.mipmapLevel = mipmapLevel;
  178. this.x = x;
  179. this.y = y;
  180. this.w = w;
  181. this.h = h;
  182. this.imageData = imageData;
  183. this.numFrames = numFrames;
  184. currentFrame = -1;
  185. numTiles = numFrames;
  186. loadProperties(properties);
  187. }
  188. void update() {
  189. if (error) {
  190. return;
  191. }
  192. int texture = TexturePackAPI.getTextureIfLoaded(dstName);
  193. if (texture < 0) {
  194. return;
  195. }
  196. if (--currentDelay > 0) {
  197. return;
  198. }
  199. if (++currentFrame >= numFrames) {
  200. currentFrame = 0;
  201. }
  202. TexturePackAPI.bindTexture(texture);
  203. update(texture, 0, 0);
  204. int glError = GL11.glGetError();
  205. if (glError != 0) {
  206. logger.severe("%s: %s", this, GLU.gluErrorString(glError));
  207. error = true;
  208. return;
  209. }
  210. currentDelay = getDelay();
  211. }
  212. public int compareTo(CustomAnimation o) {
  213. return dstName.toString().compareTo(o.dstName.toString());
  214. }
  215. @Override
  216. public String toString() {
  217. return String.format("CustomAnimation{%s %s %dx%d -> %s%s @ %d,%d (%d frames)}",
  218. propertiesName, srcName, w, h, dstName, (mipmapLevel > 0 ? "#" + mipmapLevel : ""), x, y, numFrames
  219. );
  220. }
  221. private static void ARGBtoRGBA(int[] src, byte[] dest) {
  222. for (int i = 0; i < src.length; i++) {
  223. int v = src[i];
  224. dest[(i * 4) + 3] = (byte) ((v >> 24) & 0xff);
  225. dest[(i * 4) + 0] = (byte) ((v >> 16) & 0xff);
  226. dest[(i * 4) + 1] = (byte) ((v >> 8) & 0xff);
  227. dest[(i * 4) + 2] = (byte) ((v >> 0) & 0xff);
  228. }
  229. }
  230. private static BufferedImage resizeImage(BufferedImage image, int width) {
  231. if (width == image.getWidth()) {
  232. return image;
  233. }
  234. int height = image.getHeight() * width / image.getWidth();
  235. logger.finer("resizing to %dx%d", width, height);
  236. BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  237. Graphics2D graphics2D = newImage.createGraphics();
  238. graphics2D.drawImage(image, 0, 0, width, height, null);
  239. return newImage;
  240. }
  241. private void loadProperties(Properties properties) {
  242. loadTileOrder(properties);
  243. if (tileOrder == null) {
  244. tileOrder = new int[numFrames];
  245. for (int i = 0; i < numFrames; i++) {
  246. tileOrder[i] = i % numTiles;
  247. }
  248. }
  249. tileDelay = new int[numFrames];
  250. loadTileDelay(properties);
  251. for (int i = 0; i < numFrames; i++) {
  252. tileDelay[i] = Math.max(tileDelay[i], 1);
  253. }
  254. }
  255. private void loadTileOrder(Properties properties) {
  256. if (properties == null) {
  257. return;
  258. }
  259. int i = 0;
  260. for (; getIntValue(properties, "tile.", i) != null; i++) {
  261. }
  262. if (i > 0) {
  263. numFrames = i;
  264. tileOrder = new int[numFrames];
  265. for (i = 0; i < numFrames; i++) {
  266. tileOrder[i] = Math.abs(getIntValue(properties, "tile.", i)) % numTiles;
  267. }
  268. }
  269. }
  270. private void loadTileDelay(Properties properties) {
  271. if (properties == null) {
  272. return;
  273. }
  274. Integer defaultValue = getIntValue(properties, "duration");
  275. for (int i = 0; i < numFrames; i++) {
  276. Integer value = getIntValue(properties, "duration.", i);
  277. if (value != null) {
  278. tileDelay[i] = value;
  279. } else if (defaultValue != null) {
  280. tileDelay[i] = defaultValue;
  281. }
  282. }
  283. }
  284. private static Integer getIntValue(Properties properties, String key) {
  285. try {
  286. String value = properties.getProperty(key);
  287. if (value != null && value.matches("^\\d+$")) {
  288. return Integer.parseInt(value);
  289. }
  290. } catch (NumberFormatException e) {
  291. }
  292. return null;
  293. }
  294. private static Integer getIntValue(Properties properties, String prefix, int index) {
  295. return getIntValue(properties, prefix + index);
  296. }
  297. private void update(int texture, int dx, int dy) {
  298. GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, mipmapLevel, x + dx, y + dy, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) imageData.position(4 * w * h * tileOrder[currentFrame]));
  299. }
  300. private int getDelay() {
  301. return tileDelay[currentFrame];
  302. }
  303. }