package net.kennux.cubicworld.environment; import net.kennux.cubicworld.util.ShaderLoader; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Quaternion; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.GdxRuntimeException; /** * <pre> * Simple skybox implementation. * This class renders a skybox constructed from 6 given textures. * * TODO Cleaner re-implementation and sun / moon and stars support * </pre> * * @author KennuX * */ public class Skybox implements Disposable { /** * <pre> * The pixel maps of the 6 side textures. * 0 = right * 1 = left * 2 = up * 3 = down * 4 = front * 5 = back * </pre> * */ protected final Pixmap[] data = new Pixmap[6]; /** * The skybox shader, loaded by ShaderLoader.loadShader("skybox"). */ protected ShaderProgram shader; /** * The uniform location of the world translation matrix. */ protected int u_worldTrans; /** * The skybox quad constructed in the constructor. */ protected Mesh quad; private Matrix4 worldTrans; private Matrix4 fakeCam; /** * Constructs the skybox from the given filehandles. * * @param positiveX * right texture * @param negativeX * left texture * @param positiveY * up texture * @param negativeY * bottom texture * @param positiveZ * front texture * @param negativeZ * back texture */ public Skybox(FileHandle positiveX, FileHandle negativeX, FileHandle positiveY, FileHandle negativeY, FileHandle positiveZ, FileHandle negativeZ) { this(new Pixmap(positiveX), new Pixmap(negativeX), new Pixmap(positiveY), new Pixmap(negativeY), new Pixmap(positiveZ), new Pixmap(negativeZ)); } /** * Constructs the skybox from the given pixmaps. * * @param positiveX * right texture * @param negativeX * left texture * @param positiveY * up texture * @param negativeY * bottom texture * @param positiveZ * front texture * @param negativeZ * back texture */ public Skybox(Pixmap positiveX, Pixmap negativeX, Pixmap positiveY, Pixmap negativeY, Pixmap positiveZ, Pixmap negativeZ) { // Load shader this.shader = ShaderLoader.loadShader("skybox"); data[0] = positiveX; data[1] = negativeX; data[2] = positiveY; data[3] = negativeY; data[4] = positiveZ; data[5] = negativeZ; if (!shader.isCompiled()) throw new GdxRuntimeException(shader.getLog()); u_worldTrans = shader.getUniformLocation("u_worldTrans"); quad = this.createQuad(); worldTrans = new Matrix4(); fakeCam = new Matrix4(); fakeCam.setTranslation(0, 0, -1f); // bind cubemap Gdx.gl20.glBindTexture(GL20.GL_TEXTURE_CUBE_MAP, 0); Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL20.GL_RGB, data[0].getWidth(), data[0].getHeight(), 0, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, data[0].getPixels()); Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL20.GL_RGB, data[1].getWidth(), data[1].getHeight(), 0, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, data[1].getPixels()); Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL20.GL_RGB, data[2].getWidth(), data[2].getHeight(), 0, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, data[2].getPixels()); Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL20.GL_RGB, data[3].getWidth(), data[3].getHeight(), 0, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, data[3].getPixels()); Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL20.GL_RGB, data[4].getWidth(), data[4].getHeight(), 0, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, data[4].getPixels()); Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL20.GL_RGB, data[5].getWidth(), data[5].getHeight(), 0, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, data[5].getPixels()); Gdx.gl20.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR_MIPMAP_LINEAR); Gdx.gl20.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR); Gdx.gl20.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE); Gdx.gl20.glTexParameteri(GL20.GL_TEXTURE_CUBE_MAP, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE); } /** * Creates a simple quad mesh. * * @return */ public Mesh createQuad() { Mesh mesh = new Mesh(true, 4, 6, VertexAttribute.Position(), VertexAttribute.ColorUnpacked(), VertexAttribute.TexCoords(0)); mesh.setVertices(new float[] { -1f, -1f, 0, 1, 1, 1, 1, 0, 1, 1f, -1f, 0, 1, 1, 1, 1, 1, 1, 1f, 1f, 0, 1, 1, 1, 1, 1, 0, -1f, 1f, 0, 1, 1, 1, 1, 0, 0 }); mesh.setIndices(new short[] { 0, 1, 2, 2, 3, 0 }); return mesh; } @Override public void dispose() { shader.dispose(); quad.dispose(); for (int i = 0; i < 6; i++) data[i].dispose(); } /** * Renders the skybox for the given quaternion camera rotation. * * @param quaternion * The rotation of the camera used to render the world. */ public void render(Quaternion quaternion) { worldTrans.idt(); worldTrans.rotate(quaternion); shader.begin(); shader.setUniformMatrix(u_worldTrans, worldTrans.cpy().mul(fakeCam)); Gdx.gl20.glGenerateMipmap(GL20.GL_TEXTURE_CUBE_MAP); quad.render(shader, GL20.GL_TRIANGLES); shader.end(); } }