472 lines
No EOL
13 KiB
Java
472 lines
No EOL
13 KiB
Java
package resonantinduction.electrical.wire.flat;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.LinkedList;
|
|
|
|
import net.minecraft.util.Icon;
|
|
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);
|
|
}
|
|
} |