package net.kennux.cubicworld.voxel; import java.io.IOException; import java.util.HashMap; import java.util.Map.Entry; import java.util.zip.DataFormatException; import net.kennux.cubicworld.assets.TextureAtlas; import net.kennux.cubicworld.serialization.BitReader; import net.kennux.cubicworld.serialization.BitWriter; import net.kennux.cubicworld.util.CompressionUtils; import net.kennux.cubicworld.util.ConsoleHelper; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.math.Vector2; /** * Voxel engine static base class. * * TODO: Support for merging voxel ids between updates, so if one plugin got updated and adds a new item the item id table will get built offsetted! * TODO: May change world saving to write the current item name -> id table into the world save file. * * @author KennuX * */ public class VoxelEngine { /** * Do this directly after you added all textures. */ public static void compileTextureAtlas() { textureAtlas.compileTexture(); } /** * Deserializes the given compressed & serialized voxel data generated by serializeVoxelData(). * * @param voxelData * @return The voxel data. Null if there was an error! */ public static VoxelData[][][] deserializeVoxelData(byte[] data) { VoxelData[][][] voxelData = new VoxelData[16][16][16]; // Decompress chunk data BitReader chunkReader = null; try { data = CompressionUtils.decompress(data); chunkReader = new BitReader(data); // Read voxel data for (int x = 0; x < voxelData.length; x++) for (int y = 0; y < voxelData[x].length; y++) for (int z = 0; z < voxelData[x][y].length; z++) voxelData[x][y][z] = chunkReader.readVoxelData(); return voxelData; } catch (DataFormatException | IOException e) { ConsoleHelper.writeLog("error", "IOException occured while unpacking chunk data: ", "ServerChunkDataPacket"); e.printStackTrace(); } return null; } /** * Gets called when the bootstrap execution finished. * This will initialize all uv's on the voxeltypes. */ public static void finalization() { for (Entry<Short, VoxelType> e : voxelTypes.entrySet()) { e.getValue().initializeUvs(); } } /** * Returns the dictionary string for the voxel with the given id. * Returns an empty string if there was an error. * * @param id * @return */ public static String getNameByVoxelId(short id) { for (Entry<Short, String> entry : typeDictionary.entrySet()) { if (entry.getKey().shortValue() == id) { return entry.getValue(); } } return ""; } /** * Returns the texture with the given texture id. Returns null if the * texture is not found. * * @param textureId * @return */ public static Texture getTexture(int textureId) { // Search texture by id Integer texId = new Integer(textureId); if (textures.containsKey(texId)) { return textures.get(texId); } return null; } /** * Returns the texture coordinates for the given texture id. * * @param textureId * @return */ public static Vector2[] getUvForTexture(int textureId) { return textureAtlas.getUvForTexture(textureId); } /** * Returns the voxel type registered at the given voxel type. If the id was * not found it will return null. * * @param typeId * @return */ public static VoxelType getVoxelType(short typeId) { Short id = new Short(typeId); if (voxelTypes.containsKey(id)) { return voxelTypes.get(id); } return null; } /** * Performs a reverse lookup for retrieving voxel type id by type name. * Returns -1 if the voxel was not found. * * @param typeName * @return */ public static short getVoxelTypeIdByName(String typeName) { for (Entry<Short, String> e : typeDictionary.entrySet()) { if (e.getValue().equals(typeName)) return e.getKey().shortValue(); } return -1; } /** * Returns an array containing all voxel types. * * @return */ public static VoxelType[] getVoxelTypes() { return voxelTypes.values().toArray(new VoxelType[voxelTypes.size()]); } /** * Initializes the voxel engine if it isn't initialized already. */ public static void initialize(int textureWidth, int textureHeight) { if (textureWidth % 2 != 0) { ConsoleHelper.writeLog("error", "Non-power of 2 texture width in engine initialization given!", "VoxelEngine Init"); System.exit(-1); } if (textureHeight % 2 != 0) { ConsoleHelper.writeLog("error", "Non-power of 2 texture height in engine initialization given!", "VoxelEngine Init"); System.exit(-1); } // Initialize collections voxelTypes = new HashMap<Short, VoxelType>(); typeDictionary = new HashMap<Short, String>(); textures = new HashMap<Integer, Texture>(); textureDictionary = new HashMap<String, Integer>(); // Initialize counter typeIdCounter = 0; // Create texture atlas textureAtlas = new TextureAtlas(textureWidth, textureHeight); } /** * Registers a texture and returns it's texture id. * If the texture is already registered (with same name), nothing will happen in here. * This function will return -1 if there was an error (texture already registered). * * @param textureName * @return */ public static int registerTexture(String textureName, Texture texture) { if (!textureDictionary.containsKey(textureName)) { int textureId = textureAtlas.addTexture(texture); textureDictionary.put(textureName, textureId); textures.put(textureId, texture); return textureId; } else { return textureDictionary.get(textureName).intValue(); } } /** * Registers a new type with the given type name and returns it's type * instance for chaining. * * @param typeName * @return */ public static VoxelType registerType(String typeName) { if (!typeDictionary.containsValue(typeName)) { // Create new type VoxelType newType = new VoxelType(); newType.voxelId = typeIdCounter; newType.voxelName = typeName; // Register type typeDictionary.put(typeIdCounter, typeName); voxelTypes.put(typeIdCounter, newType); typeIdCounter++; return newType; } else { return voxelTypes.get(VoxelEngine.getVoxelTypeIdByName(typeName)); } } /** * Serializes the given voxel data. * It also compressed the serialized data. * * @param voxelData * @return The serialized & compressed chunk data. Null if there was an error! */ public static byte[] serializeVoxelData(VoxelData[][][] voxelData) { // Build chunk data in an external builder BitWriter chunkBuilder = new BitWriter(); // Write voxel data for (int x = 0; x < voxelData.length; x++) for (int y = 0; y < voxelData[x].length; y++) for (int z = 0; z < voxelData[x][y].length; z++) { chunkBuilder.writeVoxelData(voxelData[x][y][z]); } // Compress chunk data and add it to the builder byte[] data = chunkBuilder.getPacket(); try { data = CompressionUtils.compress(data); return data; } catch (IOException e) { ConsoleHelper.writeLog("error", "IOException occured while packing chunk data: ", "ServerChunkDataPacket"); e.printStackTrace(); } return null; } /** * The voxel types registered in the engine */ private static HashMap<Short, VoxelType> voxelTypes; /** * The types dictionary maps type names to shorts. */ private static HashMap<Short, String> typeDictionary; /** * The voxel textures registered in the engine */ private static HashMap<Integer, Texture> textures; /** * The types dictionary maps type names to shorts. */ private static HashMap<String, Integer> textureDictionary; /** * The types identifier counter. Gets incremented for every added type. */ private static short typeIdCounter = 0; public static TextureAtlas textureAtlas; }