Hi,
I've recently started modding and was looking for a tool to properly generate a texture map for mobs.
Fortunately notch already wrote such a tool (http://pastebin.com/0AcY6YV2).
However this does not work with forge so I changed the necessary variables to make it work.
To use it simply pass a "new ModelMob().parts" in the main class and wait for the results.
You will need to put all of your boxes into a list called parts (protected ArrayList<ModelRenderer> parts = new ArrayList<ModelRenderer>(); ).
Here's an example mob (only the constructor and the list):
protected ArrayList<ModelRenderer> parts = new ArrayList<ModelRenderer>();
ModelMob()
{
textureWidth = 128; //change this to the same size as in ImageGenerator (WIDTH)
textureHeight = 64;
setTextureOffset("null.part1", 0, 0); // important (needs to be called with any offset for the first time, otherwise you will get a NullPtr exception)
setTextureOffset("null.part2", 0, 0); // change it to the output of ImageGenerator afterwards
ModelRenderer part1 = new ModelRenderer(this, 0, 0);
parts.add(part1);
part1.addBox("part1",-8F, -8F, 0F, 16, 16, 32);
//some other code
ModelRenderer part2 = new ModelRenderer(this, 0, 0);
parts.add(part2);
part2.addBox("part2",-0.5F, 0F, 0F, 1, 8, ;
//some other code
}
Finally here is the actual code:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.imageio.ImageIO;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
/*
* Generates a texture map for a specified model
* modified from: http://pastebin.com/0AcY6YV2
*/
public class ImageGenerator {
private static final int WIDTH = 256;
private static final int HEIGHT = 256;
private class Region {
public int x1, y1, x2, y2;
public Region(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public void move(int xo, int yo) {
x1 += xo;
y1 += yo;
x2 += xo;
y2 += yo;
}
public boolean overlaps(Region r2, int xo, int yo) {
if (r2.x2 + xo <= x1) return false;
if (r2.y2 + yo <= y1) return false;
if (r2.x1 + xo >= x2) return false;
if (r2.y1 + yo >= y2) return false;
return true;
}
public void render(Graphics g) {
g.fillRect(x1, y1, x2 - x1, y2 - y1);
}
}
private class UnfoldedCube {
public int x, y;
public Region[] cubeRegions = new Region[6];
public String id;
public int width, height;
public UnfoldedCube(ModelBox cube) {
int xs = (int) Math.ceil(cube.posX2 - cube.posX1);
int ys = (int) Math.ceil(cube.posY2 - cube.posY1);
int zs = (int) Math.ceil(cube.posZ2 - cube.posZ1);
width = xs * 2 + zs * 2;
height = zs + ys;
id = cube.field_78247_g;
cubeRegions[0] = new Region(0, zs, zs, zs + ys);
cubeRegions[1] = new Region(zs, zs, zs + xs, zs + ys);
cubeRegions[2] = new Region(zs + xs, zs, zs + xs + zs, zs + ys);
cubeRegions[3] = new Region(zs + xs + zs, zs, zs + xs + zs + xs, zs + ys);
cubeRegions[4] = new Region(zs, 0, zs + xs, zs);
cubeRegions[5] = new Region(zs + xs, 0, zs + xs * 2, zs);
}
public boolean fits(int x, int y) {
for (int i = 0; i < 6; i++) {
if (!isFree(cubeRegions[i], x, y)) return false;
}
return true;
}
public void place(int x, int y) {
this.x = x;
this.y = y;
for (int i = 0; i < 6; i++) {
cubeRegions[i].move(x, y);
occupiedRegions.add(cubeRegions[i]);
}
}
public void render(Graphics g) {
float hue = (float) Math.random();
g.setColor(new Color(Color.HSBtoRGB(hue, 1, 0.7f)));
cubeRegions[5].render(g);
g.setColor(new Color(Color.HSBtoRGB(hue, 1, 1.0f)));
cubeRegions[4].render(g);
g.setColor(new Color(Color.HSBtoRGB(hue, 1, 0.9f)));
cubeRegions[1].render(g);
cubeRegions[3].render(g);
g.setColor(new Color(Color.HSBtoRGB(hue, 1, 0.8f)));
cubeRegions[0].render(g);
cubeRegions[2].render(g);
}
}
private List<Region> occupiedRegions = new ArrayList<Region>();
private boolean isFree(Region region, int xo, int yo) {
if (region.x1 + xo < 0) return false;
if (region.y1 + yo < 0) return false;
if (region.x2 + xo > WIDTH) return false;
if (region.y2 + yo > HEIGHT) return false;
for (int i = 0; i < occupiedRegions.size(); i++) {
if (occupiedRegions.get(i).overlaps(region, xo, yo)) return false;
}
return true;
}
public void generateImage(List<ModelRenderer> parts) {
List<ModelBox> cubes = new ArrayList<ModelBox>();
for (ModelRenderer mp : parts) {
getCubes(mp, cubes);
}
Collections.sort(cubes, new Comparator<ModelBox>() {
@Override
public int compare(ModelBox c0, ModelBox c1) {
int xs = (int) Math.ceil(c0.posX2 - c0.posX1);
int ys = (int) Math.ceil(c0.posY2 - c0.posY1);
int zs = (int) Math.ceil(c0.posZ2 - c0.posZ1);
int ww0 = xs * 2 + zs * 2;
int hh0 = zs + ys;
xs = (int) Math.ceil(c1.posX2 - c1.posX1);
ys = (int) Math.ceil(c1.posY2 - c1.posY1);
zs = (int) Math.ceil(c1.posZ2 - c1.posZ1);
int ww1 = xs * 2 + zs * 2;
int hh1 = zs + ys;
if (ww0 * hh0 < ww1 * hh1) {
return 1;
} else if (ww0 * hh0 > ww1 * hh1) {
return -1;
}
return 0;
}
});
List<UnfoldedCube> unfolded = new ArrayList<UnfoldedCube>();
for (ModelBox cube : cubes) {
UnfoldedCube uc = new UnfoldedCube(cube);
boolean placed = false;
positionLoop: for (int y = 0; y < HEIGHT - uc.height; y++) {
for (int x = 0; x < WIDTH - uc.width; x++) {
if (uc.fits(x, y)) {
uc.place(x, y);
placed = true;
break positionLoop;
}
}
}
if (!placed) {
System.out.println("Failed to place " + uc.id);
} else {
unfolded.add(uc);
}
}
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
System.out.println("----------------");
for (UnfoldedCube uc : unfolded) {
System.out.println("setTextureOffset(\"" + uc.id + "\", " + uc.x + ", " + uc.y + ");");
uc.render(g);
}
try {
ImageIO.write(img, "png", new File("output.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void getCubes(ModelRenderer mp, List<ModelBox> cubeList) {
addLoop: for (ModelBox cube : (List<ModelBox>)mp.cubeList) {
if (cube.field_78247_g != null) {
for (ModelBox otherCube : cubeList) {
if (otherCube.field_78247_g != null) {
if (otherCube.field_78247_g.equals(cube.field_78247_g)) {
System.out.println("Duplicate " + cube.field_78247_g);
continue addLoop;
}
}
}
}
cubeList.add(cube);
}
}
public static void main(String[] args) {
new ImageGenerator().generateImage(new ModelMob().parts );
}
}
I hope it helps.