package openmods.geometry;
import com.google.common.collect.Maps;
import java.util.EnumMap;
import java.util.Map;
import net.minecraftforge.common.util.ForgeDirection;
public class BlockTextureTransform {
public static class TexCoords {
public final double u;
public final double v;
public final double h;
public TexCoords(double u, double v, double h) {
this.u = u;
this.v = v;
this.h = h;
}
}
public static class WorldCoords {
public final double x;
public final double y;
public final double z;
public WorldCoords(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
private static final Matrix2d MIRROR_U = Matrix2d.createMirrorX();
private static final Matrix2d MIRROR_V = Matrix2d.createMirrorY();
private static final Matrix2d MIRROR_UV = Matrix2d.createMirrorXY();
private static final Matrix2d ROTATE_CW = Matrix2d.createRotateCW();
private static final Matrix2d ROTATE_CCW = Matrix2d.createRotateCCW();
private static final Matrix2d SWAP = Matrix2d.createSwap();
public static class Builder {
private final Map<ForgeDirection, Matrix2d> transforms = Maps.newEnumMap(ForgeDirection.class);
private Builder() {
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS)
transforms.put(dir, Matrix2d.createIdentity());
}
private Builder(Map<ForgeDirection, Matrix2d> transforms) {
for (Map.Entry<ForgeDirection, Matrix2d> e : transforms.entrySet())
this.transforms.put(e.getKey(), e.getValue().copy());
}
public BlockTextureTransform build() {
final EnumMap<ForgeDirection, Matrix2d> inverseTransforms = Maps.newEnumMap(transforms);
for (Map.Entry<ForgeDirection, Matrix2d> e : inverseTransforms.entrySet())
e.setValue(e.getValue().invertCopy());
return new BlockTextureTransform(Maps.newEnumMap(transforms), inverseTransforms);
}
public Builder mirrorU(ForgeDirection side) {
transforms.get(side).mulRight(MIRROR_U);
return this;
}
public Builder mirrorV(ForgeDirection side) {
transforms.get(side).mulRight(MIRROR_V);
return this;
}
public Builder mirrorUV(ForgeDirection side) {
transforms.get(side).mulRight(MIRROR_UV);
return this;
}
public Builder rotateCW(ForgeDirection side) {
transforms.get(side).mulRight(ROTATE_CW);
return this;
}
public Builder rotateCCW(ForgeDirection side) {
transforms.get(side).mulRight(ROTATE_CCW);
return this;
}
public Builder swapUV(ForgeDirection side) {
transforms.get(side).mulRight(SWAP);
return this;
}
}
private final Map<ForgeDirection, Matrix2d> transforms;
private final Map<ForgeDirection, Matrix2d> inverseTransforms;
private BlockTextureTransform(Map<ForgeDirection, Matrix2d> transforms, Map<ForgeDirection, Matrix2d> inverseTransforms) {
this.transforms = transforms;
this.inverseTransforms = inverseTransforms;
}
public TexCoords worldVecToTextureCoords(ForgeDirection side, double x, double y, double z) {
final double wallX;
final double wallY;
final double h;
// positive h always points "outside" block
switch (side) {
case UP:
wallX = x;
wallY = z;
h = y - 1;
break;
case DOWN:
wallX = x;
wallY = z;
h = -y;
break;
case EAST:
wallX = z;
wallY = 1 - y;
h = x - 1;
break;
case WEST:
wallX = z;
wallY = 1 - y;
h = -x;
break;
case NORTH:
wallX = x;
wallY = 1 - y;
h = -z;
break;
case SOUTH:
wallX = x;
wallY = 1 - y;
h = z - 1;
break;
default:
throw new IllegalArgumentException(side.toString());
}
final Matrix2d transformation = transforms.get(side);
final double u = transformation.transformX(wallX - 0.5, wallY - 0.5) + 0.5;
final double v = transformation.transformY(wallX - 0.5, wallY - 0.5) + 0.5;
return new TexCoords(u, v, h);
}
public WorldCoords textureCoordsToWorldVec(ForgeDirection side, double u, double v, double h) {
final Matrix2d transformation = inverseTransforms.get(side);
if (transformation == null) throw new IllegalArgumentException(side.toString());
final double wallX = transformation.transformX(u - 0.5, v - 0.5) + 0.5;
final double wallY = transformation.transformY(u - 0.5, v - 0.5) + 0.5;
final double globalX;
final double globalY;
final double globalZ;
switch (side) {
case UP:
globalX = wallX;
globalY = h + 1;
globalZ = wallY;
break;
case DOWN:
globalX = wallX;
globalY = -h;
globalZ = wallY;
break;
case EAST:
globalX = h + 1;
globalY = 1 - wallY;
globalZ = wallX;
break;
case WEST:
globalX = -h;
globalY = 1 - wallY;
globalZ = wallX;
break;
case NORTH:
globalX = wallX;
globalY = 1 - wallY;
globalZ = -h;
break;
case SOUTH:
globalX = wallX;
globalY = 1 - wallY;
globalZ = h + 1;
break;
default:
throw new IllegalArgumentException(side.toString());
}
return new WorldCoords(globalX, globalY, globalZ);
}
public static Builder builder() {
return new Builder();
}
public Builder builderFromThis() {
return new Builder(transforms);
}
}