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

/newcode/src/com/prupe/mcpatcher/sky/SkyRenderer.java

https://bitbucket.org/prupe/mcpatcher
Java | 435 lines | 370 code | 54 blank | 11 comment | 64 complexity | 50bd9d03e64f9f6c452d6cbbc364d466 MD5 | raw file
  1. package com.prupe.mcpatcher.sky;
  2. import com.prupe.mcpatcher.Config;
  3. import com.prupe.mcpatcher.MCLogger;
  4. import com.prupe.mcpatcher.MCPatcherUtils;
  5. import com.prupe.mcpatcher.mal.resource.*;
  6. import com.prupe.mcpatcher.mal.tessellator.TessellatorAPI;
  7. import net.minecraft.src.Minecraft;
  8. import net.minecraft.src.ResourceLocation;
  9. import net.minecraft.src.Tessellator;
  10. import net.minecraft.src.World;
  11. import org.lwjgl.opengl.GL11;
  12. import java.util.*;
  13. public class SkyRenderer {
  14. private static final MCLogger logger = MCLogger.getLogger(MCPatcherUtils.BETTER_SKIES);
  15. private static final boolean enable = Config.getBoolean(MCPatcherUtils.BETTER_SKIES, "skybox", true);
  16. private static final boolean unloadTextures = Config.getBoolean(MCPatcherUtils.BETTER_SKIES, "unloadTextures", true);
  17. public static final double horizonHeight = Config.getInt(MCPatcherUtils.BETTER_SKIES, "horizon", 16);
  18. private static double worldTime;
  19. private static float celestialAngle;
  20. private static float rainStrength;
  21. private static final HashMap<Integer, WorldEntry> worldSkies = new HashMap<Integer, WorldEntry>();
  22. private static WorldEntry currentWorld;
  23. public static boolean active;
  24. static {
  25. TexturePackChangeHandler.register(new TexturePackChangeHandler(MCPatcherUtils.BETTER_SKIES, 2) {
  26. @Override
  27. public void beforeChange() {
  28. worldSkies.clear();
  29. }
  30. @Override
  31. public void afterChange() {
  32. if (enable) {
  33. World world = Minecraft.getInstance().theWorld;
  34. if (world != null) {
  35. getWorldEntry(world.worldProvider.getWorldType());
  36. }
  37. }
  38. FireworksHelper.reload();
  39. }
  40. });
  41. }
  42. public static void setup(World world, float partialTick, float celestialAngle) {
  43. if (TexturePackAPI.isDefaultTexturePack()) {
  44. active = false;
  45. } else {
  46. int worldType = Minecraft.getInstance().theWorld.worldProvider.getWorldType();
  47. WorldEntry newEntry = getWorldEntry(worldType);
  48. if (newEntry != currentWorld && currentWorld != null) {
  49. currentWorld.unloadTextures();
  50. }
  51. currentWorld = newEntry;
  52. active = currentWorld.active();
  53. if (active) {
  54. worldTime = world.getWorldTime() + partialTick;
  55. rainStrength = 1.0f - world.getRainStrength(partialTick);
  56. SkyRenderer.celestialAngle = celestialAngle;
  57. }
  58. }
  59. }
  60. public static void renderAll() {
  61. if (active) {
  62. currentWorld.renderAll(TessellatorAPI.getTessellator());
  63. }
  64. }
  65. public static ResourceLocation setupCelestialObject(ResourceLocation defaultTexture) {
  66. if (active) {
  67. Layer.clearBlendingMethod();
  68. Layer layer = currentWorld.getCelestialObject(defaultTexture);
  69. if (layer != null) {
  70. layer.setBlendingMethod(rainStrength);
  71. return layer.texture;
  72. }
  73. }
  74. return defaultTexture;
  75. }
  76. private static WorldEntry getWorldEntry(int worldType) {
  77. WorldEntry entry = worldSkies.get(worldType);
  78. if (entry == null) {
  79. entry = new WorldEntry(worldType);
  80. worldSkies.put(worldType, entry);
  81. }
  82. return entry;
  83. }
  84. private static class WorldEntry {
  85. private final int worldType;
  86. private final List<Layer> skies = new ArrayList<Layer>();
  87. private final Map<ResourceLocation, Layer> objects = new HashMap<ResourceLocation, Layer>();
  88. private final Set<ResourceLocation> textures = new HashSet<ResourceLocation>();
  89. WorldEntry(int worldType) {
  90. this.worldType = worldType;
  91. loadSkies();
  92. loadCelestialObject("sun");
  93. loadCelestialObject("moon_phases");
  94. }
  95. private void loadSkies() {
  96. for (int i = -1; ; i++) {
  97. String v1Path = "/environment/sky" + worldType + "/sky" + (i < 0 ? "" : String.valueOf(i)) + ".properties";
  98. String v2Path = "sky/world" + worldType + "/sky" + (i < 0 ? "" : String.valueOf(i)) + ".properties";
  99. ResourceLocation resource = TexturePackAPI.newMCPatcherResourceLocation(v1Path, v2Path);
  100. Layer layer = Layer.create(resource);
  101. if (layer == null) {
  102. if (i > 0) {
  103. break;
  104. }
  105. } else if (layer.properties.valid()) {
  106. logger.fine("loaded %s", resource);
  107. skies.add(layer);
  108. textures.add(layer.texture);
  109. }
  110. }
  111. }
  112. private void loadCelestialObject(String objName) {
  113. ResourceLocation textureName = new ResourceLocation(TexturePackAPI.select("", "textures") + "/environment/" + objName + ".png");
  114. String v1Path = "/environment/sky" + worldType + "/" + objName + ".properties";
  115. String v2Path = "sky/world" + worldType + "/" + objName + ".properties";
  116. ResourceLocation resource = TexturePackAPI.newMCPatcherResourceLocation(v1Path, v2Path);
  117. PropertiesFile properties = PropertiesFile.get(logger, resource);
  118. if (properties != null) {
  119. properties.setProperty("fade", "false");
  120. properties.setProperty("rotate", "true");
  121. Layer layer = new Layer(properties);
  122. if (properties.valid()) {
  123. logger.fine("using %s (%s) for the %s", resource, layer.texture, objName);
  124. objects.put(textureName, layer);
  125. }
  126. }
  127. }
  128. boolean active() {
  129. return !skies.isEmpty() || !objects.isEmpty();
  130. }
  131. void renderAll(Tessellator tessellator) {
  132. if (unloadTextures) {
  133. Set<ResourceLocation> texturesNeeded = new HashSet<ResourceLocation>();
  134. for (Layer layer : skies) {
  135. if (layer.prepare()) {
  136. texturesNeeded.add(layer.texture);
  137. }
  138. }
  139. Set<ResourceLocation> texturesToUnload = new HashSet<ResourceLocation>();
  140. texturesToUnload.addAll(textures);
  141. texturesToUnload.removeAll(texturesNeeded);
  142. for (ResourceLocation resource : texturesToUnload) {
  143. TexturePackAPI.unloadTexture(resource);
  144. }
  145. }
  146. for (Layer layer : skies) {
  147. if (!unloadTextures) {
  148. layer.prepare();
  149. }
  150. if (layer.brightness > 0.0f) {
  151. layer.render(tessellator);
  152. Layer.clearBlendingMethod();
  153. }
  154. }
  155. }
  156. Layer getCelestialObject(ResourceLocation defaultTexture) {
  157. return objects.get(defaultTexture);
  158. }
  159. void unloadTextures() {
  160. for (Layer layer : skies) {
  161. TexturePackAPI.unloadTexture(layer.texture);
  162. }
  163. }
  164. }
  165. private static class Layer {
  166. private static final int SECS_PER_DAY = 24 * 60 * 60;
  167. private static final int TICKS_PER_DAY = 24000;
  168. private static final double TOD_OFFSET = -0.25;
  169. private static final double SKY_DISTANCE = 100.0;
  170. private final PropertiesFile properties;
  171. private ResourceLocation texture;
  172. private boolean fade;
  173. private boolean rotate;
  174. private float[] axis;
  175. private float speed;
  176. private BlendMethod blendMethod;
  177. private double a;
  178. private double b;
  179. private double c;
  180. float brightness;
  181. static Layer create(ResourceLocation resource) {
  182. PropertiesFile properties = PropertiesFile.get(logger, resource);
  183. if (properties == null) {
  184. return null;
  185. } else {
  186. return new Layer(properties);
  187. }
  188. }
  189. Layer(PropertiesFile properties) {
  190. this.properties = properties;
  191. boolean valid = (readTexture() && readRotation() & readBlendingMethod() && readFadeTimers());
  192. }
  193. private boolean readTexture() {
  194. texture = properties.getResourceLocation("source", properties.toString().replaceFirst("\\.properties$", ".png"));
  195. if (TexturePackAPI.hasResource(texture)) {
  196. return true;
  197. } else {
  198. return properties.error("source texture %s not found", texture);
  199. }
  200. }
  201. private boolean readRotation() {
  202. rotate = properties.getBoolean("rotate", true);
  203. if (rotate) {
  204. speed = properties.getFloat("speed", 1.0f);
  205. String value = properties.getString("axis", "0.0 0.0 1.0");
  206. String[] tokens = value.split("\\s+");
  207. if (tokens.length == 3) {
  208. float x;
  209. float y;
  210. float z;
  211. try {
  212. x = Float.parseFloat(tokens[0]);
  213. y = Float.parseFloat(tokens[1]);
  214. z = Float.parseFloat(tokens[2]);
  215. } catch (NumberFormatException e) {
  216. return properties.error("invalid rotation axis");
  217. }
  218. if (x * x + y * y + z * z == 0.0f) {
  219. return properties.error("rotation axis cannot be 0");
  220. }
  221. axis = new float[]{z, y, -x};
  222. } else {
  223. return properties.error("invalid rotate value %s", value);
  224. }
  225. }
  226. return true;
  227. }
  228. private boolean readBlendingMethod() {
  229. String value = properties.getString("blend", "add");
  230. blendMethod = BlendMethod.parse(value);
  231. if (blendMethod == null) {
  232. return properties.error("unknown blend method %s", value);
  233. }
  234. return true;
  235. }
  236. private boolean readFadeTimers() {
  237. fade = properties.getBoolean("fade", true);
  238. if (!fade) {
  239. return true;
  240. }
  241. int startFadeIn = parseTime(properties, "startFadeIn");
  242. int endFadeIn = parseTime(properties, "endFadeIn");
  243. int endFadeOut = parseTime(properties, "endFadeOut");
  244. if (!properties.valid()) {
  245. return false;
  246. }
  247. while (endFadeIn <= startFadeIn) {
  248. endFadeIn += SECS_PER_DAY;
  249. }
  250. while (endFadeOut <= endFadeIn) {
  251. endFadeOut += SECS_PER_DAY;
  252. }
  253. if (endFadeOut - startFadeIn >= SECS_PER_DAY) {
  254. return properties.error("fade times must fall within a 24 hour period");
  255. }
  256. int startFadeOut = startFadeIn + endFadeOut - endFadeIn;
  257. // f(x) = a cos x + b sin x + c
  258. // f(s0) = 0
  259. // f(s1) = 1
  260. // f(e1) = 0
  261. // Solve for a, b, c using Cramer's rule.
  262. double s0 = normalize(startFadeIn, SECS_PER_DAY, TOD_OFFSET);
  263. double s1 = normalize(endFadeIn, SECS_PER_DAY, TOD_OFFSET);
  264. double e0 = normalize(startFadeOut, SECS_PER_DAY, TOD_OFFSET);
  265. double e1 = normalize(endFadeOut, SECS_PER_DAY, TOD_OFFSET);
  266. double det = Math.cos(s0) * Math.sin(s1) + Math.cos(e1) * Math.sin(s0) + Math.cos(s1) * Math.sin(e1) -
  267. Math.cos(s0) * Math.sin(e1) - Math.cos(s1) * Math.sin(s0) - Math.cos(e1) * Math.sin(s1);
  268. if (det == 0.0) {
  269. return properties.error("determinant is 0");
  270. }
  271. a = (Math.sin(e1) - Math.sin(s0)) / det;
  272. b = (Math.cos(s0) - Math.cos(e1)) / det;
  273. c = (Math.cos(e1) * Math.sin(s0) - Math.cos(s0) * Math.sin(e1)) / det;
  274. logger.finer("%s: y = %f cos x + %f sin x + %f", properties, a, b, c);
  275. logger.finer(" at %f: %f", s0, f(s0));
  276. logger.finer(" at %f: %f", s1, f(s1));
  277. logger.finer(" at %f: %f", e0, f(e0));
  278. logger.finer(" at %f: %f", e1, f(e1));
  279. return true;
  280. }
  281. private int parseTime(PropertiesFile properties, String key) {
  282. String s = properties.getString(key, "");
  283. if ("".equals(s)) {
  284. properties.error("missing value for %s", key);
  285. return -1;
  286. }
  287. String[] t = s.split(":");
  288. if (t.length >= 2) {
  289. try {
  290. int hh = Integer.parseInt(t[0].trim());
  291. int mm = Integer.parseInt(t[1].trim());
  292. int ss;
  293. if (t.length >= 3) {
  294. ss = Integer.parseInt(t[2].trim());
  295. } else {
  296. ss = 0;
  297. }
  298. return (60 * 60 * hh + 60 * mm + ss) % SECS_PER_DAY;
  299. } catch (NumberFormatException e) {
  300. }
  301. }
  302. properties.error("invalid %s time %s", key, s);
  303. return -1;
  304. }
  305. private static double normalize(double time, int period, double offset) {
  306. return 2.0 * Math.PI * (time / period + offset);
  307. }
  308. private double f(double x) {
  309. return a * Math.cos(x) + b * Math.sin(x) + c;
  310. }
  311. boolean prepare() {
  312. brightness = rainStrength;
  313. if (fade) {
  314. double x = normalize(worldTime, TICKS_PER_DAY, 0.0);
  315. brightness *= (float) f(x);
  316. }
  317. if (brightness <= 0.0f) {
  318. return false;
  319. }
  320. if (brightness > 1.0f) {
  321. brightness = 1.0f;
  322. }
  323. return true;
  324. }
  325. boolean render(Tessellator tessellator) {
  326. TexturePackAPI.bindTexture(texture);
  327. setBlendingMethod(brightness);
  328. GL11.glPushMatrix();
  329. if (rotate) {
  330. GL11.glRotatef(celestialAngle * 360.0f * speed, axis[0], axis[1], axis[2]);
  331. }
  332. // north
  333. GL11.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
  334. GL11.glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
  335. drawTile(tessellator, 4);
  336. // top
  337. GL11.glPushMatrix();
  338. GL11.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
  339. drawTile(tessellator, 1);
  340. GL11.glPopMatrix();
  341. // bottom
  342. GL11.glPushMatrix();
  343. GL11.glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
  344. drawTile(tessellator, 0);
  345. GL11.glPopMatrix();
  346. // west
  347. GL11.glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
  348. drawTile(tessellator, 5);
  349. // south
  350. GL11.glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
  351. drawTile(tessellator, 2);
  352. // east
  353. GL11.glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
  354. drawTile(tessellator, 3);
  355. GL11.glPopMatrix();
  356. return true;
  357. }
  358. private static void drawTile(Tessellator tessellator, int tile) {
  359. double tileX = (tile % 3) / 3.0;
  360. double tileY = (tile / 3) / 2.0;
  361. TessellatorAPI.startDrawingQuads(tessellator);
  362. TessellatorAPI.addVertexWithUV(tessellator, -SKY_DISTANCE, -SKY_DISTANCE, -SKY_DISTANCE, tileX, tileY);
  363. TessellatorAPI.addVertexWithUV(tessellator, -SKY_DISTANCE, -SKY_DISTANCE, SKY_DISTANCE, tileX, tileY + 0.5);
  364. TessellatorAPI.addVertexWithUV(tessellator, SKY_DISTANCE, -SKY_DISTANCE, SKY_DISTANCE, tileX + 1.0 / 3.0, tileY + 0.5);
  365. TessellatorAPI.addVertexWithUV(tessellator, SKY_DISTANCE, -SKY_DISTANCE, -SKY_DISTANCE, tileX + 1.0 / 3.0, tileY);
  366. TessellatorAPI.draw(tessellator);
  367. }
  368. void setBlendingMethod(float brightness) {
  369. blendMethod.applyFade(brightness);
  370. blendMethod.applyAlphaTest();
  371. blendMethod.applyBlending();
  372. GL11.glEnable(GL11.GL_TEXTURE_2D);
  373. }
  374. static void clearBlendingMethod() {
  375. GL11.glDisable(GL11.GL_ALPHA_TEST);
  376. GL11.glEnable(GL11.GL_BLEND);
  377. GLAPI.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
  378. GLAPI.glColor4f(1.0f, 1.0f, 1.0f, rainStrength);
  379. }
  380. }
  381. }