GlEngine/src/gl_engine/texture/TextureAtlas2D.java

339 lines
7.2 KiB
Java

package gl_engine.texture;
import static org.lwjgl.opengl.GL11.GL_NEAREST;
import static org.lwjgl.opengl.GL11.GL_RGBA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL11.glGenTextures;
import static org.lwjgl.opengl.GL11.glTexImage2D;
import static org.lwjgl.opengl.GL11.glTexParameteri;
import static org.lwjgl.opengl.GL30.glGenerateMipmap;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Comparator;
import gl_engine.ResourceLoader;
import gl_engine.range.Range2i;
import gl_engine.vec.Vec2i;
public class TextureAtlas2D
{
int texture;
public int width, height;
public ArrayList<TextureRaw> raw_textures;
public ArrayList<TextureRef2D> tex_refs;
public TextureAtlas2D()
{
raw_textures = new ArrayList<TextureRaw>();
tex_refs = new ArrayList<TextureRef2D>();
raw_textures.add(TextureRaw.createEmpty());
width = 0;
height = 0;
}
public void add(TextureRaw tex) {
raw_textures.add(tex);
}
public void clear() {
raw_textures.clear();
raw_textures.add(TextureRaw.createEmpty());
}
private static String getParentPath(String path)
{
int at = 0;
byte[] path_b = path.getBytes();
for(int i=0;i<path_b.length;i++) {
if(path_b[i] == '/') {
at = i;
}
}
return path.substring(0, at + 1);
}
public static TextureAtlas2D loadAll(String path)
{
String path_parent = getParentPath(path);
String[] tree = new String(ResourceLoader.loadResource(path)).split("\n");
TextureAtlas2D texmap = new TextureAtlas2D();
for(String dir : tree) {
if(dir.endsWith(".png"))
{
while(dir.startsWith("./")) {
dir = dir.substring(2, dir.length());
}
TextureRaw tex = TextureRaw.loadFromFile(path_parent + dir);
if(tex == null) {
continue;
}
tex.ref = "/" + dir;
texmap.add(tex);
}
}
return texmap;
}
public TextureRef2D get(String ref)
{
for(TextureRef2D tex_ref : tex_refs) {
if(tex_ref.ref.contentEquals(ref)) {
return tex_ref;
}
}
for(TextureRef2D tex_ref : tex_refs) {
if(tex_ref.ref.contentEquals("EMPTY")) {
return get("EMPTY");
}
}
System.err.println("Error: atlas not generated");
System.exit(0);
return null;
}
private boolean refHasCollision(TextureRect check, TextureRect other)
{
if(check.y >= other.y + other.h || other.y >= check.y + check.h) {
return false;
}
if(check.x >= other.x + other.w || other.x >= check.x + check.w) {
return false;
}
return true;
}
private TextureRect refHasCollision(ArrayList<TextureRect> refs, TextureRect other)
{
for(TextureRect ref : refs) {
if(refHasCollision(ref, other)) {
return ref;
}
}
return null;
}
public void bind() {
glBindTexture(GL_TEXTURE_2D, texture);
}
public void generate()
{
tex_refs.clear();
int size = 1;
ArrayList<TextureRect> tex_rects = new ArrayList<TextureRect>();
TextureRect lastRect = null;
raw_textures.sort(new Comparator<TextureRaw>() {
@Override
public int compare(TextureRaw o1, TextureRaw o2)
{
if(o1.height < o2.height) {
return 1;
}
if(o1.height > o2.height) {
return -1;
}
if(o1.width > o2.width) {
return 1;
}
if(o1.width < o2.width) {
return -1;
}
return 0;
}
});
long millis_now = System.currentTimeMillis();
long millis_last = millis_now;
// Work out the positions of each rect
for(int i=0;i<raw_textures.size();i++)
{
millis_now = System.currentTimeMillis();
if(millis_now - millis_last > 1000) {
millis_last += 1000;
System.out.println("Generating atlas " + i + "/" + raw_textures.size());
}
TextureRaw tex = raw_textures.get(i);
TextureRect rect = new TextureRect();
rect.r = tex;
rect.w = tex.width;
rect.h = tex.height;
rect.x = 0;
rect.y = 0;
if(lastRect != null) {
if(lastRect.h <= rect.h && lastRect.w <= rect.w) {
rect.x = lastRect.x;
rect.y = lastRect.y;
}
}
while(true)
{
TextureRect collision = refHasCollision(tex_rects, rect);
if(collision == null) {
break;
}
int change = collision.x + collision.w;
if(change != rect.x) {
rect.x = change;
} else {
rect.x += 1;
}
if(rect.x + rect.w > size) {
rect.y += 1;
rect.x = 0;
}
if(rect.y + rect.h > size) {
size *= 2;
rect.x = 0;
rect.y = 0;
}
}
TextureRef2D ref = new TextureRef2D();
ref.texmap = this;
ref.ref = tex.ref;
ref.sx = rect.x;
ref.sy = rect.y;
ref.ex = rect.x + rect.w;
ref.ey = rect.y + rect.h;
lastRect = rect;
tex_rects.add(rect);
tex_refs.add(ref);
if(rect.h > height) {
height = rect.h;
}
}
for(TextureRef2D ref : tex_refs) {
ref.sx /= size;
ref.ex /= size;
ref.sy /= size;
ref.ey /= size;
}
width = size;
height = size;
byte[] texmap_b = new byte[width*height*4];
Range2i texmap_r = new Range2i(width, height);
TextureRect last_rect = null;
for(int i=0;i<width*height;i++)
{
Vec2i pos = Vec2i.fromId(texmap_r, i);
TextureRect r = null;
boolean found = false;
if(millis_now - millis_last > 1000) {
millis_last += 1000;
System.out.println("Rendering atlas " + ((int)(i / ((double)width*height) * 1000)) / (double)10 + "%");
}
if(
last_rect != null &&
pos.x >= last_rect.x && pos.x < last_rect.x + last_rect.w &&
pos.y >= last_rect.y && pos.y < last_rect.y + last_rect.h
) {
found = true;
r = last_rect;
}
if(!found)
{
//System.out.println("hi");
for(TextureRect rect : tex_rects) {
if(
pos.x >= rect.x && pos.x < rect.x + rect.w &&
pos.y >= rect.y && pos.y < rect.y + rect.h
) {
r = rect;
last_rect = rect;
found = true;
break;
}
}
}
if(r == null) {
texmap_b[i*4+0] = 0;
texmap_b[i*4+1] = 0;
texmap_b[i*4+2] = 0;
texmap_b[i*4+3] = 0;
continue;
}
Range2i tex_r = new Range2i(r.w, r.h);
Vec2i tex_p = new Vec2i(pos.x - r.x, pos.y - r.y);
int tex_id = tex_p.getId(tex_r);
if(tex_p.x == r.w - 1 && tex_p.y == r.h - 1) {
tex_rects.remove(r);
}
texmap_b[i*4+0] = r.r.bytes[tex_id*4+0];
texmap_b[i*4+1] = r.r.bytes[tex_id*4+1];
texmap_b[i*4+2] = r.r.bytes[tex_id*4+2];
texmap_b[i*4+3] = r.r.bytes[tex_id*4+3];
millis_now = System.currentTimeMillis();
}
//System.out.println(Arrays.toString(texmap_b));
System.out.println("Atlas size: "+size+" x "+size);
ByteBuffer texmap_buff = ByteBuffer.allocateDirect(texmap_b.length);
texmap_buff.put(texmap_b);
texmap_buff.position(0);
texture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texmap_buff);
glGenerateMipmap(GL_TEXTURE_2D);
raw_textures.clear();
}
}