resonant-induction/src/main/java/resonantinduction/wire/RenderFlatWire.java
2013-12-26 22:55:47 +08:00

473 lines
13 KiB
Java

package resonantinduction.wire;
import java.util.Arrays;
import java.util.LinkedList;
import net.minecraft.util.Icon;
import resonantinduction.wire.part.PartFlatWire;
import codechicken.lib.lighting.LightModel;
import codechicken.lib.math.MathHelper;
import codechicken.lib.render.CCModel;
import codechicken.lib.render.ColourModifier;
import codechicken.lib.render.ColourMultiplier;
import codechicken.lib.render.IUVTransformation;
import codechicken.lib.render.IVertexModifier;
import codechicken.lib.render.IconTransformation;
import codechicken.lib.render.RenderUtils;
import codechicken.lib.render.UV;
import codechicken.lib.render.UVScale;
import codechicken.lib.render.UVTranslation;
import codechicken.lib.render.Vertex5;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Transformation;
import codechicken.lib.vec.Translation;
import codechicken.lib.vec.Vector3;
public class RenderFlatWire
{
public static Icon flatWireTexture;
public static class UVT implements IUVTransformation
{
public Transformation t;
private Vector3 vec = new Vector3();
public UVT(Transformation t)
{
this.t = t;
}
@Override
public void transform(UV uv)
{
vec.set(uv.u, 0, uv.v).apply(t);
uv.set(vec.x, vec.z);
}
}
public static int[] reorientSide = new int[] { 0, 3, 3, 0, 0, 3 };
/*
* All generations are done on side 0 so know that for rotation r
* 0 = side 3 = +Z = SOUTH
* 1 = side 4 = -X = WEST
* 2 = side 2 = -Z = NORTH
* 3 = side 5 = +X = EAST
*/
private static class WireModelGenerator
{
int side;
int tw;
int th;
double w;
double h;
int mask;
int connMask;
int connCount;
CCModel model;
int i = 0;
boolean inv;
public static int countConnections(int connMask)
{
int n = 0;
for (int r = 0; r < 4; r++)
if ((connMask & 1 << r) != 0)
n += 1;
return n;
}
public int numFaces()
{
if (inv)
return 22;
int conns;
if (connCount <= 2)
conns = 2;
else
conns = connCount;
int faces = conns * 3 + 5;
for (int i = 0; i < 4; i++)
if ((mask >> i & 0x11) == 1)
faces++;
return faces;
}
public CCModel generateInvModel(int thickness)
{
return generateModel(modelKey(0, thickness, 0xF0), true);
}
public CCModel generateModel(int key, boolean inv)
{
this.inv = inv;
side = (key >> 8) % 6;
tw = (key >> 8) / 6 + 1;
w = tw / 16D;
th = tw + 1;
h = th / 16D;
mask = key & 0xFF;
connMask = (mask & 0xF0) >> 4 | mask & 0xF;
connCount = countConnections(connMask);
model = CCModel.quadModel(numFaces() * 4);
i = 0;
generateCenter();
for (int r = 0; r < 4; r++)
generateSide(r);
model.apply(Rotation.sideOrientation(side, 0).at(Vector3.center));
return finishModel(model);
}
private void generateSide(int r)
{
int type = mask >> r & 0x11;
Vertex5[] verts;
if (inv)
verts = generateSideInv(r);
else if (connCount == 0)
if (r % 2 == 1)
verts = generateStub(r);
else
verts = generateFlat(r);
else if (connCount == 1)
if (connMask == 1 << (r + 2) % 4)// this side is opposite the one with a connection
verts = generateStub(r);
else
verts = generateSideFromType(type, r);
else
verts = generateSideFromType(type, r);
Transformation t = Rotation.quarterRotations[r].at(Vector3.center);
for (Vertex5 vert : verts)
vert.apply(t);
i = addVerts(model, verts, i);
}
private Vertex5[] generateSideInv(int r)
{
return withBottom(generateStraight(r), 4, 4);
}
private Vertex5[] generateSideFromType(int type, int r)
{
if (type == 0x00)
return generateFlat(r);
else if (type == 0x01)
return generateCorner(r);
else if (type == 0x10)
return generateStraight(r);
else
return generateInternal(r);
}
private Vertex5[] generateFlat(int r)
{
Vertex5[] verts = new Vertex5[] { new Vertex5(0.5 - w, 0, 0.5 + w, 16, 16 + tw), new Vertex5(0.5 + w, 0, 0.5 + w, 16, 16 - tw), new Vertex5(0.5 + w, h, 0.5 + w, 16 - th, 16 - tw), new Vertex5(0.5 - w, h, 0.5 + w, 16 - th, 16 + tw) };
if (Rotation.rotateSide(side, r) % 2 == 0) // red is on the negative side
{
UVT uvt = new UVT(Rotation.quarterRotations[2].at(new Vector3(8, 0, 16)));
for (Vertex5 vert : verts)
vert.apply(uvt);
}
return verts;
}
private Vertex5[] generateStub(int r)
{
Vertex5[] verts = generateExtension(4);
for (int i = 0; i < 4; i++)
verts[i].vec.z -= 0.002;// pull the stub in a little so it
// doesn't z fight with jacketed cables
reflectSide(verts, r);
return verts;
}
private Vertex5[] generateStraight(int r)
{
Vertex5[] verts = generateExtension(8);
reflectSide(verts, r);
return verts;
}
private Vertex5[] generateCorner(int r)
{
Vertex5[] verts = generateExtension(8 + th);
// retexture cap
for (int i = 0; i < 4; i++)
verts[i].apply(new UVTranslation(0, -th));
// add end face extending around block
verts = Arrays.copyOf(verts, 20);
verts[16] = new Vertex5(0.5 - w, 0, 1, 8 - tw, 24 + 2 * th);
verts[17] = new Vertex5(0.5 + w, 0, 1, 8 + tw, 24 + 2 * th);
verts[18] = new Vertex5(0.5 + w, 0, 1 + h, 8 + tw, 24 + th);
verts[19] = new Vertex5(0.5 - w, 0, 1 + h, 8 - tw, 24 + th);
reflectSide(verts, r);
return verts;
}
private Vertex5[] generateInternal(int r)
{
Vertex5[] verts = generateExtension(8);
// retexture cap
verts[0].uv.set(8 + tw, 24);
verts[1].uv.set(8 - tw, 24);
verts[2].uv.set(8 - tw, 24 + tw);
verts[3].uv.set(8 + tw, 24 + tw);
reflectSide(verts, r);
// offset side textures
for (int i = 4; i < 16; i++)
verts[i].apply(new UVTranslation(16, 0));
return verts;
}
private Vertex5[] generateExtension(int tl)
{
double l = tl / 16D;
return new Vertex5[] {// cap
new Vertex5(0.5 - w, 0, 0.5 + l, 8 - tw, 24 + 2 * th), new Vertex5(0.5 + w, 0, 0.5 + l, 8 + tw, 24 + 2 * th), new Vertex5(0.5 + w, h, 0.5 + l, 8 + tw, 24 + th), new Vertex5(0.5 - w, h, 0.5 + l, 8 - tw, 24 + th),
// top
new Vertex5(0.5 - w, h, 0.5 + l, 8 - tw, 16 + tl), new Vertex5(0.5 + w, h, 0.5 + l, 8 + tw, 16 + tl), new Vertex5(0.5 + w, h, 0.5 + w, 8 + tw, 16 + tw), new Vertex5(0.5 - w, h, 0.5 + w, 8 - tw, 16 + tw),
// left
new Vertex5(0.5 - w, 0, 0.5 + w, 0, 16 + tw), new Vertex5(0.5 - w, 0, 0.5 + l, 0, 16 + tl), new Vertex5(0.5 - w, h, 0.5 + l, th, 16 + tl), new Vertex5(0.5 - w, h, 0.5 + w, th, 16 + tw),
// right
new Vertex5(0.5 + w, 0, 0.5 + l, 16, 16 + tl), new Vertex5(0.5 + w, 0, 0.5 + w, 16, 16 + tw), new Vertex5(0.5 + w, h, 0.5 + w, 16 - th, 16 + tw), new Vertex5(0.5 + w, h, 0.5 + l, 16 - th, 16 + tl) };
}
private void generateCenter()
{
int tex;// 0 = straight n/s, 1 = straight e/w, 2 = circle
if (connCount == 0)
tex = 1;
else if (connCount == 1)
tex = (connMask & 5) != 0 ? 0 : 1;// if there is one connection, and it is
// north/south then north/south, otherwise
// east/west
else if (connMask == 5)
tex = 0;
else if (connMask == 10)
tex = 1;
else
tex = 2;
Vertex5[] verts = new Vertex5[] { new Vertex5(0.5 - w, h, 0.5 + w, 8 - tw, 16 + tw), new Vertex5(0.5 + w, h, 0.5 + w, 8 + tw, 16 + tw), new Vertex5(0.5 + w, h, 0.5 - w, 8 + tw, 16 - tw), new Vertex5(0.5 - w, h, 0.5 - w, 8 - tw, 16 - tw) };
if (tex == 0 || tex == 1)
tex = (tex + reorientSide[side]) % 2;
int r = reorientSide[side];
if (tex == 1)
r += 3;
if (r != 0)
{
IUVTransformation uvt = new UVT(Rotation.quarterRotations[r % 4].at(new Vector3(8, 0, 16)));
for (Vertex5 vert : verts)
vert.apply(uvt);
}
if (tex == 2)
{// circle (translate across to u = 24)
UVTranslation uvt = new UVTranslation(16, 0);
for (Vertex5 vert : verts)
vert.apply(uvt);
}
if (inv)
verts = withBottom(verts, 0, 4);
i = addVerts(model, verts, i);
}
private static UVT sideReflect = new UVT(Rotation.quarterRotations[2].at(new Vector3(8, 0, 16)));
private void reflectSide(Vertex5[] verts, int r)
{
if ((r + reorientSide[side]) % 4 >= 2)// rotate the texture about
// the y center
for (Vertex5 vert : verts)
vert.apply(sideReflect);
}
/**
* Returns a copy of vertices with the bottom face added at the start.
*
* @param start The index of the first vertex making up the top face
* @param count The number of vertices making up the top face
*/
private Vertex5[] withBottom(Vertex5[] verts, int start, int count)
{
Vertex5[] i_verts = new Vertex5[verts.length + count];
// add the bottom face, just a copy of the top, rotated about the z
// axis
Transformation r = new Rotation(MathHelper.pi, 0, 0, 1).at(new Vector3(0.5, h / 2, 0));
for (int i = 0; i < count; i++)
i_verts[i] = verts[i + start].copy().apply(r);
System.arraycopy(verts, 0, i_verts, count, verts.length);
return i_verts;
}
}
/**
* Array of all built models. These will be generated on demand.
*/
public static CCModel[] wireModels = new CCModel[3 * 6 * 256];
public static CCModel[] invModels = new CCModel[3];
private static WireModelGenerator gen_inst = new WireModelGenerator();
/**
* Puts verts into model m starting at index k
*/
public static int addVerts(CCModel m, Vertex5[] verts, int k)
{
for (int i = 0; i < verts.length; i++)
m.verts[k + i] = verts[i];
return k + verts.length;
}
public static CCModel finishModel(CCModel m)
{
m.apply(new UVScale(1 / 32D));
m.shrinkUVs(0.0005);
m.computeNormals();
m.computeLighting(LightModel.standardLightModel);
return m;
}
/**
* Returns a tightly packed unique index for the specific model represented
* by this wire. The mask is split into 3 sections the combination of
* corresponding bits from the two lowest nybbles gives the connection type
* in that direction.
* 00 = none
* 01 = corner
* 10 = straight
* 11 = internal The
* second byte contains the thickness*6+side
*
* @param side The side the wire is attached to
* @param thickness The thickness of the wire -1 in 1/8th blocks. Supported
* values 0, 1, 2
* @param connMap The connection mask of the wire
*/
public static int modelKey(int side, int thickness, int connMap)
{
int key = connMap & 0xFF;// take the straight and corner connections
int renderCorner = connMap >> 20 & 0xF;
key |= (renderCorner ^ key & 0xF) << 4;// any corner connections that aren't rendered
// convert to straight
key &= ~0xF | renderCorner;// set corners to renderCorners
int internal = (connMap & 0xF00) >> 8;// internal connections
key |= internal << 4 | internal;// if internal is set, set both straight and corner to 1
key |= side + thickness * 6 << 8;// add side and thickness
return key;
}
public static int modelKey(PartFlatWire w)
{
return modelKey(w.side, w.getThickness(), w.connMap);
}
public static CCModel getOrGenerateModel(int key)
{
CCModel m = wireModels[key];
if (m == null)
wireModels[key] = m = gen_inst.generateModel(key, false);
return m;
}
public static void render(PartFlatWire w, Vector3 pos)
{
IVertexModifier m = w.getColour().pack() == -1 ? ColourModifier.instance : new ColourMultiplier(w.getColour());
CCModel model = getOrGenerateModel(modelKey(w));
model.render(new Translation(pos), new IconTransformation(w.getIcon()), m);
}
public static void renderInv(int thickness, Transformation t, Icon icon)
{
CCModel m = invModels[thickness];
if (m == null)
invModels[thickness] = m = gen_inst.generateInvModel(thickness);
m.render(t, new IconTransformation(icon));
}
public static void renderBreakingOverlay(Icon icon, PartFlatWire wire)
{
int key = modelKey(wire);
int side = (key >> 8) % 6;
double w = ((key >> 8) / 6 + 1) / 16D;
double h = w + 1 / 16D;
int mask = key & 0xFF;
int connMask = (mask & 0xF0) >> 4 | mask & 0xF;
int connCount = WireModelGenerator.countConnections(connMask);
LinkedList<Cuboid6> boxes = new LinkedList<Cuboid6>();
boxes.add(new Cuboid6(0.5 - w, 0, 0.5 - w, 0.5 + w, h, 0.5 + w).apply(Rotation.sideRotations[side].at(Vector3.center)));// center
for (int r = 0; r < 4; r++)
{
int length;
if (connCount == 0)
{
if (r % 2 == 1)
length = 4;
else
length = 0;
}
else if (connCount == 1)
{
if (connMask == 1 << (r + 2) % 4)// this side is opposite the one with a connection
length = 4;
else if (connMask == 1 << r)
length = 8;
else
length = 0;
}
else
length = (connMask & 1 << r) != 0 ? 8 : 0;
if (length > 0)
{
double l = length / 16D;
boxes.add(new Cuboid6(0.5 - w, 0, 0.5 + w, 0.5 + w, h, 0.5 + l).apply(Rotation.sideOrientation(side, r).at(Vector3.center)));
}
}
for (Cuboid6 box : boxes)
RenderUtils.renderBlock(box, 0, new Translation(wire.x(), wire.y(), wire.z()), new IconTransformation(icon), null);
}
}