PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/SevenBits/mcpatcher
Java | 437 lines | 370 code | 56 blank | 11 comment | 62 complexity | 01149a3b81be97d2e43c7b31d85eb219 MD5 | raw file
  1. package com.prupe.mcpatcher.sky;
  2. import com.prupe.mcpatcher.*;
  3. import net.minecraft.client.Minecraft;
  4. import net.minecraft.src.ResourceLocation;
  5. import net.minecraft.src.Tessellator;
  6. import net.minecraft.src.World;
  7. import org.lwjgl.opengl.GL11;
  8. import java.util.*;
  9. public class SkyRenderer {
  10. private static final MCLogger logger = MCLogger.getLogger(MCPatcherUtils.BETTER_SKIES);
  11. private static final boolean enable = Config.getBoolean(MCPatcherUtils.BETTER_SKIES, "skybox", true);
  12. public static final double horizonHeight = Config.getInt(MCPatcherUtils.BETTER_SKIES, "horizon", 16);
  13. private static double worldTime;
  14. private static float celestialAngle;
  15. private static float rainStrength;
  16. private static final HashMap<Integer, WorldEntry> worldSkies = new HashMap<Integer, WorldEntry>();
  17. private static WorldEntry currentWorld;
  18. public static boolean active;
  19. static {
  20. TexturePackChangeHandler.register(new TexturePackChangeHandler(MCPatcherUtils.BETTER_SKIES, 2) {
  21. @Override
  22. public void beforeChange() {
  23. worldSkies.clear();
  24. }
  25. @Override
  26. public void afterChange() {
  27. if (enable) {
  28. World world = Minecraft.getInstance().theWorld;
  29. if (world != null) {
  30. getWorldEntry(world.worldProvider.getWorldType());
  31. }
  32. }
  33. FireworksHelper.reload();
  34. }
  35. });
  36. }
  37. public static void setup(World world, float partialTick, float celestialAngle) {
  38. if (TexturePackAPI.isDefaultTexturePack()) {
  39. active = false;
  40. } else {
  41. int worldType = Minecraft.getInstance().theWorld.worldProvider.getWorldType();
  42. WorldEntry newEntry = getWorldEntry(worldType);
  43. if (newEntry != currentWorld && currentWorld != null) {
  44. currentWorld.unloadTextures();
  45. }
  46. currentWorld = newEntry;
  47. active = currentWorld.active();
  48. if (active) {
  49. worldTime = world.getWorldTime() + partialTick;
  50. rainStrength = 1.0f - world.getRainStrength(partialTick);
  51. SkyRenderer.celestialAngle = celestialAngle;
  52. }
  53. }
  54. }
  55. public static void renderAll() {
  56. if (active) {
  57. currentWorld.renderAll(Tessellator.instance);
  58. }
  59. }
  60. public static ResourceLocation setupCelestialObject(ResourceLocation defaultTexture) {
  61. if (active) {
  62. Layer.clearBlendingMethod();
  63. Layer layer = currentWorld.getCelestialObject(defaultTexture);
  64. if (layer != null) {
  65. layer.setBlendingMethod(rainStrength);
  66. return layer.texture;
  67. }
  68. }
  69. return defaultTexture;
  70. }
  71. private static WorldEntry getWorldEntry(int worldType) {
  72. WorldEntry entry = worldSkies.get(worldType);
  73. if (entry == null) {
  74. entry = new WorldEntry(worldType);
  75. worldSkies.put(worldType, entry);
  76. }
  77. return entry;
  78. }
  79. private static class WorldEntry {
  80. private final int worldType;
  81. private final List<Layer> skies = new ArrayList<Layer>();
  82. private final Map<ResourceLocation, Layer> objects = new HashMap<ResourceLocation, Layer>();
  83. private final Set<ResourceLocation> textures = new HashSet<ResourceLocation>();
  84. WorldEntry(int worldType) {
  85. this.worldType = worldType;
  86. loadSkies();
  87. loadCelestialObject("sun");
  88. loadCelestialObject("moon_phases");
  89. }
  90. private void loadSkies() {
  91. for (int i = -1; ; i++) {
  92. String path = "sky/world" + worldType + "/sky" + (i < 0 ? "" : String.valueOf(i)) + ".properties";
  93. ResourceLocation resource = TexturePackAPI.newMCPatcherResourceLocation(path);
  94. Layer layer = Layer.create(resource);
  95. if (layer == null) {
  96. if (i > 0) {
  97. break;
  98. }
  99. } else if (layer.valid) {
  100. logger.fine("loaded %s", resource);
  101. skies.add(layer);
  102. textures.add(layer.texture);
  103. }
  104. }
  105. }
  106. private void loadCelestialObject(String objName) {
  107. ResourceLocation textureName = new ResourceLocation("textures/environment/" + objName + ".png");
  108. ResourceLocation resource = TexturePackAPI.newMCPatcherResourceLocation("sky/world0/" + objName + ".properties");
  109. Properties properties = TexturePackAPI.getProperties(resource);
  110. if (properties != null) {
  111. properties.setProperty("fade", "false");
  112. properties.setProperty("rotate", "true");
  113. Layer layer = new Layer(resource, properties);
  114. if (layer.valid) {
  115. logger.fine("using %s (%s) for the %s", resource, layer.texture, objName);
  116. objects.put(textureName, layer);
  117. }
  118. }
  119. }
  120. boolean active() {
  121. return !skies.isEmpty() || !objects.isEmpty();
  122. }
  123. void renderAll(Tessellator tessellator) {
  124. Set<ResourceLocation> texturesNeeded = new HashSet<ResourceLocation>();
  125. for (Layer layer : skies) {
  126. if (layer.prepare()) {
  127. texturesNeeded.add(layer.texture);
  128. }
  129. }
  130. Set<ResourceLocation> texturesToUnload = new HashSet<ResourceLocation>();
  131. texturesToUnload.addAll(textures);
  132. texturesToUnload.removeAll(texturesNeeded);
  133. for (ResourceLocation resource : texturesToUnload) {
  134. TexturePackAPI.unloadTexture(resource);
  135. }
  136. for (Layer layer : skies) {
  137. if (layer.brightness > 0.0f) {
  138. layer.render(tessellator);
  139. Layer.clearBlendingMethod();
  140. }
  141. }
  142. }
  143. Layer getCelestialObject(ResourceLocation defaultTexture) {
  144. return objects.get(defaultTexture);
  145. }
  146. void unloadTextures() {
  147. for (Layer layer : skies) {
  148. TexturePackAPI.unloadTexture(layer.texture);
  149. }
  150. }
  151. }
  152. private static class Layer {
  153. private static final int SECS_PER_DAY = 24 * 60 * 60;
  154. private static final int TICKS_PER_DAY = 24000;
  155. private static final double TOD_OFFSET = -0.25;
  156. private static final double SKY_DISTANCE = 100.0;
  157. private final ResourceLocation propertiesName;
  158. private final Properties properties;
  159. private ResourceLocation texture;
  160. private boolean fade;
  161. private boolean rotate;
  162. private float[] axis;
  163. private float speed;
  164. private BlendMethod blendMethod;
  165. private double a;
  166. private double b;
  167. private double c;
  168. boolean valid;
  169. float brightness;
  170. static Layer create(ResourceLocation resource) {
  171. Properties properties = TexturePackAPI.getProperties(resource);
  172. if (properties == null) {
  173. return null;
  174. } else {
  175. return new Layer(resource, properties);
  176. }
  177. }
  178. Layer(ResourceLocation propertiesName, Properties properties) {
  179. this.propertiesName = propertiesName;
  180. this.properties = properties;
  181. valid = true;
  182. valid = (readTexture() && readRotation() & readBlendingMethod() && readFadeTimers());
  183. }
  184. private boolean readTexture() {
  185. texture = TexturePackAPI.parseResourceLocation(propertiesName, properties.getProperty("source", propertiesName.getPath().replaceFirst("\\.properties$", ".png")));
  186. if (TexturePackAPI.hasResource(texture)) {
  187. return true;
  188. } else {
  189. return addError("source texture %s not found", texture);
  190. }
  191. }
  192. private boolean readRotation() {
  193. rotate = MCPatcherUtils.getBooleanProperty(properties, "rotate", true);
  194. if (rotate) {
  195. try {
  196. speed = Float.parseFloat(properties.getProperty("speed", "1.0"));
  197. } catch (NumberFormatException e) {
  198. return addError("invalid rotation speed");
  199. }
  200. String value = properties.getProperty("axis", "0.0 0.0 1.0").trim().toLowerCase();
  201. String[] tokens = value.split("\\s+");
  202. if (tokens.length == 3) {
  203. float x;
  204. float y;
  205. float z;
  206. try {
  207. x = Float.parseFloat(tokens[0]);
  208. y = Float.parseFloat(tokens[1]);
  209. z = Float.parseFloat(tokens[2]);
  210. } catch (NumberFormatException e) {
  211. return addError("invalid rotation axis");
  212. }
  213. if (x * x + y * y + z * z == 0.0f) {
  214. return addError("rotation axis cannot be 0");
  215. }
  216. axis = new float[]{z, y, -x};
  217. } else {
  218. return addError("invalid rotate value %s", value);
  219. }
  220. }
  221. return true;
  222. }
  223. private boolean readBlendingMethod() {
  224. String value = properties.getProperty("blend", "add");
  225. blendMethod = BlendMethod.parse(value);
  226. if (blendMethod == null) {
  227. return addError("unknown blend method %s", value);
  228. }
  229. return true;
  230. }
  231. private boolean readFadeTimers() {
  232. fade = Boolean.parseBoolean(properties.getProperty("fade", "true"));
  233. if (!fade) {
  234. return true;
  235. }
  236. int startFadeIn = parseTime(properties, "startFadeIn");
  237. int endFadeIn = parseTime(properties, "endFadeIn");
  238. int endFadeOut = parseTime(properties, "endFadeOut");
  239. if (!valid) {
  240. return false;
  241. }
  242. while (endFadeIn <= startFadeIn) {
  243. endFadeIn += SECS_PER_DAY;
  244. }
  245. while (endFadeOut <= endFadeIn) {
  246. endFadeOut += SECS_PER_DAY;
  247. }
  248. if (endFadeOut - startFadeIn >= SECS_PER_DAY) {
  249. return addError("fade times must fall within a 24 hour period");
  250. }
  251. int startFadeOut = startFadeIn + endFadeOut - endFadeIn;
  252. // f(x) = a cos x + b sin x + c
  253. // f(s0) = 0
  254. // f(s1) = 1
  255. // f(e1) = 0
  256. // Solve for a, b, c using Cramer's rule.
  257. double s0 = normalize(startFadeIn, SECS_PER_DAY, TOD_OFFSET);
  258. double s1 = normalize(endFadeIn, SECS_PER_DAY, TOD_OFFSET);
  259. double e0 = normalize(startFadeOut, SECS_PER_DAY, TOD_OFFSET);
  260. double e1 = normalize(endFadeOut, SECS_PER_DAY, TOD_OFFSET);
  261. double det = Math.cos(s0) * Math.sin(s1) + Math.cos(e1) * Math.sin(s0) + Math.cos(s1) * Math.sin(e1) -
  262. Math.cos(s0) * Math.sin(e1) - Math.cos(s1) * Math.sin(s0) - Math.cos(e1) * Math.sin(s1);
  263. if (det == 0.0) {
  264. return addError("determinant is 0");
  265. }
  266. a = (Math.sin(e1) - Math.sin(s0)) / det;
  267. b = (Math.cos(s0) - Math.cos(e1)) / det;
  268. c = (Math.cos(e1) * Math.sin(s0) - Math.cos(s0) * Math.sin(e1)) / det;
  269. logger.finer("%s: y = %f cos x + %f sin x + %f", propertiesName, a, b, c);
  270. logger.finer(" at %f: %f", s0, f(s0));
  271. logger.finer(" at %f: %f", s1, f(s1));
  272. logger.finer(" at %f: %f", e0, f(e0));
  273. logger.finer(" at %f: %f", e1, f(e1));
  274. return true;
  275. }
  276. private boolean addError(String format, Object... params) {
  277. logger.error("" + propertiesName + ": " + format, params);
  278. valid = false;
  279. return false;
  280. }
  281. private int parseTime(Properties properties, String key) {
  282. String s = properties.getProperty(key, "").trim();
  283. if ("".equals(s)) {
  284. addError("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. addError("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. tessellator.startDrawingQuads();
  362. tessellator.addVertexWithUV(-SKY_DISTANCE, -SKY_DISTANCE, -SKY_DISTANCE, tileX, tileY);
  363. tessellator.addVertexWithUV(-SKY_DISTANCE, -SKY_DISTANCE, SKY_DISTANCE, tileX, tileY + 0.5);
  364. tessellator.addVertexWithUV(SKY_DISTANCE, -SKY_DISTANCE, SKY_DISTANCE, tileX + 1.0 / 3.0, tileY + 0.5);
  365. tessellator.addVertexWithUV(SKY_DISTANCE, -SKY_DISTANCE, -SKY_DISTANCE, tileX + 1.0 / 3.0, tileY);
  366. tessellator.draw();
  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. GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
  378. GL11.glColor4f(1.0f, 1.0f, 1.0f, rainStrength);
  379. }
  380. }
  381. }