use of io.xol.chunkstories.api.content.mods.AssetHierarchy in project chunkstories by Hugobros3.
the class VoxelModelsStore method resetAndLoadModels.
public void resetAndLoadModels() {
models.clear();
Iterator<AssetHierarchy> allFiles = voxels.parent().modsManager().getAllUniqueEntries();
while (allFiles.hasNext()) {
AssetHierarchy entry = allFiles.next();
if (entry.getName().startsWith("./voxels/blockmodels/") && entry.getName().endsWith(".model")) {
Asset f = entry.topInstance();
readBlockModel(f);
}
}
}
use of io.xol.chunkstories.api.content.mods.AssetHierarchy in project chunkstories by Hugobros3.
the class ModsManagerImplementation method getAllAssetsByPrefix.
@Override
public Iterator<Asset> getAllAssetsByPrefix(String prefix) {
return new Iterator<Asset>() {
Iterator<ModsAssetHierarchy> base = avaibleAssets.values().iterator();
Asset next = null;
@Override
public boolean hasNext() {
if (next != null)
return true;
// If next == null, try to set it
while (base.hasNext()) {
AssetHierarchy entry = base.next();
if (entry.getName().startsWith(prefix)) {
next = entry.topInstance();
break;
}
}
// Did we suceed etc
return next != null;
}
@Override
public Asset next() {
// Try loading
if (next == null)
hasNext();
// Null out reference and return it
Asset ret = next;
next = null;
return ret;
}
};
}
use of io.xol.chunkstories.api.content.mods.AssetHierarchy in project chunkstories by Hugobros3.
the class ModsManagerImplementation method getAllAssetsByExtension.
@Override
public Iterator<Asset> getAllAssetsByExtension(String extension) {
return new Iterator<Asset>() {
Iterator<ModsAssetHierarchy> base = avaibleAssets.values().iterator();
Asset next = null;
@Override
public boolean hasNext() {
if (next != null)
return true;
// If next == null, try to set it
while (base.hasNext()) {
AssetHierarchy entry = base.next();
if (entry.getName().endsWith(extension)) {
next = entry.topInstance();
break;
}
}
// Did we suceed etc
return next != null;
}
@Override
public Asset next() {
// Try loading
if (next == null)
hasNext();
// Null out reference and return it
Asset ret = next;
next = null;
return ret;
}
};
}
use of io.xol.chunkstories.api.content.mods.AssetHierarchy in project chunkstories by Hugobros3.
the class VoxelTexturesArrays method buildTextureAtlas.
public void buildTextureAtlas() {
defaultAlbedo = null;
defaultNormal = null;
defaultMaterial = null;
textures.clear();
try {
int gl_MaxTextureUnits = glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS);
int gl_MaxTextureArraySize = glGetInteger(GL_MAX_ARRAY_TEXTURE_LAYERS);
if (gl_MaxTextureArraySize < 2048) {
logger().warn("Max texture array size < 2048. For ideal results please use a GPU from this geological era.");
}
if (gl_MaxTextureUnits < 32) {
logger().warn("Max texture units < 32. This means your GPU is ancient and you'll run into a lot of issues!");
}
// We'll reserve 8 texture units for all the other fluff
int maxTextureArrays = (gl_MaxTextureUnits - 8);
List<AtlasElement> elements = new ArrayList<AtlasElement>();
// Map<Integer, AtlasElement> sizeBuckets = new HashMap<Integer, AtlasElement>();
// First we want to iterate over every file to get an idea of how many textures (and of how many sizes) we are dealing
Iterator<AssetHierarchy> allFiles = content.modsManager().getAllUniqueEntries();
AssetHierarchy entry;
Asset f;
while (allFiles.hasNext()) {
entry = allFiles.next();
if (entry.getName().startsWith("./voxels/textures/")) {
String name = entry.getName().replace("./voxels/textures/", "");
Type type = Type.ALBEDO;
if (name.startsWith("normal/")) {
name = name.substring("normal/".length());
type = Type.NORMAL;
} else if (name.startsWith("material/")) {
name = name.substring("material/".length());
type = Type.MATERIAL;
}
// We are only interested in top-level textures.
if (name.contains("/"))
continue;
f = entry.topInstance();
if (f.getName().endsWith(".png")) {
String textureName = name.replace(".png", "");
// VoxelTextureAtlased voxelTexture = new VoxelTextureAtlased(textureName, uniquesIds);
int width, height;
try {
ImageReader reader = ImageIO.getImageReadersBySuffix("png").next();
ImageInputStream stream = ImageIO.createImageInputStream(f.read());
reader.setInput(stream);
width = reader.getWidth(reader.getMinIndex());
height = reader.getHeight(reader.getMinIndex());
} catch (Exception e) {
logger().warn("Could not obtain the size of the asset: " + f.getName());
// e.printStackTrace();
continue;
}
// We want nice powers of two
if ((width & (width - 1)) != 0 || (height & (height - 1)) != 0) {
logger().warn("Non pow2 texture size (" + width + ":" + height + ") for: " + f.getName() + ", skipping.");
continue;
}
// Width >= 16
if (width < 16 || height < 16) {
logger().warn("Too small (<16px) texture (" + width + ":" + height + ") for: " + f.getName() + ", skipping.");
continue;
}
int frames = height / width;
AtlasElement texture = new AtlasElement(f, type, textureName, width, frames);
elements.add(texture);
if (textureName.equals("notex")) {
switch(type) {
case ALBEDO:
defaultAlbedo = texture;
break;
case NORMAL:
defaultNormal = texture;
break;
case MATERIAL:
defaultMaterial = texture;
break;
}
}
// System.out.println(textureName + " : " + frames);
// sizeBuckets.put(width, texture);
// System.out.println("Added: "+texture);
} else if (f.getName().endsWith(".jpg") || f.getName().endsWith(".tiff") || f.getName().endsWith(".bmp") || f.getName().endsWith(".gif")) {
logger().warn("Found image file of unsupported format in voxels folder: " + f.getName() + ", ignoring.");
continue;
}
}
}
// Check we DID obtain default textures
if (defaultAlbedo == null || defaultNormal == null || defaultMaterial == null) {
logger().error("Missing 'notex.png' for one of the 3 texture types (albedo, normal, material), exiting !");
System.exit(-602);
}
// Once that's done, try to fit all everything in the texture units constraints
int[] sizes = new int[maxTextureArrays];
int[] counts = new int[maxTextureArrays];
AtlasElement[][] stuff = new AtlasElement[maxTextureArrays][gl_MaxTextureArraySize];
int maxSize = 0;
int maxDownscaled = 0;
int downscaleTimes = 0;
while (true) {
// Check if everything could fit in N - 8 texture units
for (int i = 0; i < maxTextureArrays; i++) {
sizes[i] = 0;
counts[i] = 0;
}
boolean ko = false;
for (AtlasElement e : elements) {
if (e.downscaleTo > maxSize)
maxSize = e.downscaleTo;
boolean foundSpace = false;
for (int i = 0; i < maxTextureArrays; i++) {
// Can create a new size
if (sizes[i] == 0) {
sizes[i] = e.downscaleTo;
stuff[i][counts[i]] = e;
counts[i] += e.animationFrames;
foundSpace = true;
break;
} else // Size already exist
if (sizes[i] == e.downscaleTo) {
// Has enough remaining space
if (gl_MaxTextureArraySize - counts[i] >= e.animationFrames) {
stuff[i][counts[i]] = e;
counts[i] += e.animationFrames;
foundSpace = true;
break;
}
}
}
if (foundSpace) {
continue;
} else {
// We're out of space, cancel already
ko = true;
break;
}
}
if (!ko) {
// Everyone found a place! Go ahead it's cool
break;
} else {
if (maxSize == 16) {
JOptionPane.showMessageDialog(null, "Exceeded vram constraints :(");
}
// Shrink the biggest texture size and try again
for (AtlasElement e : elements) {
if (e.downscaleTo >= maxSize) {
e.downscaleTo = e.downscaleTo / 2;
}
}
downscaleTimes++;
maxDownscaled = maxSize / 2;
maxSize = 0;
}
}
System.out.println("Found space OK for every " + elements.size() + " voxel texture, had to downscale " + downscaleTimes + " resolutions down to " + maxDownscaled);
// Once we secured all the needed textures, let's assemble them in a big map
for (int i = 0; i < maxTextureArrays; i++) {
System.out.println("Array " + i + ": Size:" + sizes[i] + " Count:" + counts[i]);
sizes[i] = 0;
counts[i] = 0;
for (int j = 0; j < counts[i]; j++) {
AtlasElement element = stuff[i][j];
VoxelTextureArrayed completedTexture = textures.get(element.name);
if (completedTexture == null) {
completedTexture = new VoxelTextureArrayed(element.name);
textures.put(element.name, completedTexture);
}
switch(element.type) {
case ALBEDO:
completedTexture.albedo = element;
break;
case NORMAL:
completedTexture.normal = element;
break;
case MATERIAL:
completedTexture.material = element;
break;
}
}
}
} catch (Exception e) {
logger().error("Exception during loading of voxel textures: " + e.getMessage());
// e.printStackTrace(logger().getPrintWriter());
e.printStackTrace();
}
System.exit(0);
}
use of io.xol.chunkstories.api.content.mods.AssetHierarchy in project chunkstories by Hugobros3.
the class VoxelTexturesStoreAndAtlaser method buildTextureAtlas.
public void buildTextureAtlas() {
try {
// Clear previous values
texMap.clear();
// colors.clear();
// Compute all sizes first.
int totalSurfacedNeeded = 0;
// File folder = new File("./res/voxels/textures/");
// Get all sizes :
List<VoxelTextureAtlased> voxelTexturesSortedBySize = new ArrayList<VoxelTextureAtlased>();
// First we want to iterate over every file to get an idea of how many textures (and of how many sizes) we are dealing
Iterator<AssetHierarchy> allFiles = content.modsManager().getAllUniqueEntries();
AssetHierarchy entry;
Asset f;
while (allFiles.hasNext()) {
entry = allFiles.next();
if (entry.getName().startsWith("./voxels/textures/")) {
String name = entry.getName().replace("./voxels/textures/", "");
if (name.contains("/"))
continue;
f = entry.topInstance();
if (f.getName().endsWith(".png")) {
String textureName = name.replace(".png", "");
// System.out.println("texName:"+textureName+" "+entry.getKey());
if (!texMap.containsKey(textureName)) {
VoxelTextureAtlased voxelTexture = new VoxelTextureAtlased(textureName, uniquesIds);
uniquesIds++;
voxelTexture.imageFileDimensions = getImageSize(f);
voxelTexturesSortedBySize.add(voxelTexture);
totalSurfacedNeeded += voxelTexture.imageFileDimensions * voxelTexture.imageFileDimensions;
}
}
}
}
// Sort them by size
Collections.sort(voxelTexturesSortedBySize, new Comparator<VoxelTextureAtlased>() {
@Override
public int compare(VoxelTextureAtlased a, VoxelTextureAtlased b) {
return Integer.compare(b.imageFileDimensions, a.imageFileDimensions);
}
});
for (VoxelTextureAtlased voxelTexture : voxelTexturesSortedBySize) {
// System.out.println(vt.imageFileDimensions);
texMap.put(voxelTexture.getName(), voxelTexture);
}
// Estimates the required texture atlas size by surface
int sizeRequired = 16;
for (int i = 4; i < 14; i++) {
int iSize = (int) Math.pow(2, i);
if (iSize * iSize >= totalSurfacedNeeded) {
sizeRequired = iSize;
break;
}
}
// ChunkStoriesLogger.getInstance().info("At least " + sizeRequired + " by " + sizeRequired + " for TextureAtlas (surfacedNeeded : " + totalSurfacedNeeded + ")");
// Delete previous atlases
File diffuseTextureFile = new File(GameDirectory.getGameFolderPath() + "/cache/tiles_merged_albedo.png");
if (diffuseTextureFile.exists())
diffuseTextureFile.delete();
File normalTextureFile = new File(GameDirectory.getGameFolderPath() + "/cache/tiles_merged_normal.png");
if (normalTextureFile.exists())
normalTextureFile.delete();
File materialTextureFile = new File(GameDirectory.getGameFolderPath() + "/cache/tiles_merged_material.png");
if (materialTextureFile.exists())
materialTextureFile.delete();
// Build the new one
boolean loadedOK = false;
while (// Security to prevend
!loadedOK && sizeRequired <= 8192) // HUGE-ASS textures
{
// We need this
BLOCK_ATLAS_SIZE = sizeRequired;
BLOCK_ATLAS_FACTOR = 32768 / BLOCK_ATLAS_SIZE;
loadedOK = true;
// Create boolean bitfield
boolean[][] used = new boolean[sizeRequired / 16][sizeRequired / 16];
diffuseTextureImage = null;
normalTextureImage = null;
materialTextureImage = null;
if (content.getContext() instanceof ClientInterface) {
diffuseTextureImage = new BufferedImage(sizeRequired, sizeRequired, Transparency.TRANSLUCENT);
normalTextureImage = new BufferedImage(sizeRequired, sizeRequired, Transparency.TRANSLUCENT);
materialTextureImage = new BufferedImage(sizeRequired, sizeRequired, Transparency.TRANSLUCENT);
logger.debug("This is a client so we'll make the texture atlas");
}
BufferedImage imageBuffer;
for (VoxelTextureAtlased vt : voxelTexturesSortedBySize) {
// Find a free spot on the atlas
boolean foundSpot = false;
int spotX = 0, spotY = 0;
for (int a = 0; (a < sizeRequired / 16 && !foundSpot); a++) for (int b = 0; (b < sizeRequired / 16 && !foundSpot); b++) {
if (// Unused
used[a][b] == false && a + vt.imageFileDimensions / 16 <= sizeRequired / 16 && b + vt.imageFileDimensions / 16 <= sizeRequired / 16) {
boolean usedAlready = false;
// Not pretty loops that do clamped space checks
for (int i = 0; (i < vt.imageFileDimensions / 16 && a + i < sizeRequired / 16); i++) for (int j = 0; (j < vt.imageFileDimensions / 16 && b + j < sizeRequired / 16); j++) if (// Well
used[a + i][b + j] == true)
// fuck
// it
usedAlready = true;
if (!usedAlready) {
spotX = a * 16;
spotY = b * 16;
vt.setAtlasS(spotX * BLOCK_ATLAS_FACTOR);
vt.setAtlasT(spotY * BLOCK_ATLAS_FACTOR);
vt.setAtlasOffset(vt.imageFileDimensions * BLOCK_ATLAS_FACTOR);
foundSpot = true;
for (int i = 0; (i < vt.imageFileDimensions / 16 && a + i < sizeRequired / 16); i++) for (int j = 0; (j < vt.imageFileDimensions / 16 && b + j < sizeRequired / 16); j++) used[a + i][b + j] = true;
}
}
}
if (!foundSpot) {
System.out.println("Failed to find a space to place the texture in. Retrying with a larger atlas.");
loadedOK = false;
break;
}
imageBuffer = ImageIO.read(content.modsManager().getAsset("./voxels/textures/" + vt.getName() + ".png").read());
// imageBuffer = ImageIO.read(GameContent.getTextureFileLocation());
float alphaTotal = 0;
int nonNullPixels = 0;
Vector3f color = new Vector3f();
for (int x = 0; x < vt.imageFileDimensions; x++) {
for (int y = 0; y < vt.imageFileDimensions; y++) {
int rgb = imageBuffer.getRGB(x, y);
if (diffuseTextureImage != null)
diffuseTextureImage.setRGB(spotX + x, spotY + y, rgb);
float alpha = ((rgb & 0xFF000000) >>> 24) / 255f;
// System.out.println("a:"+alpha);
alphaTotal += alpha;
if (alpha > 0)
nonNullPixels++;
float red = ((rgb & 0xFF0000) >> 16) / 255f * alpha;
float green = ((rgb & 0x00FF00) >> 8) / 255f * alpha;
float blue = (rgb & 0x0000FF) / 255f * alpha;
color.add(new Vector3f(red, green, blue));
// Vector3f.add(color, new Vector3f(red, green, blue), color);
}
}
color.mul(1f / alphaTotal);
if (nonNullPixels > 0)
alphaTotal /= nonNullPixels;
vt.setColor(new Vector4f(color.x(), color.y(), color.z(), alphaTotal));
// Don't bother if it's not a Client context
if (diffuseTextureImage == null)
continue;
// Do also the normal maps !
Asset normalMap = content.modsManager().getAsset("./voxels/textures/normal/" + vt.getName() + ".png");
if (normalMap == null)
normalMap = content.modsManager().getAsset("./voxels/textures/normal/notex.png");
imageBuffer = ImageIO.read(normalMap.read());
for (int x = 0; x < vt.imageFileDimensions; x++) {
for (int y = 0; y < vt.imageFileDimensions; y++) {
int rgb = imageBuffer.getRGB(x % imageBuffer.getWidth(), y % imageBuffer.getHeight());
normalTextureImage.setRGB(spotX + x, spotY + y, rgb);
}
}
// And the materials !
Asset materialMap = content.modsManager().getAsset("./voxels/textures/material/" + vt.getName() + ".png");
if (materialMap == null)
materialMap = content.modsManager().getAsset("./voxels/textures/material/notex.png");
imageBuffer = ImageIO.read(materialMap.read());
for (int x = 0; x < vt.imageFileDimensions; x++) {
for (int y = 0; y < vt.imageFileDimensions; y++) {
int rgb = imageBuffer.getRGB(x % imageBuffer.getWidth(), y % imageBuffer.getHeight());
materialTextureImage.setRGB(spotX + x, spotY + y, rgb);
}
}
}
if (loadedOK && diffuseTextureImage != null) {
// save it son
ImageIO.write(diffuseTextureImage, "PNG", diffuseTextureFile);
ImageIO.write(normalTextureImage, "PNG", normalTextureFile);
ImageIO.write(materialTextureImage, "PNG", materialTextureFile);
diffuseTexture = null;
normalTexture = null;
materialTexture = null;
} else
// It's too small, initial estimation was wrong !
sizeRequired *= 2;
}
// Read textures metadata
// TODO read all overrides in priority
readTexturesMeta(content.modsManager().getAsset("./voxels/textures/meta.txt"));
} catch (Exception e) {
e.printStackTrace();
}
}
Aggregations