/* * Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.rendering.world; import static org.lwjgl.opengl.GL11.GL_CULL_FACE; import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; import static org.lwjgl.opengl.GL11.GL_LIGHT0; import static org.lwjgl.opengl.GL11.glCallList; import static org.lwjgl.opengl.GL11.glDisable; import static org.lwjgl.opengl.GL11.glEnable; import static org.lwjgl.opengl.GL11.glEndList; import static org.lwjgl.opengl.GL11.glGenLists; import static org.lwjgl.opengl.GL11.glLight; import static org.lwjgl.opengl.GL11.glNewList; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import javax.vecmath.Vector3d; import javax.vecmath.Vector4d; import javax.vecmath.Vector4f; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; import org.lwjgl.util.glu.Sphere; import org.terasology.logic.manager.AssetManager; import org.terasology.logic.manager.ShaderManager; import org.terasology.math.TeraMath; import org.terasology.rendering.interfaces.IGameObject; import org.terasology.rendering.shader.ShaderProgram; /** * Skysphere based on the Perez all weather luminance model. * * @author Anthony Kireev <adeon.k87@gmail.com> * @author Benjamin Glatzel <benjamin.glatzel@me.com> */ public class Skysphere implements IGameObject { private static int _displayListSphere = -1; private static final float PI = 3.1415926f; /* SKY */ private double _turbidity = 4.0f, _sunPosAngle = 0.1f; private static IntBuffer _textureIds; private final WorldRenderer _parent; public Skysphere(WorldRenderer parent) { _parent = parent; initTextures(); loadCubeMap(_textureIds.get(0), "stars", 128); loadCubeMap(_textureIds.get(1), "sky", 512); } private void initTextures() { if (_textureIds == null) { _textureIds = BufferUtils.createIntBuffer(2); GL11.glGenTextures(_textureIds); } } private void loadCubeMap(int textureId, String name, int size) { int internalFormat = GL11.GL_RGBA8, format = GL12.GL_BGRA; GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, textureId); GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE); GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL12.GL_TEXTURE_WRAP_R, GL12.GL_CLAMP_TO_EDGE); GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); for (int i = 0; i < 6; i++) { ByteBuffer data = AssetManager.loadTexture("engine:"+ name + (i + 1)).getImageData(0); GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, size, size, 0, format, GL11.GL_UNSIGNED_BYTE, data); } } public void render() { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glEnable(GL13.GL_TEXTURE_CUBE_MAP); GL13.glActiveTexture(GL13.GL_TEXTURE0); GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, _textureIds.get(0)); GL13.glActiveTexture(GL13.GL_TEXTURE1); GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, _textureIds.get(1)); _sunPosAngle = (float) java.lang.Math.toRadians(360.0 - 90.0); Vector4d sunNormalise = new Vector4d(0.0f, java.lang.Math.cos(_sunPosAngle), java.lang.Math.sin(_sunPosAngle), 1.0); sunNormalise.normalize(); Vector3d zenithColor = new Vector3d(); if (sunNormalise.y >= -0.35) zenithColor = getAllWeatherZenith((float) sunNormalise.y); ShaderProgram shader = ShaderManager.getInstance().getShaderProgram("sky"); shader.enable(); shader.setInt("texCubeStars", 0); shader.setInt("texCubeSky", 1); shader.setFloat4("sunPos", 0.0f, (float) java.lang.Math.cos(_sunPosAngle), (float) java.lang.Math.sin(_sunPosAngle), 1.0f); shader.setFloat("time", 0.5f); shader.setFloat("sunAngle", (float) _sunPosAngle); shader.setFloat("turbidity", (float) _turbidity); shader.setFloat3("zenith", (float) zenithColor.x, (float) zenithColor.y, (float) zenithColor.z); shader.setFloat("daylight", (float) getDaylight()); // Draw the skysphere drawSphere(); glDisable(GL13.GL_TEXTURE_CUBE_MAP); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); } private Vector3d getAllWeatherZenith(float thetaSun) { thetaSun = (float) java.lang.Math.acos(thetaSun); Vector4f cx1 = new Vector4f(0.0f, 0.00209f, -0.00375f, 0.00165f); Vector4f cx2 = new Vector4f(0.00394f, -0.03202f, 0.06377f, -0.02903f); Vector4f cx3 = new Vector4f(0.25886f, 0.06052f, -0.21196f, 0.11693f); Vector4f cy1 = new Vector4f(0.0f, 0.00317f, -0.00610f, 0.00275f); Vector4f cy2 = new Vector4f(0.00516f, -0.04153f, 0.08970f, -0.04214f); Vector4f cy3 = new Vector4f(0.26688f, 0.06670f, -0.26756f, 0.15346f); double t2 = (float) java.lang.Math.pow(_turbidity, 2); double chi = (4.0f / 9.0f - _turbidity / 120.0f) * (PI - 2.0f * thetaSun); Vector4f theta = new Vector4f(1, thetaSun, (float) java.lang.Math.pow(thetaSun, 2), (float) java.lang.Math.pow(thetaSun, 3)); double Y = (4.0453f * _turbidity - 4.9710f) * (float) java.lang.Math.tan(chi) - 0.2155f * _turbidity + 2.4192f; double x = t2 * cx1.dot(theta) + _turbidity * cx2.dot(theta) + cx3.dot(theta); double y = t2 * cy1.dot(theta) + _turbidity * cy2.dot(theta) + cy3.dot(theta); return new Vector3d(Y, x, y); } public void update(float delta) { // Set the light direction according to the position of the sun FloatBuffer buffer = BufferUtils.createFloatBuffer(4); buffer.put(0.0f).put((float) java.lang.Math.cos(_sunPosAngle)).put((float) java.lang.Math.sin(_sunPosAngle)).put(1.0f); buffer.flip(); glLight(GL_LIGHT0, GL11.GL_POSITION, buffer); } private void drawSphere() { if (_displayListSphere == -1) { _displayListSphere = glGenLists(1); Sphere sphere = new Sphere(); glNewList(_displayListSphere, GL11.GL_COMPILE); sphere.draw(16, 16, 128); glEndList(); } glCallList(_displayListSphere); } public double getSunPosAngle() { return _sunPosAngle; } public double getTurbidity() { return _turbidity; } public double getDaylight() { double angle = java.lang.Math.toDegrees(TeraMath.clamp(java.lang.Math.cos(_sunPosAngle))); double daylight = 1.0; if (angle < 24.0) { daylight = 1.0 - (24.0 - angle) / 24.0; } return daylight; } }