1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.graphics; 18 19 import java.util.HashMap; 20 import java.util.Map; 21 22 import com.badlogic.gdx.Application; 23 import com.badlogic.gdx.Gdx; 24 import com.badlogic.gdx.assets.AssetLoaderParameters.LoadedCallback; 25 import com.badlogic.gdx.assets.AssetManager; 26 import com.badlogic.gdx.assets.loaders.AssetLoader; 27 import com.badlogic.gdx.assets.loaders.CubemapLoader.CubemapParameter; 28 import com.badlogic.gdx.files.FileHandle; 29 import com.badlogic.gdx.graphics.Pixmap.Format; 30 import com.badlogic.gdx.graphics.Texture.TextureFilter; 31 import com.badlogic.gdx.graphics.Texture.TextureWrap; 32 import com.badlogic.gdx.graphics.glutils.FacedCubemapData; 33 import com.badlogic.gdx.graphics.glutils.PixmapTextureData; 34 import com.badlogic.gdx.math.Vector3; 35 import com.badlogic.gdx.utils.Array; 36 import com.badlogic.gdx.utils.GdxRuntimeException; 37 38 /** Wraps a standard OpenGL ES Cubemap. Must be disposed when it is no longer used. 39 * @author Xoppa */ 40 public class Cubemap extends GLTexture { 41 private static AssetManager assetManager; 42 final static Map<Application, Array<Cubemap>> managedCubemaps = new HashMap<Application, Array<Cubemap>>(); 43 44 /** Enum to identify each side of a Cubemap */ 45 public enum CubemapSide { 46 /** The positive X and first side of the cubemap */ 47 PositiveX(0, GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, -1, 0, 1, 0, 0), 48 /** The negative X and second side of the cubemap */ 49 NegativeX(1, GL20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, -1, 0, -1, 0, 0), 50 /** The positive Y and third side of the cubemap */ 51 PositiveY(2, GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, 0, 1, 0, 1, 0), 52 /** The negative Y and fourth side of the cubemap */ 53 NegativeY(3, GL20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, 0, -1, 0, -1, 0), 54 /** The positive Z and fifth side of the cubemap */ 55 PositiveZ(4, GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, -1, 0, 0, 0, 1), 56 /** The negative Z and sixth side of the cubemap */ 57 NegativeZ(5, GL20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, -1, 0, 0, 0, -1); 58 59 /** The zero based index of the side in the cubemap */ 60 public final int index; 61 /** The OpenGL target (used for glTexImage2D) of the side. */ 62 public final int glEnum; 63 /** The up vector to target the side. */ 64 public final Vector3 up; 65 /** The direction vector to target the side. */ 66 public final Vector3 direction; 67 CubemapSide(int index, int glEnum, float upX, float upY, float upZ, float directionX, float directionY, float directionZ)68 CubemapSide (int index, int glEnum, float upX, float upY, float upZ, float directionX, float directionY, float directionZ) { 69 this.index = index; 70 this.glEnum = glEnum; 71 this.up = new Vector3(upX, upY, upZ); 72 this.direction = new Vector3(directionX, directionY, directionZ); 73 } 74 75 /** @return The OpenGL target (used for glTexImage2D) of the side. */ getGLEnum()76 public int getGLEnum () { 77 return glEnum; 78 } 79 80 /** @return The up vector of the side. */ getUp(Vector3 out)81 public Vector3 getUp (Vector3 out) { 82 return out.set(up); 83 } 84 85 /** @return The direction vector of the side. */ getDirection(Vector3 out)86 public Vector3 getDirection (Vector3 out) { 87 return out.set(direction); 88 } 89 } 90 91 protected CubemapData data; 92 93 /** Construct an Cubemap based on the given CubemapData. */ Cubemap(CubemapData data)94 public Cubemap (CubemapData data) { 95 super(GL20.GL_TEXTURE_CUBE_MAP); 96 this.data = data; 97 load(data); 98 } 99 100 /** Construct a Cubemap with the specified texture files for the sides, does not generate mipmaps. */ Cubemap(FileHandle positiveX, FileHandle negativeX, FileHandle positiveY, FileHandle negativeY, FileHandle positiveZ, FileHandle negativeZ)101 public Cubemap (FileHandle positiveX, FileHandle negativeX, FileHandle positiveY, FileHandle negativeY, FileHandle positiveZ, 102 FileHandle negativeZ) { 103 this(positiveX, negativeX, positiveY, negativeY, positiveZ, negativeZ, false); 104 } 105 106 /** Construct a Cubemap with the specified texture files for the sides, optionally generating mipmaps. */ Cubemap(FileHandle positiveX, FileHandle negativeX, FileHandle positiveY, FileHandle negativeY, FileHandle positiveZ, FileHandle negativeZ, boolean useMipMaps)107 public Cubemap (FileHandle positiveX, FileHandle negativeX, FileHandle positiveY, FileHandle negativeY, FileHandle positiveZ, 108 FileHandle negativeZ, boolean useMipMaps) { 109 this(TextureData.Factory.loadFromFile(positiveX, useMipMaps), TextureData.Factory.loadFromFile(negativeX, useMipMaps), 110 TextureData.Factory.loadFromFile(positiveY, useMipMaps), TextureData.Factory.loadFromFile(negativeY, useMipMaps), 111 TextureData.Factory.loadFromFile(positiveZ, useMipMaps), TextureData.Factory.loadFromFile(negativeZ, useMipMaps)); 112 } 113 114 /** Construct a Cubemap with the specified {@link Pixmap}s for the sides, does not generate mipmaps. */ Cubemap(Pixmap positiveX, Pixmap negativeX, Pixmap positiveY, Pixmap negativeY, Pixmap positiveZ, Pixmap negativeZ)115 public Cubemap (Pixmap positiveX, Pixmap negativeX, Pixmap positiveY, Pixmap negativeY, Pixmap positiveZ, Pixmap negativeZ) { 116 this(positiveX, negativeX, positiveY, negativeY, positiveZ, negativeZ, false); 117 } 118 119 /** Construct a Cubemap with the specified {@link Pixmap}s for the sides, optionally generating mipmaps. */ Cubemap(Pixmap positiveX, Pixmap negativeX, Pixmap positiveY, Pixmap negativeY, Pixmap positiveZ, Pixmap negativeZ, boolean useMipMaps)120 public Cubemap (Pixmap positiveX, Pixmap negativeX, Pixmap positiveY, Pixmap negativeY, Pixmap positiveZ, Pixmap negativeZ, 121 boolean useMipMaps) { 122 this(positiveX == null ? null : new PixmapTextureData(positiveX, null, useMipMaps, false), negativeX == null ? null 123 : new PixmapTextureData(negativeX, null, useMipMaps, false), positiveY == null ? null : new PixmapTextureData(positiveY, 124 null, useMipMaps, false), negativeY == null ? null : new PixmapTextureData(negativeY, null, useMipMaps, false), 125 positiveZ == null ? null : new PixmapTextureData(positiveZ, null, useMipMaps, false), negativeZ == null ? null 126 : new PixmapTextureData(negativeZ, null, useMipMaps, false)); 127 } 128 129 /** Construct a Cubemap with {@link Pixmap}s for each side of the specified size. */ Cubemap(int width, int height, int depth, Format format)130 public Cubemap (int width, int height, int depth, Format format) { 131 this(new PixmapTextureData(new Pixmap(depth, height, format), null, false, true), new PixmapTextureData(new Pixmap(depth, 132 height, format), null, false, true), new PixmapTextureData(new Pixmap(width, depth, format), null, false, true), 133 new PixmapTextureData(new Pixmap(width, depth, format), null, false, true), new PixmapTextureData(new Pixmap(width, 134 height, format), null, false, true), new PixmapTextureData(new Pixmap(width, height, format), null, false, true)); 135 } 136 137 /** Construct a Cubemap with the specified {@link TextureData}'s for the sides */ Cubemap(TextureData positiveX, TextureData negativeX, TextureData positiveY, TextureData negativeY, TextureData positiveZ, TextureData negativeZ)138 public Cubemap (TextureData positiveX, TextureData negativeX, TextureData positiveY, TextureData negativeY, 139 TextureData positiveZ, TextureData negativeZ) { 140 super(GL20.GL_TEXTURE_CUBE_MAP); 141 minFilter = TextureFilter.Nearest; 142 magFilter = TextureFilter.Nearest; 143 uWrap = TextureWrap.ClampToEdge; 144 vWrap = TextureWrap.ClampToEdge; 145 data = new FacedCubemapData(positiveX, negativeX, positiveY, negativeY, positiveZ, negativeZ); 146 load(data); 147 } 148 149 /** Sets the sides of this cubemap to the specified {@link CubemapData}. */ load(CubemapData data)150 public void load (CubemapData data) { 151 if (!data.isPrepared()) data.prepare(); 152 bind(); 153 unsafeSetFilter(minFilter, magFilter, true); 154 unsafeSetWrap(uWrap, vWrap, true); 155 data.consumeCubemapData(); 156 Gdx.gl.glBindTexture(glTarget, 0); 157 } 158 getCubemapData()159 public CubemapData getCubemapData () { 160 return data; 161 } 162 163 @Override isManaged()164 public boolean isManaged () { 165 return data.isManaged(); 166 } 167 168 @Override reload()169 protected void reload () { 170 if (!isManaged()) throw new GdxRuntimeException("Tried to reload an unmanaged Cubemap"); 171 glHandle = Gdx.gl.glGenTexture(); 172 load(data); 173 } 174 175 @Override getWidth()176 public int getWidth () { 177 return data.getWidth(); 178 } 179 180 @Override getHeight()181 public int getHeight () { 182 return data.getHeight(); 183 } 184 185 @Override getDepth()186 public int getDepth () { 187 return 0; 188 } 189 190 /** Disposes all resources associated with the cubemap */ 191 @Override dispose()192 public void dispose () { 193 // this is a hack. reason: we have to set the glHandle to 0 for textures that are 194 // reloaded through the asset manager as we first remove (and thus dispose) the texture 195 // and then reload it. the glHandle is set to 0 in invalidateAllTextures prior to 196 // removal from the asset manager. 197 if (glHandle == 0) return; 198 delete(); 199 if (data.isManaged()) if (managedCubemaps.get(Gdx.app) != null) managedCubemaps.get(Gdx.app).removeValue(this, true); 200 } 201 addManagedCubemap(Application app, Cubemap cubemap)202 private static void addManagedCubemap (Application app, Cubemap cubemap) { 203 Array<Cubemap> managedCubemapArray = managedCubemaps.get(app); 204 if (managedCubemapArray == null) managedCubemapArray = new Array<Cubemap>(); 205 managedCubemapArray.add(cubemap); 206 managedCubemaps.put(app, managedCubemapArray); 207 } 208 209 /** Clears all managed cubemaps. This is an internal method. Do not use it! */ clearAllCubemaps(Application app)210 public static void clearAllCubemaps (Application app) { 211 managedCubemaps.remove(app); 212 } 213 214 /** Invalidate all managed cubemaps. This is an internal method. Do not use it! */ invalidateAllCubemaps(Application app)215 public static void invalidateAllCubemaps (Application app) { 216 Array<Cubemap> managedCubemapArray = managedCubemaps.get(app); 217 if (managedCubemapArray == null) return; 218 219 if (assetManager == null) { 220 for (int i = 0; i < managedCubemapArray.size; i++) { 221 Cubemap cubemap = managedCubemapArray.get(i); 222 cubemap.reload(); 223 } 224 } else { 225 // first we have to make sure the AssetManager isn't loading anything anymore, 226 // otherwise the ref counting trick below wouldn't work (when a cubemap is 227 // currently on the task stack of the manager.) 228 assetManager.finishLoading(); 229 230 // next we go through each cubemap and reload either directly or via the 231 // asset manager. 232 Array<Cubemap> cubemaps = new Array<Cubemap>(managedCubemapArray); 233 for (Cubemap cubemap : cubemaps) { 234 String fileName = assetManager.getAssetFileName(cubemap); 235 if (fileName == null) { 236 cubemap.reload(); 237 } else { 238 // get the ref count of the cubemap, then set it to 0 so we 239 // can actually remove it from the assetmanager. Also set the 240 // handle to zero, otherwise we might accidentially dispose 241 // already reloaded cubemaps. 242 final int refCount = assetManager.getReferenceCount(fileName); 243 assetManager.setReferenceCount(fileName, 0); 244 cubemap.glHandle = 0; 245 246 // create the parameters, passing the reference to the cubemap as 247 // well as a callback that sets the ref count. 248 CubemapParameter params = new CubemapParameter(); 249 params.cubemapData = cubemap.getCubemapData(); 250 params.minFilter = cubemap.getMinFilter(); 251 params.magFilter = cubemap.getMagFilter(); 252 params.wrapU = cubemap.getUWrap(); 253 params.wrapV = cubemap.getVWrap(); 254 params.cubemap = cubemap; // special parameter which will ensure that the references stay the same. 255 params.loadedCallback = new LoadedCallback() { 256 @Override 257 public void finishedLoading (AssetManager assetManager, String fileName, Class type) { 258 assetManager.setReferenceCount(fileName, refCount); 259 } 260 }; 261 262 // unload the c, create a new gl handle then reload it. 263 assetManager.unload(fileName); 264 cubemap.glHandle = Gdx.gl.glGenTexture(); 265 assetManager.load(fileName, Cubemap.class, params); 266 } 267 } 268 managedCubemapArray.clear(); 269 managedCubemapArray.addAll(cubemaps); 270 } 271 } 272 273 /** Sets the {@link AssetManager}. When the context is lost, cubemaps managed by the asset manager are reloaded by the manager 274 * on a separate thread (provided that a suitable {@link AssetLoader} is registered with the manager). Cubemaps not managed by 275 * the AssetManager are reloaded via the usual means on the rendering thread. 276 * @param manager the asset manager. */ setAssetManager(AssetManager manager)277 public static void setAssetManager (AssetManager manager) { 278 Cubemap.assetManager = manager; 279 } 280 getManagedStatus()281 public static String getManagedStatus () { 282 StringBuilder builder = new StringBuilder(); 283 builder.append("Managed cubemap/app: { "); 284 for (Application app : managedCubemaps.keySet()) { 285 builder.append(managedCubemaps.get(app).size); 286 builder.append(" "); 287 } 288 builder.append("}"); 289 return builder.toString(); 290 } 291 292 /** @return the number of managed cubemaps currently loaded */ getNumManagedCubemaps()293 public static int getNumManagedCubemaps () { 294 return managedCubemaps.get(Gdx.app).size; 295 } 296 297 } 298