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;
}