package mods.eln.gridnode;
import mods.eln.misc.UtilsClient;
import mods.eln.node.transparent.TransparentNodeDescriptor;
import mods.eln.node.transparent.TransparentNodeElementRender;
import mods.eln.node.transparent.TransparentNodeEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Vec3;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import static org.lwjgl.opengl.GL11.*;
/**
* Created by svein on 25/08/15.
*/
public abstract class GridRender extends TransparentNodeElementRender {
private final GridDescriptor descriptor;
private final ResourceLocation cableTexture;
private ArrayList<Catenary> catenaries = new ArrayList<Catenary>();
private float idealRenderingAngle;
public GridRender(TransparentNodeEntity tileEntity, TransparentNodeDescriptor descriptor) {
super(tileEntity, descriptor);
this.descriptor = (GridDescriptor) descriptor;
cableTexture = new ResourceLocation("eln", this.descriptor.cableTexture);
}
@Override
public void draw() {
descriptor.draw(idealRenderingAngle);
UtilsClient.bindTexture(cableTexture);
// TODO: Try not to need this. (How? Math.)
glDisable(GL_CULL_FACE);
for (Catenary catenary : catenaries) {
catenary.draw();
}
glEnable(GL_CULL_FACE);
}
private Vec3 readVec(DataInputStream stream) throws IOException {
return Vec3.createVectorHelper(stream.readFloat(), stream.readFloat(), stream.readFloat());
}
@Override
public void networkUnserialize(DataInputStream stream) {
super.networkUnserialize(stream);
try {
for (Catenary catenary : catenaries) {
catenary.destroy();
}
catenaries.clear();
idealRenderingAngle = stream.readFloat();
int linkCount = stream.readInt();
for (int i = 0; i < linkCount; i++) {
// Links always come in pairs.
Vec3 splus = readVec(stream);
Vec3 tplus = readVec(stream);
Vec3 sgnd = readVec(stream);
Vec3 tgnd = readVec(stream);
Vec3 dplus = splus.subtract(tplus).normalize();
Vec3 dgnd = sgnd.subtract(tgnd).normalize();
double straightV = dplus.dotProduct(dgnd);
dplus = splus.subtract(tgnd).normalize();
dgnd = sgnd.subtract(tplus).normalize();
double crossV = dplus.dotProduct(dgnd);
if (crossV < straightV) {
catenaries.add(new Catenary(splus, tplus));
catenaries.add(new Catenary(sgnd, tgnd));
} else {
catenaries.add(new Catenary(splus, tgnd));
catenaries.add(new Catenary(sgnd, tplus));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean cameraDrawOptimisation() {
return false;
}
private class Catenary {
final int list;
final Vec3 origin = Vec3.createVectorHelper(0, 0, 0);
final int box[] = {
3, 7, 5, 3, 5, 1,
4, 8, 6, 4, 6, 2,
1, 6, 5, 1, 2, 6,
3, 8, 7, 3, 4, 8
};
// Maps box coordinates (above) to texture coordinates.
final int boxTex[] = {
0, 0, // 1
0, 1, // 2
0, 1, // 3
0, 0, // 4
1, 0, // 5
1, 1, // 6
1, 1, // 7
1, 0, // 8
};
private final double cableWidth = 0.05;
// TODO: Lighting and such should not be the same across the entire cable.
// Probably need make physical "cable" blocks, to make minecraft cooperate.
// The individual blocks should do the rendering.
// ...later. Much later.
Catenary(Vec3 start, Vec3 end) {
// These are the central vertices of the catenary.
Vec3[] catenary = getConnectionCatenary(start, end);
list = glGenLists(1);
glNewList(list, GL_COMPILE);
glBegin(GL_TRIANGLES);
if (start.xCoord == end.xCoord && start.zCoord == end.zCoord) {
// Poles right on top of each other? No catenaries here.
drawBox(spread(start, end), spread(end, start));
} else {
// Four points at the starting pole.
Vec3 previous[] = spread(start, catenary[0]);
for (int i = 0; i < catenary.length - 1; i++) {
// Some more points at intermediate junctions.
Vec3 next[] = spread(catenary[i], catenary[i + 1]);
drawBox(previous, next);
previous = next;
}
// Finally, at the ending pole. We'll just translate the second-to-last points to fit.
Vec3 last[] = translate(previous, catenary[catenary.length - 2].subtract(catenary[catenary.length - 1]));
drawBox(previous, last);
}
glEnd();
glEndList();
}
private void drawBox(Vec3[] from, Vec3[] to) {
Vec3 v[] = new Vec3[]{from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3]};
// Figure out the lighting.
// Vec3 middle = Vec3.createVectorHelper(0, 0, 0);
// for (Vec3 x : v) {
// middle = middle.addVector(x.xCoord, x.yCoord, x.zCoord);
// }
// middle = multiply(middle, v.length).addVector(tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord);
// glColor3d(
// 139 / 255.0,
// 69 / 255.0,
// 19 / 255.0);
// And let's draw it all.
for (int i = 0; i < box.length; i++) {
final int bc = box[i] - 1;
glTexCoord2f(boxTex[bc * 2], boxTex[bc * 2 + 1]);
glVertex3f((float) v[bc].xCoord, (float) v[bc].yCoord, (float) v[bc].zCoord);
}
}
private Vec3[] translate(Vec3[] start, Vec3 delta) {
Vec3 ret[] = new Vec3[start.length];
for (int i = 0; i < start.length; i++) {
ret[i] = start[i].addVector(delta.xCoord, delta.yCoord, delta.zCoord);
}
return ret;
}
private Vec3[] spread(Vec3 a, Vec3 b) {
// We want to draw a box-shaped cable following the catenary.
// To start with, compute a vector perpendicular to the first
// catenary segment, then rotate it around the catenary to form four points.
final Vec3 delta = b.subtract(a);
// This is just to copy.
// We don't care what r is, so long as it's linearly independent of delta.
final Vec3 r = delta.normalize();
r.rotateAroundY(1);
r.rotateAroundX(1);
// This gives us one vector which is perpendicular to delta.
final Vec3 x1 = multiply(delta.crossProduct(r).normalize(), cableWidth);
// And this, another, perpendicular to delta and x1.
final Vec3 y1 = multiply(delta.crossProduct(x1).normalize(), cableWidth);
// Now just invert those to get the other two corners.
final Vec3 x2 = negate(x1), y2 = negate(y1);
return translate(new Vec3[]{x1, y1, y2, x2}, a);
}
private Vec3 negate(Vec3 v) {
return v.subtract(origin);
}
Vec3 multiply(Vec3 a, double b) {
return Vec3.createVectorHelper(
a.xCoord * b,
a.yCoord * b,
a.zCoord * b
);
}
// This function borrowed from Immersive Engineering. Check them out!
private Vec3[] getConnectionCatenary(Vec3 start, Vec3 end) {
// TODO: Thermal heating.
final double slack = 1.005;
final int vertices = 16;
double dx = (end.xCoord) - (start.xCoord);
double dy = (end.yCoord) - (start.yCoord);
double dz = (end.zCoord) - (start.zCoord);
double dw = Math.sqrt(dx * dx + dz * dz);
double k = Math.sqrt(dx * dx + dy * dy + dz * dz) * slack;
double l = 0;
int limiter = 0;
while (limiter < 300) {
limiter++;
l += 0.01;
if (Math.sinh(l) / l >= Math.sqrt(k * k - dy * dy) / dw)
break;
}
double a = dw / 2 / l;
double p = (0 + dw - a * Math.log((k + dy) / (k - dy))) * 0.5;
double q = (dy + 0 - k * Math.cosh(l) / Math.sinh(l)) * 0.5;
Vec3[] vex = new Vec3[vertices];
for (int i = 0; i < vertices; i++) {
float n1 = (i + 1) / (float) vertices;
double x1 = 0 + dx * n1;
double z1 = 0 + dz * n1;
double y1 = a * Math.cosh(((Math.sqrt(x1 * x1 + z1 * z1)) - p) / a) + q;
vex[i] = Vec3.createVectorHelper(start.xCoord + x1, start.yCoord + y1, start.zCoord + z1);
}
return vex;
}
public void draw() {
glCallList(list);
}
public void destroy() {
glDeleteLists(list, 1);
}
}
}