340 lines
7.3 KiB
Java
340 lines
7.3 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.Buffer;
|
|
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);
|
|
((Buffer)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();
|
|
}
|
|
}
|