1 package com.badlogic.gdx.graphics.g3d.particles; 2 3 import com.badlogic.gdx.assets.AssetDescriptor; 4 import com.badlogic.gdx.assets.AssetManager; 5 import com.badlogic.gdx.utils.Array; 6 import com.badlogic.gdx.utils.GdxRuntimeException; 7 import com.badlogic.gdx.utils.IntArray; 8 import com.badlogic.gdx.utils.Json; 9 import com.badlogic.gdx.utils.JsonValue; 10 import com.badlogic.gdx.utils.ObjectMap; 11 import com.badlogic.gdx.utils.ObjectMap.Entry; 12 import com.badlogic.gdx.utils.reflect.ClassReflection; 13 import com.badlogic.gdx.utils.reflect.ReflectionException; 14 15 /** This class handles the assets and configurations required by a given resource when de/serialized. 16 * It's handy when a given object or one of its members requires some assets to be loaded to work properly after 17 * being deserialized. To save the assets, the object should implement the {@link Configurable} interface and obtain 18 * a {@link SaveData} object to store every required asset or information which will be used during the loading phase. 19 * The passed in {@link AssetManager} is generally used to find the asset file name for a given resource of a given type. 20 * The class can also store global configurations, this is useful when dealing with objects which should be allocated once 21 * (i.e singleton). 22 * The deserialization process must happen in the same order of serialization, because the per object {@link SaveData} blocks are stored 23 * as an {@link Array} within the {@link ResourceData}, while the global {@link SaveData} instances can be accessed in any order because 24 * require a unique {@link String} and are stored in an {@link ObjectMap}. 25 * @author Inferno */ 26 public class ResourceData<T> implements Json.Serializable{ 27 28 /** This interface must be implemented by any class requiring additional assets to be loaded/saved */ 29 public static interface Configurable<T>{ save(AssetManager manager, ResourceData<T> resources)30 public void save(AssetManager manager, ResourceData<T> resources); load(AssetManager manager, ResourceData<T> resources)31 public void load(AssetManager manager, ResourceData<T> resources); 32 } 33 34 /** Contains all the saved data. 35 * {@link #data} is a map which link an asset name to its instance. 36 * {@link #assets} is an array of indices addressing a given 37 * {@link com.badlogic.gdx.graphics.g3d.particles.ResourceData.AssetData} in the {@link ResourceData} */ 38 public static class SaveData implements Json.Serializable{ 39 ObjectMap<String, Object> data; 40 IntArray assets; 41 private int loadIndex; 42 protected ResourceData resources; 43 SaveData()44 public SaveData(){ 45 data = new ObjectMap<String, Object>(); 46 assets = new IntArray(); 47 loadIndex = 0; 48 } 49 SaveData(ResourceData resources)50 public SaveData(ResourceData resources){ 51 data = new ObjectMap<String, Object>(); 52 assets = new IntArray(); 53 loadIndex = 0; 54 this.resources = resources; 55 } 56 saveAsset(String filename, Class<K> type)57 public <K> void saveAsset(String filename, Class<K> type){ 58 int i = resources.getAssetData(filename, type); 59 if(i == -1){ 60 resources.sharedAssets.add(new AssetData(filename, type)); 61 i = resources.sharedAssets.size -1; 62 } 63 assets.add(i); 64 } 65 save(String key, Object value)66 public void save(String key, Object value){ 67 data.put(key, value); 68 } 69 loadAsset()70 public AssetDescriptor loadAsset(){ 71 if(loadIndex == assets.size) return null; 72 AssetData data = (AssetData)resources.sharedAssets.get(assets.get(loadIndex++)); 73 return new AssetDescriptor(data.filename, data.type); 74 } 75 load(String key)76 public <K> K load(String key){ 77 return (K)data.get(key); 78 } 79 80 @Override write(Json json)81 public void write (Json json) { 82 json.writeValue("data", data, ObjectMap.class); 83 json.writeValue("indices", assets.toArray(), int[].class); 84 } 85 86 @Override read(Json json, JsonValue jsonData)87 public void read (Json json, JsonValue jsonData) { 88 data = json.readValue("data", ObjectMap.class, jsonData); 89 assets.addAll(json.readValue("indices", int[].class, jsonData)); 90 } 91 } 92 93 /** This class contains all the information related to a given asset */ 94 public static class AssetData<T> implements Json.Serializable{ 95 public String filename; 96 public Class<T> type; AssetData()97 public AssetData(){} AssetData(String filename, Class<T> type)98 public AssetData(String filename, Class<T> type){ 99 this.filename = filename; 100 this.type = type; 101 } 102 @Override write(Json json)103 public void write (Json json) { 104 json.writeValue("filename", filename); 105 json.writeValue("type", type.getName()); 106 } 107 @Override read(Json json, JsonValue jsonData)108 public void read (Json json, JsonValue jsonData) { 109 filename = json.readValue("filename", String.class, jsonData); 110 String className = json.readValue("type", String.class, jsonData); 111 try { 112 type = (Class<T>)ClassReflection.forName(className); 113 } catch (ReflectionException e) { 114 throw new GdxRuntimeException("Class not found: " + className, e); 115 } 116 } 117 } 118 119 /** Unique data, can be used to save/load generic data which is not always loaded back after saving. 120 * Must be used to store data which is uniquely addressable by a given string (i.e a system configuration).*/ 121 private ObjectMap<String, SaveData> uniqueData; 122 123 /** Objects save data, must be loaded in the same saving order*/ 124 private Array<SaveData> data; 125 126 /** Shared assets among all the configurable objects*/ 127 Array<AssetData> sharedAssets; 128 private int currentLoadIndex; 129 public T resource; 130 ResourceData()131 public ResourceData(){ 132 uniqueData = new ObjectMap<String, SaveData>(); 133 data = new Array<SaveData>(true, 3, SaveData.class); 134 sharedAssets = new Array<AssetData>(); 135 currentLoadIndex = 0; 136 } 137 ResourceData(T resource)138 public ResourceData(T resource){ 139 this(); 140 this.resource = resource; 141 } 142 getAssetData(String filename, Class<K> type)143 <K> int getAssetData(String filename, Class<K> type){ 144 int i=0; 145 for(AssetData data : sharedAssets){ 146 if(data.filename.equals(filename) && data.type.equals(type)){ 147 return i; 148 } 149 ++i; 150 } 151 return -1; 152 } 153 getAssetDescriptors()154 public Array<AssetDescriptor> getAssetDescriptors () { 155 Array<AssetDescriptor> descriptors = new Array<AssetDescriptor>(); 156 for(AssetData data : sharedAssets){ 157 descriptors.add(new AssetDescriptor<T>(data.filename, data.type)); 158 } 159 return descriptors; 160 } 161 getAssets()162 public Array<AssetData> getAssets(){ 163 return sharedAssets; 164 } 165 166 /** Creates and adds a new SaveData object to the save data list*/ createSaveData()167 public SaveData createSaveData() { 168 SaveData saveData = new SaveData(this); 169 data.add(saveData); 170 return saveData; 171 } 172 173 /** Creates and adds a new and unique SaveData object to the save data map*/ createSaveData(String key)174 public SaveData createSaveData(String key) { 175 SaveData saveData = new SaveData(this); 176 if(uniqueData.containsKey(key)) 177 throw new RuntimeException("Key already used, data must be unique, use a different key"); 178 uniqueData.put(key, saveData); 179 return saveData; 180 } 181 182 /** @return the next save data in the list */ getSaveData()183 public SaveData getSaveData() { 184 return data.get(currentLoadIndex++); 185 } 186 187 /** @return the unique save data in the map */ getSaveData(String key)188 public SaveData getSaveData(String key) { 189 return uniqueData.get(key); 190 } 191 192 @Override write(Json json)193 public void write (Json json) { 194 json.writeValue("unique", uniqueData, ObjectMap.class); 195 json.writeValue("data", data, Array.class, SaveData.class); 196 json.writeValue("assets", sharedAssets.toArray(AssetData.class), AssetData[].class); 197 json.writeValue("resource", resource, null); 198 } 199 200 @Override read(Json json, JsonValue jsonData)201 public void read (Json json, JsonValue jsonData) { 202 uniqueData = json.readValue("unique", ObjectMap.class, jsonData); 203 for(Entry<String, SaveData> entry : uniqueData.entries()){ 204 entry.value.resources = this; 205 } 206 207 data = json.readValue("data", Array.class, SaveData.class, jsonData); 208 for(SaveData saveData : data){ 209 saveData.resources = this; 210 } 211 212 sharedAssets.addAll(json.readValue("assets", Array.class, AssetData.class, jsonData)); 213 resource = json.readValue("resource", null, jsonData); 214 } 215 216 217 } 218