package com.vitco.export.generic.container;
import com.vitco.layout.content.console.ConsoleInterface;
import com.vitco.util.components.progressbar.ProgressDialog;
import com.vitco.util.components.progressbar.ProgressReporter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
/**
* Manages a list of textures and implements compression techniques for the textures.
*/
public class TriTextureManager extends ProgressReporter {
// holds list of textures
private final ArrayList<TriTexture> textures = new ArrayList<TriTexture>();
// list of unique TriTextures (by id)
private final HashMap<Integer, TriTexture> uniqueTextures = new HashMap<Integer, TriTexture>();
// maps textures to their corresponding id
private final HashMap<TriTexture, Integer> textureIds = new HashMap<TriTexture, Integer>();
// true if the texture list is outdated
private boolean outdated = false;
// constructor
public TriTextureManager(ProgressDialog dialog, ConsoleInterface console) {
super(dialog, console);
}
// invalidate the texture list
private void invalidate() {
outdated = true;
}
// validate the texture list
private void validate() {
if (outdated) {
outdated = false;
// order by parent/not parent
Collections.sort(textures, new Comparator<TriTexture>() {
@Override
public int compare(TriTexture o1, TriTexture o2) {
return (o1.hasParent() ? 1 : 0) - (o2.hasParent() ? 1 : 0);
}
});
// regenerate texture id list
textureIds.clear();
int i = 0;
for (TriTexture tex : textures) {
textureIds.put(tex, i++);
}
// ===========================
// We need to obtain the id here since it might come from a parent texture.
// Hence this must be strictly separated from the id generation above (!)
uniqueTextures.clear();
for (TriTexture tex : textures) {
uniqueTextures.put(tex.getId(), tex);
}
}
}
// retrieve the id for a texture
public final int getId(TriTexture triTexture) {
validate();
return textureIds.get(triTexture);
}
// #########################
// add a texture to this manager
public final void addTexture(TriTexture triTexture) {
textures.add(triTexture);
invalidate();
}
// get a texture with a certain id
public final TriTexture getTexture(int id) {
validate();
return uniqueTextures.get(id);
}
// ########################
// pad textures to the next power of two size
public final void resizeToPowOfTwo() {
for (TriTexture tex : new ArrayList<TriTexture>(this.textures)) {
if (!tex.hasParent()) {
addTexture(new TriTexture(tex, this));
}
}
}
// combine the textures in this manager (but don't overlap uvs)
public final void combineNonOverlapping() {
// create dummy list that we can delete from
ArrayList<TriTexture> textures = new ArrayList<TriTexture>(this.textures);
// sort by pixel used (largest first)
Collections.sort(textures, new Comparator<TriTexture>() {
@Override
public int compare(TriTexture o1, TriTexture o2) {
return o2.getPixelCount() - o1.getPixelCount();
}
});
setActivity("Combining Textures...", false);
// generate the new TriTexture
TriTexture main = new TriTexture(textures, new TriTexture.TickAction() {
@Override
void onTick(int current, int target) {
setProgress((float)current/target * 100);
}
}, this);
// register texture
this.addTexture(main);
// invalidate texture list (for id generation)
invalidate();
}
// combine the textures in this manager
public final void combine() {
// -- find textures that are "inside" other textures
// create dummy list that we can delete from
ArrayList<TriTexture> textures = new ArrayList<TriTexture>(this.textures);
// sort by size (smallest first) - this makes processing a bit faster
Collections.sort(textures, new Comparator<TriTexture>() {
@Override
public int compare(TriTexture o1, TriTexture o2) {
return Math.max(o1.width, o1.height) - Math.max(o2.width, o2.height);
}
});
int len = textures.size();
int originalLength = len;
setActivity("Merging Textures...", false);
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
if (len%100 == 0) {setProgress(((originalLength - len - i)/(float)originalLength)*100);}
TriTexture tex1 = textures.get(i);
TriTexture tex2 = textures.get(j);
if (tex1.makeChild(tex2, null)) {
textures.remove(j);
j--;
len--;
} else if (tex2.makeChild(tex1, null)) {
textures.remove(i);
i--;
len--;
break;
}
}
}
//System.out.println("Obtained " + textures.size() + " unique textures after merging.");
// sort by size (largest first) - better combining having the "smaller ones in the back"
Collections.sort(textures, new Comparator<TriTexture>() {
@Override
public int compare(TriTexture o1, TriTexture o2) {
return Math.max(o2.width, o2.height) - Math.max(o1.width, o1.height);
}
});
// -- combine remaining "parent" textures into one image
int lengthBeforeCombining = len;
setActivity("Combining Textures...", false);
while (len > 1) {
setProgress(((lengthBeforeCombining - len + 1)/(float)lengthBeforeCombining) * 100);
// find the texture with the biggest jaccard similarity
TriTexture texture = textures.get(0);
TriTexture mergeTo = textures.get(1);
int mergeToId = 1;
float similarity = texture.jaccard(mergeTo);
for (int i = 2; i < len; i++) {
TriTexture compareTo = textures.get(i);
float newSim = texture.jaccard(compareTo);
// note: this gets the largest most similar neighbour
if (newSim > similarity) {
similarity = newSim;
mergeTo = compareTo;
mergeToId = i;
}
}
// check if we can make this a child
// otherwise we combine the textures
// Note: this can succeed b/c once merged textures might allow new children
if (texture.makeChild(mergeTo, null)) {
textures.remove(mergeToId);
} else if (mergeTo.makeChild(texture, null)) {
textures.remove(0); // can this succeed? probably not
} else {
// generate the new TriTexture
TriTexture parentTexture = new TriTexture(texture, mergeTo, this);
// remove textures
textures.remove(mergeToId);
textures.remove(0);
// add new parent to front
textures.add(0, parentTexture);
// register texture
this.addTexture(parentTexture);
}
len--;
}
// invalidate texture list (for id generation)
invalidate();
//System.out.println("Pixel Count: " + textures.get(0).getPixelCount());
}
// update uv maps
public final void validateUVMappings() {
for (TriTexture texture : textures) {
texture.validateUVMapping();
}
}
}