|
|
|
|
@@ -7,81 +7,83 @@ struct GPUEdgeValues {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct GPUPositions {
|
|
|
|
|
public Vector3 centerPos;
|
|
|
|
|
public Vector3 edge0Pos, edge1Pos, edge2Pos, edge3Pos, edge4Pos, edge5Pos, edge6Pos, edge7Pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct GPUBall {
|
|
|
|
|
public float radiusSq;
|
|
|
|
|
public float factor;
|
|
|
|
|
public Vector3 position;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct GPUEdgeVertices {
|
|
|
|
|
public int index;
|
|
|
|
|
public Vector3 edge0, edge1, edge2, edge3, edge4, edge5, edge6, edge7, edge8, edge9, edge10, edge11;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public class CubeGrid {
|
|
|
|
|
public List<Vector3> vertices;
|
|
|
|
|
public int width, height, depth;
|
|
|
|
|
public float resolution;
|
|
|
|
|
|
|
|
|
|
private Container container;
|
|
|
|
|
private Ball[] metaballs;
|
|
|
|
|
private float threshold;
|
|
|
|
|
|
|
|
|
|
private ComputeShader shader;
|
|
|
|
|
private int shaderKernel;
|
|
|
|
|
private GPUPositions[] precomputedPositions;
|
|
|
|
|
|
|
|
|
|
private ComputeBuffer positionsBuffer;
|
|
|
|
|
private ComputeBuffer outputBuffer;
|
|
|
|
|
private ComputeBuffer valuesBuffer;
|
|
|
|
|
private ComputeBuffer metaballsBuffer;
|
|
|
|
|
private ComputeBuffer edgeMapBuffer;
|
|
|
|
|
private ComputeBuffer verticesBuffer;
|
|
|
|
|
|
|
|
|
|
public const bool useGPU = true;
|
|
|
|
|
private GPUPositions[] precomputedPositions;
|
|
|
|
|
|
|
|
|
|
public CubeGrid(Container container, float resolution, float threshold, ComputeShader shader, Ball[] metaballs) {
|
|
|
|
|
this.width = Mathf.RoundToInt(container.transform.localScale.x / resolution);
|
|
|
|
|
this.height = Mathf.RoundToInt(container.transform.localScale.y / resolution);
|
|
|
|
|
this.depth = Mathf.RoundToInt(container.transform.localScale.z / resolution);
|
|
|
|
|
private bool initized;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Constructor
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
public CubeGrid(Container container, ComputeShader shader) {
|
|
|
|
|
this.container = container;
|
|
|
|
|
this.shader = shader;
|
|
|
|
|
|
|
|
|
|
this.width = Mathf.RoundToInt(container.transform.localScale.x / this.container.resolution);
|
|
|
|
|
this.height = Mathf.RoundToInt(container.transform.localScale.y / this.container.resolution);
|
|
|
|
|
this.depth = Mathf.RoundToInt(container.transform.localScale.z / this.container.resolution);
|
|
|
|
|
|
|
|
|
|
this.vertices = new List<Vector3>();
|
|
|
|
|
|
|
|
|
|
this.container = container;
|
|
|
|
|
this.resolution = resolution;
|
|
|
|
|
this.threshold = threshold;
|
|
|
|
|
this.shader = shader;
|
|
|
|
|
this.metaballs = metaballs;
|
|
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
|
this.initized = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void evaluateAll() {
|
|
|
|
|
//
|
|
|
|
|
// Public methods
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
public void evaluateAll(MetaBall[] metaballs) {
|
|
|
|
|
if(!this.initized) {
|
|
|
|
|
this.init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.vertices.Clear();
|
|
|
|
|
|
|
|
|
|
// write info about metaballs in format readable by compute shaders
|
|
|
|
|
GPUBall[] gpuBalls = new GPUBall[metaballs.Length];
|
|
|
|
|
for(int i = 0; i < metaballs.Length; i++) {
|
|
|
|
|
MetaBall metaball = metaballs[i];
|
|
|
|
|
gpuBalls[i].position = metaball.transform.localPosition;
|
|
|
|
|
gpuBalls[i].factor = metaball.factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// magic happens here
|
|
|
|
|
GPUEdgeVertices[] edgeVertices = this.runComputeShader(gpuBalls);
|
|
|
|
|
|
|
|
|
|
if(CubeGrid.useGPU) {
|
|
|
|
|
GPUBall[] gpuBalls = new GPUBall[this.metaballs.Length];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < this.metaballs.Length; i++) {
|
|
|
|
|
Ball metaball = this.metaballs[i];
|
|
|
|
|
gpuBalls[i].position = metaball.transform.localPosition;
|
|
|
|
|
gpuBalls[i].radiusSq = metaball.radiusSq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GPUEdgeValues[] edgeValues = this.runComputeShader(gpuBalls);
|
|
|
|
|
|
|
|
|
|
for(int x = 0; x < this.width; x++) {
|
|
|
|
|
for(int y = 0; y < this.height; y++) {
|
|
|
|
|
for(int z = 0; z < this.depth; z++) {
|
|
|
|
|
GPUEdgeValues edgeVal = edgeValues[x + this.width * (y + this.height * z)];
|
|
|
|
|
float[] edges = { edgeVal.edge0Val, edgeVal.edge1Val, edgeVal.edge2Val, edgeVal.edge3Val,
|
|
|
|
|
edgeVal.edge4Val, edgeVal.edge5Val, edgeVal.edge6Val, edgeVal.edge7Val};
|
|
|
|
|
|
|
|
|
|
this.updateVertices(edges, this.positionMap[x, y, z]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for(int x = 0; x < this.width; x++) {
|
|
|
|
|
for(int y = 0; y < this.height; y++) {
|
|
|
|
|
for(int z = 0; z < this.depth; z++) {
|
|
|
|
|
Vector3 relativePosition = this.positionMap[x, y, z];
|
|
|
|
|
float[] edges = this.evaluateCube(relativePosition);
|
|
|
|
|
this.updateVertices(edges, relativePosition);
|
|
|
|
|
}
|
|
|
|
|
// perform rest of the marching cubes algorithm
|
|
|
|
|
for(int x = 0; x < this.width; x++) {
|
|
|
|
|
for(int y = 0; y < this.height; y++) {
|
|
|
|
|
for(int z = 0; z < this.depth; z++) {
|
|
|
|
|
this.updateVertices2(edgeVertices[x + this.width * (y + this.height * z)]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -118,8 +120,10 @@ public class CubeGrid {
|
|
|
|
|
|
|
|
|
|
public void destroy() {
|
|
|
|
|
this.positionsBuffer.Release();
|
|
|
|
|
this.outputBuffer.Release();
|
|
|
|
|
this.valuesBuffer.Release();
|
|
|
|
|
this.metaballsBuffer.Release();
|
|
|
|
|
this.edgeMapBuffer.Release();
|
|
|
|
|
this.verticesBuffer.Release();
|
|
|
|
|
this.triangleBuffer = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -130,11 +134,10 @@ public class CubeGrid {
|
|
|
|
|
private void init() {
|
|
|
|
|
this.instantiateEdgeMap();
|
|
|
|
|
this.instantiatePositionMap();
|
|
|
|
|
this.instantiateGPUPositions();
|
|
|
|
|
this.instantiateComputeShader();
|
|
|
|
|
|
|
|
|
|
if(CubeGrid.useGPU) {
|
|
|
|
|
this.instantiateGPUPositions();
|
|
|
|
|
this.instantiateComputeShader();
|
|
|
|
|
}
|
|
|
|
|
this.initized = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void instantiateEdgeMap() {
|
|
|
|
|
@@ -149,6 +152,7 @@ public class CubeGrid {
|
|
|
|
|
new Vector3(-1, 1, 1)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// scale edge map
|
|
|
|
|
for(int i = 0; i < 8; i++) {
|
|
|
|
|
this.edgeMap[i] /= 2;
|
|
|
|
|
this.edgeMap[i] = new Vector3(this.edgeMap[i].x / ((float) this.width),
|
|
|
|
|
@@ -181,6 +185,7 @@ public class CubeGrid {
|
|
|
|
|
for(int y = 0; y < this.height; y++) {
|
|
|
|
|
for(int z = 0; z < this.depth; z++) {
|
|
|
|
|
Vector3 centerPoint = this.positionMap[x, y, z];
|
|
|
|
|
this.precomputedPositions[x + this.width * (y + this.height * z)].centerPos = centerPoint;
|
|
|
|
|
|
|
|
|
|
this.precomputedPositions[x + this.width * (y + this.height * z)].edge0Pos = centerPoint + this.edgeMap[0];
|
|
|
|
|
this.precomputedPositions[x + this.width * (y + this.height * z)].edge1Pos = centerPoint + this.edgeMap[1];
|
|
|
|
|
@@ -196,131 +201,86 @@ public class CubeGrid {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void instantiateComputeShader() {
|
|
|
|
|
this.positionsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 96);
|
|
|
|
|
// setup buffers
|
|
|
|
|
this.positionsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 108);
|
|
|
|
|
this.positionsBuffer.SetData(this.precomputedPositions);
|
|
|
|
|
|
|
|
|
|
this.outputBuffer = new ComputeBuffer(this.precomputedPositions.Length, 32);
|
|
|
|
|
this.edgeMapBuffer = new ComputeBuffer(8, 12);
|
|
|
|
|
this.edgeMapBuffer.SetData(this.edgeMap);
|
|
|
|
|
|
|
|
|
|
this.verticesBuffer = new ComputeBuffer(this.precomputedPositions.Length, 148);
|
|
|
|
|
this.metaballsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 16);
|
|
|
|
|
|
|
|
|
|
this.shaderKernel = shader.FindKernel("Calculate");
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "positions", this.positionsBuffer);
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "outputBuffer", this.outputBuffer);
|
|
|
|
|
// and assign them to compute shader buffer
|
|
|
|
|
this.shaderKernel = this.shader.FindKernel("Calculate");
|
|
|
|
|
|
|
|
|
|
this.shader.SetInt("width", this.width);
|
|
|
|
|
this.shader.SetInt("height", this.height);
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "positions", this.positionsBuffer);
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "metaballs", this.metaballsBuffer);
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "edgeMap", this.edgeMapBuffer);
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "edgeVertices", this.verticesBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// GPU metaball falloff function summator
|
|
|
|
|
// GPU metaball falloff function summator & part of marching cubes algorithm
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
private GPUEdgeValues[] runComputeShader(GPUBall[] gpuBalls) {
|
|
|
|
|
private GPUEdgeVertices[] runComputeShader(GPUBall[] gpuBalls) {
|
|
|
|
|
// pass data to the compute shader
|
|
|
|
|
this.metaballsBuffer.SetData(gpuBalls);
|
|
|
|
|
this.shader.SetBuffer(this.shaderKernel, "metaballs", this.metaballsBuffer);
|
|
|
|
|
|
|
|
|
|
this.shader.SetInt("numMetaballs", this.metaballs.Length);
|
|
|
|
|
this.shader.SetInt("numMetaballs", gpuBalls.Length);
|
|
|
|
|
this.shader.SetInt("width", this.width);
|
|
|
|
|
this.shader.SetInt("height", this.height);
|
|
|
|
|
this.shader.SetFloat("threshold", this.container.threshold);
|
|
|
|
|
|
|
|
|
|
// Run, Forrest, run!
|
|
|
|
|
this.shader.Dispatch(this.shaderKernel, this.width / 8, this.height / 8, this.depth / 8);
|
|
|
|
|
|
|
|
|
|
GPUEdgeValues[] output = new GPUEdgeValues[this.precomputedPositions.Length];
|
|
|
|
|
this.outputBuffer.GetData(output);
|
|
|
|
|
// parse returned vertex data and return it
|
|
|
|
|
GPUEdgeVertices[] output = new GPUEdgeVertices[this.verticesBuffer.count];
|
|
|
|
|
this.verticesBuffer.GetData(output);
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// CPU metaball falloff function summator
|
|
|
|
|
// Rest of marching cubes algorithm (on CPU)
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
private float[] evaluateCube(Vector3 centerPoint) {
|
|
|
|
|
float[] edges = new float[8];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 8; i++) {
|
|
|
|
|
edges[i] = this.evaluatePoint(centerPoint + this.edgeMap[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return edges;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float evaluatePoint(Vector3 point) {
|
|
|
|
|
float value = 0;
|
|
|
|
|
|
|
|
|
|
foreach(Ball ball in this.metaballs) {
|
|
|
|
|
Vector3 ballPosition = ball.transform.localPosition;
|
|
|
|
|
float distX = point.x - ballPosition.x,
|
|
|
|
|
distY = point.y - ballPosition.y,
|
|
|
|
|
distZ = point.z - ballPosition.z;
|
|
|
|
|
value += ball.radiusSq / (distX * distX + distY * distY + distZ * distZ);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Marching cubes algorithm
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
private void updateVertices(float[] edges, Vector3 relativePosition) {
|
|
|
|
|
int cubeIndex = this.findIndex(edges);
|
|
|
|
|
int pattern = this.edgeTable[cubeIndex];
|
|
|
|
|
|
|
|
|
|
Vector3[] edgeVertices = new Vector3[12];
|
|
|
|
|
|
|
|
|
|
for(int edge = 0; edge < 12; edge++) {
|
|
|
|
|
if((pattern & 1 << edge) != 0) {
|
|
|
|
|
int vertIndex0 = this.verticesAtEndsOfEdges[edge * 2];
|
|
|
|
|
int vertIndex1 = this.verticesAtEndsOfEdges[edge * 2 + 1];
|
|
|
|
|
|
|
|
|
|
float vertValue0 = edges[vertIndex0];
|
|
|
|
|
float vertValue1 = edges[vertIndex1];
|
|
|
|
|
|
|
|
|
|
Vector3 vertPosition0 = relativePosition + this.edgeMap[vertIndex0];
|
|
|
|
|
Vector3 vertPosition1 = relativePosition + this.edgeMap[vertIndex1];
|
|
|
|
|
|
|
|
|
|
float delta = (threshold - vertValue0) / (vertValue1 - vertValue0);
|
|
|
|
|
|
|
|
|
|
// lineary interpolate positions
|
|
|
|
|
edgeVertices[edge] = vertPosition0 + delta * (vertPosition1 - vertPosition0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private void updateVertices2(GPUEdgeVertices vert) {
|
|
|
|
|
int cubeIndex = vert.index;
|
|
|
|
|
|
|
|
|
|
for(int k = 0; triTable[cubeIndex][k] != -1; k += 3) {
|
|
|
|
|
this.vertices.Add(edgeVertices[this.triTable[cubeIndex][k]]);
|
|
|
|
|
this.vertices.Add(edgeVertices[this.triTable[cubeIndex][k + 2]]);
|
|
|
|
|
this.vertices.Add(edgeVertices[this.triTable[cubeIndex][k + 1]]);
|
|
|
|
|
this.vertices.Add(this.findVertex(vert, this.triTable[cubeIndex][k]));
|
|
|
|
|
this.vertices.Add(this.findVertex(vert, this.triTable[cubeIndex][k + 2]));
|
|
|
|
|
this.vertices.Add(this.findVertex(vert, this.triTable[cubeIndex][k + 1]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int findIndex(float[] edges) {
|
|
|
|
|
byte cubeIndex = 0;
|
|
|
|
|
|
|
|
|
|
if(edges[0] > this.threshold) {
|
|
|
|
|
cubeIndex |= 1;
|
|
|
|
|
private Vector3 findVertex(GPUEdgeVertices vert, int i) {
|
|
|
|
|
if(i == 0) {
|
|
|
|
|
return vert.edge0;
|
|
|
|
|
} else if(i == 1) {
|
|
|
|
|
return vert.edge1;
|
|
|
|
|
} else if(i == 2) {
|
|
|
|
|
return vert.edge2;
|
|
|
|
|
} else if(i == 3) {
|
|
|
|
|
return vert.edge3;
|
|
|
|
|
} else if(i == 4) {
|
|
|
|
|
return vert.edge4;
|
|
|
|
|
} else if(i == 5) {
|
|
|
|
|
return vert.edge5;
|
|
|
|
|
} else if(i == 6) {
|
|
|
|
|
return vert.edge6;
|
|
|
|
|
} else if(i == 7) {
|
|
|
|
|
return vert.edge7;
|
|
|
|
|
} else if(i == 8) {
|
|
|
|
|
return vert.edge8;
|
|
|
|
|
} else if(i == 9) {
|
|
|
|
|
return vert.edge9;
|
|
|
|
|
} else if(i == 10) {
|
|
|
|
|
return vert.edge10;
|
|
|
|
|
} else {
|
|
|
|
|
return vert.edge11;
|
|
|
|
|
}
|
|
|
|
|
if(edges[1] > this.threshold) {
|
|
|
|
|
cubeIndex |= 2;
|
|
|
|
|
}
|
|
|
|
|
if(edges[2] > this.threshold) {
|
|
|
|
|
cubeIndex |= 4;
|
|
|
|
|
}
|
|
|
|
|
if(edges[3] > this.threshold) {
|
|
|
|
|
cubeIndex |= 8;
|
|
|
|
|
}
|
|
|
|
|
if(edges[4] > this.threshold) {
|
|
|
|
|
cubeIndex |= 16;
|
|
|
|
|
}
|
|
|
|
|
if(edges[5] > this.threshold) {
|
|
|
|
|
cubeIndex |= 32;
|
|
|
|
|
}
|
|
|
|
|
if(edges[6] > this.threshold) {
|
|
|
|
|
cubeIndex |= 64;
|
|
|
|
|
}
|
|
|
|
|
if(edges[7] > this.threshold) {
|
|
|
|
|
cubeIndex |= 128;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return index
|
|
|
|
|
return cubeIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
@@ -332,56 +292,6 @@ public class CubeGrid {
|
|
|
|
|
private Vector3[,,] positionMap;
|
|
|
|
|
|
|
|
|
|
private Vector3[] edgeMap;
|
|
|
|
|
|
|
|
|
|
private int[] edgeTable = {
|
|
|
|
|
0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
|
|
|
|
|
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
|
|
|
|
0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
|
|
|
|
|
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
|
|
|
|
0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
|
|
|
|
|
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
|
|
|
|
0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
|
|
|
|
|
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
|
|
|
|
0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
|
|
|
|
|
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
|
|
|
|
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
|
|
|
|
|
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
|
|
|
|
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
|
|
|
|
|
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
|
|
|
|
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
|
|
|
|
|
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
|
|
|
|
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
|
|
|
|
|
0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
|
|
|
|
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
|
|
|
|
|
0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
|
|
|
|
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
|
|
|
|
|
0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
|
|
|
|
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
|
|
|
|
|
0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
|
|
|
|
|
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
|
|
|
|
|
0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
|
|
|
|
|
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
|
|
|
|
|
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
|
|
|
|
|
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
|
|
|
|
|
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
|
|
|
|
|
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
|
|
|
|
|
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private int[] verticesAtEndsOfEdges = {
|
|
|
|
|
0, 1,
|
|
|
|
|
1, 2,
|
|
|
|
|
2, 3,
|
|
|
|
|
3, 0,
|
|
|
|
|
4, 5,
|
|
|
|
|
5, 6,
|
|
|
|
|
6, 7,
|
|
|
|
|
7, 4,
|
|
|
|
|
0, 4,
|
|
|
|
|
1, 5,
|
|
|
|
|
2, 6,
|
|
|
|
|
3, 7
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private int[][] triTable = {
|
|
|
|
|
new int[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
|
|
|
|