PageRenderTime 161ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

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