public static int PolygonizeRegularCell(Vector3i min, Vector3f offset, Vector3i xyz, IVolumeData samples, byte lodIndex, float cellSize, ref IList <Vertex> verts, ref IList <int> indices, ref RegularCache cache) { int lodScale = 1 << lodIndex; int last = 15 * lodScale; byte directionMask = (byte)((xyz.X > 0 ? 1 : 0) | ((xyz.Y > 0 ? 1 : 0) << 1) | ((xyz.Z > 0 ? 1 : 0) << 2)); byte near = 0; // Compute which of the six faces of the block that the vertex // is near. (near is defined as being in boundary cell.) for (int i = 0; i < 3; i++) { //Vertex close to negative face. if (min[i] == 0) { near |= (byte)(1 << (i * 2 + 0)); } //Vertex close to positive face. if (min[i] == last) { near |= (byte)(1 << (i * 2 + 1)); } } Vector3i[] cornerPositions = Tables.CornerIndex; for (int i = 0; i < cornerPositions.Length; i++) { cornerPositions[i] = min + cornerPositions[i] * lodScale; } // new Vector3i[] // { // min + new Vector3i(0, 0, 0)*lodScale, // min + new Vector3i(1, 0, 0)*lodScale, // min + new Vector3i(0, 1, 0)*lodScale, // min + new Vector3i(1, 1, 0)*lodScale, // // min + new Vector3i(0, 0, 1)*lodScale, // min + new Vector3i(1, 0, 1)*lodScale, // min + new Vector3i(0, 1, 1)*lodScale, // min + new Vector3i(1, 1, 1)*lodScale // }; Vector3i dif = cornerPositions[7] - cornerPositions[1]; // Retrieve sample values for all the corners. sbyte[] cornerSamples = new sbyte[] { samples[cornerPositions[0]], samples[cornerPositions[1]], samples[cornerPositions[2]], samples[cornerPositions[3]], samples[cornerPositions[4]], samples[cornerPositions[5]], samples[cornerPositions[6]], samples[cornerPositions[7]], }; Vector3f[] cornerNormals = new Vector3f[8]; // Determine the index into the edge table which // tells us which vertices are inside of the surface uint caseCode = (uint)(((cornerSamples[0] >> 7) & 0x01) | ((cornerSamples[1] >> 6) & 0x02) | ((cornerSamples[2] >> 5) & 0x04) | ((cornerSamples[3] >> 4) & 0x08) | ((cornerSamples[4] >> 3) & 0x10) | ((cornerSamples[5] >> 2) & 0x20) | ((cornerSamples[6] >> 1) & 0x40) | (cornerSamples[7] & 0x80)); cache[xyz].CaseIndex = (byte)caseCode; if ((caseCode ^ ((cornerSamples[7] >> 7) & 0xff)) == 0) { return(0); } // Compute the normals at the cell corners using central difference. for (int i = 0; i < 8; ++i) { var p = cornerPositions[i]; float nx = (samples[p + Vector3i.UnitX] - samples[p - Vector3i.UnitX]) * 0.5f; float ny = (samples[p + Vector3i.UnitY] - samples[p - Vector3i.UnitY]) * 0.5f; float nz = (samples[p + Vector3i.UnitZ] - samples[p - Vector3i.UnitZ]) * 0.5f; cornerNormals[i] = new Vector3f(nx, ny, nz); cornerNormals[i].Normalize(); } var c = Tables.RegularCellClass[caseCode]; var data = Tables.RegularCellData[c]; byte nt = (byte)data.GetTriangleCount(); byte nv = (byte)data.GetVertexCount(); int[] localVertexMapping = new int[12]; var vert = new Vertex(); vert.Near = near; // Generate all the vertex positions by interpolating along // each of the edges that intersect the isosurface. for (int i = 0; i < nv; i++) { ushort edgeCode = Tables.RegularVertexData[caseCode][i]; byte v0 = HiNibble((byte)(edgeCode & 0xFF)); byte v1 = LoNibble((byte)(edgeCode & 0xFF)); Vector3i p0 = cornerPositions[v0]; Vector3i p1 = cornerPositions[v1]; Vector3f n0 = cornerNormals[v0]; Vector3f n1 = cornerNormals[v1]; int d0 = samples[p0]; int d1 = samples[p1]; Debug.Assert(v0 < v1); int t = (d1 << 8) / (d1 - d0); int u = 0x0100 - t; float t0 = t * S; float t1 = u * S; if ((t & 0x00ff) != 0) { // Vertex lies in the interior of the edge. byte dir = HiNibble((byte)(edgeCode >> 8)); byte idx = LoNibble((byte)(edgeCode >> 8)); bool present = (dir & directionMask) == dir; if (present) { var prev = cache[xyz + PrevOffset(dir)]; // I don't think this can happen for non-corner vertices. if (prev.CaseIndex == 0 || prev.CaseIndex == 255) { localVertexMapping[i] = -1; } else { localVertexMapping[i] = prev.Verts[idx]; } } if (!present || localVertexMapping[i] < 0) { localVertexMapping[i] = verts.Count; Vector3f pi = Interp(p0, p1, p0, p1, samples, lodIndex); vert.Primary = offset + pi; vert.Normal = n0 * t0 + n1 * t1; if (near > 0) { Vector3f delta = ComputeDelta(pi, lodIndex, 16); vert.Secondary = vert.Primary + ProjectNormal(vert.Normal, delta); } else { // The vertex is not in a boundary cell, so the // secondary position will never be used. vert.Secondary = Unused; //vert.Primary; } verts.Add(vert); if ((dir & 8) != 0) { // Store the generated vertex so that other cells can reuse it. cache[xyz].Verts[idx] = localVertexMapping[i]; } } } else if (t == 0 && v1 == 7) { // This cell owns the vertex, so it should be created. localVertexMapping[i] = verts.Count; Vector3f pi = (Vector3f)p1 * t0 + (Vector3f)p1 * t1; vert.Primary = offset + pi; vert.Normal = n0 * t0 + n1 * t1; if (near > 0) { Vector3f delta = ComputeDelta(pi, lodIndex, 16); vert.Secondary = vert.Primary + ProjectNormal(vert.Normal, delta); } else { // The vertex is not in a boundary cell, so the secondary // position will never be used. vert.Secondary = Unused; } verts.Add(vert); cache[xyz].Verts[0] = localVertexMapping[i]; } else { // A 3-bit direction code leading to the proper cell can easily be obtained by // inverting the 3-bit corner index (bitwise, by exclusive ORing with the number 7). // The corner index depends on the value of t, t = 0 means that we're at the higher // numbered endpoint. byte dir = t == 0 ? (byte)(v1 ^ 7) : (byte)(v0 ^ 7); bool present = (dir & directionMask) == dir; if (present) { var prev = cache[xyz + PrevOffset(dir)]; // The previous cell might not have any geometry, and we // might therefore have to create a new vertex anyway. if (prev.CaseIndex == 0 || prev.CaseIndex == 255) { localVertexMapping[i] = -1; } else { localVertexMapping[i] = prev.Verts[0]; } } if (!present || (localVertexMapping[i] < 0)) { localVertexMapping[i] = verts.Count; Vector3f pi = (Vector3f)p0 * t0 + (Vector3f)p1 * t1; vert.Primary = offset + pi; vert.Normal = n0 * t0 + n1 * t1; if (near > 0) { Vector3f delta = ComputeDelta(pi, lodIndex, 16); vert.Secondary = vert.Primary + ProjectNormal(vert.Normal, delta); } else { vert.Secondary = Unused; } verts.Add(vert); } } } for (int t = 0; t < nt; t++) { for (int i = 0; i < 3; i++) { indices.Add(localVertexMapping[data.Indizes()[t * 3 + i]]); } } return(nt); }
public static int PolygonizeRegularCell(Vector3i min, Vector3f offset, Vector3i xyz, IVolumeData samples, byte lodIndex, float cellSize, ref IList<Vertex> verts, ref IList<int> indices, ref RegularCache cache) { int lodScale = 1 << lodIndex; int last = 15 * lodScale; byte directionMask = (byte)((xyz.X > 0 ? 1 : 0) | ((xyz.Y > 0 ? 1 : 0) << 1) | ((xyz.Z > 0 ? 1 : 0) << 2)); byte near = 0; // Compute which of the six faces of the block that the vertex // is near. (near is defined as being in boundary cell.) for (int i = 0; i < 3; i++) { //Vertex close to negative face. if (min[i] == 0) { near |= (byte)(1 << (i * 2 + 0)); } //Vertex close to positive face. if (min[i] == last) { near |= (byte)(1 << (i * 2 + 1)); } } Vector3i[] cornerPositions = Tables.CornerIndex; for (int i = 0; i < cornerPositions.Length; i++) { cornerPositions[i] = min + cornerPositions[i] * lodScale; } // new Vector3i[] // { // min + new Vector3i(0, 0, 0)*lodScale, // min + new Vector3i(1, 0, 0)*lodScale, // min + new Vector3i(0, 1, 0)*lodScale, // min + new Vector3i(1, 1, 0)*lodScale, // // min + new Vector3i(0, 0, 1)*lodScale, // min + new Vector3i(1, 0, 1)*lodScale, // min + new Vector3i(0, 1, 1)*lodScale, // min + new Vector3i(1, 1, 1)*lodScale // }; Vector3i dif = cornerPositions[7] - cornerPositions[1]; // Retrieve sample values for all the corners. sbyte[] cornerSamples = new sbyte[] { samples[cornerPositions[0]], samples[cornerPositions[1]], samples[cornerPositions[2]], samples[cornerPositions[3]], samples[cornerPositions[4]], samples[cornerPositions[5]], samples[cornerPositions[6]], samples[cornerPositions[7]], }; Vector3f[] cornerNormals = new Vector3f[8]; // Determine the index into the edge table which // tells us which vertices are inside of the surface uint caseCode = (uint)(((cornerSamples[0] >> 7) & 0x01) | ((cornerSamples[1] >> 6) & 0x02) | ((cornerSamples[2] >> 5) & 0x04) | ((cornerSamples[3] >> 4) & 0x08) | ((cornerSamples[4] >> 3) & 0x10) | ((cornerSamples[5] >> 2) & 0x20) | ((cornerSamples[6] >> 1) & 0x40) | (cornerSamples[7] & 0x80)); cache[xyz].CaseIndex = (byte)caseCode; if ((caseCode ^ ((cornerSamples[7] >> 7) & 0xff)) == 0) return 0; // Compute the normals at the cell corners using central difference. for (int i = 0; i < 8; ++i) { var p = cornerPositions[i]; float nx = (samples[p + Vector3i.UnitX] - samples[p - Vector3i.UnitX]) * 0.5f; float ny = (samples[p + Vector3i.UnitY] - samples[p - Vector3i.UnitY]) * 0.5f; float nz = (samples[p + Vector3i.UnitZ] - samples[p - Vector3i.UnitZ]) * 0.5f; cornerNormals[i] = new Vector3f(nx, ny, nz); cornerNormals[i].Normalize(); } var c = Tables.RegularCellClass[caseCode]; var data = Tables.RegularCellData[c]; byte nt = (byte)data.GetTriangleCount(); byte nv = (byte)data.GetVertexCount(); int[] localVertexMapping = new int[12]; var vert = new Vertex(); vert.Near = near; // Generate all the vertex positions by interpolating along // each of the edges that intersect the isosurface. for (int i = 0; i < nv; i++) { ushort edgeCode = Tables.RegularVertexData[caseCode][i]; byte v0 = HiNibble((byte)(edgeCode & 0xFF)); byte v1 = LoNibble((byte)(edgeCode & 0xFF)); Vector3i p0 = cornerPositions[v0]; Vector3i p1 = cornerPositions[v1]; Vector3f n0 = cornerNormals[v0]; Vector3f n1 = cornerNormals[v1]; int d0 = samples[p0]; int d1 = samples[p1]; Debug.Assert(v0 < v1); int t = (d1 << 8) / (d1 - d0); int u = 0x0100 - t; float t0 = t * S; float t1 = u * S; if ((t & 0x00ff) != 0) { // Vertex lies in the interior of the edge. byte dir = HiNibble((byte)(edgeCode >> 8)); byte idx = LoNibble((byte)(edgeCode >> 8)); bool present = (dir & directionMask) == dir; if (present) { var prev = cache[xyz + PrevOffset(dir)]; // I don't think this can happen for non-corner vertices. if (prev.CaseIndex == 0 || prev.CaseIndex == 255) { localVertexMapping[i] = -1; } else { localVertexMapping[i] = prev.Verts[idx]; } } if (!present || localVertexMapping[i] < 0) { localVertexMapping[i] = verts.Count; Vector3f pi = Interp(p0, p1, p0, p1, samples, lodIndex); vert.Primary = offset + pi; vert.Normal = n0 * t0 + n1 * t1; if (near > 0) { Vector3f delta = ComputeDelta(pi, lodIndex, 16); vert.Secondary = vert.Primary + ProjectNormal(vert.Normal, delta); } else { // The vertex is not in a boundary cell, so the // secondary position will never be used. vert.Secondary = Unused; //vert.Primary; } verts.Add(vert); if ((dir & 8) != 0) { // Store the generated vertex so that other cells can reuse it. cache[xyz].Verts[idx] = localVertexMapping[i]; } } } else if (t == 0 && v1 == 7) { // This cell owns the vertex, so it should be created. localVertexMapping[i] = verts.Count; Vector3f pi = (Vector3f)p1 * t0 + (Vector3f)p1 * t1; vert.Primary = offset + pi; vert.Normal = n0 * t0 + n1 * t1; if (near > 0) { Vector3f delta = ComputeDelta(pi, lodIndex, 16); vert.Secondary = vert.Primary + ProjectNormal(vert.Normal, delta); } else { // The vertex is not in a boundary cell, so the secondary // position will never be used. vert.Secondary = Unused; } verts.Add(vert); cache[xyz].Verts[0] = localVertexMapping[i]; } else { // A 3-bit direction code leading to the proper cell can easily be obtained by // inverting the 3-bit corner index (bitwise, by exclusive ORing with the number 7). // The corner index depends on the value of t, t = 0 means that we're at the higher // numbered endpoint. byte dir = t == 0 ? (byte)(v1 ^ 7) : (byte)(v0 ^ 7); bool present = (dir & directionMask) == dir; if (present) { var prev = cache[xyz + PrevOffset(dir)]; // The previous cell might not have any geometry, and we // might therefore have to create a new vertex anyway. if (prev.CaseIndex == 0 || prev.CaseIndex == 255) { localVertexMapping[i] = -1; } else { localVertexMapping[i] = prev.Verts[0]; } } if (!present || (localVertexMapping[i] < 0)) { localVertexMapping[i] = verts.Count; Vector3f pi = (Vector3f)p0 * t0 + (Vector3f)p1 * t1; vert.Primary = offset + pi; vert.Normal = n0 * t0 + n1 * t1; if (near > 0) { Vector3f delta = ComputeDelta(pi, lodIndex, 16); vert.Secondary = vert.Primary + ProjectNormal(vert.Normal, delta); } else { vert.Secondary = Unused; } verts.Add(vert); } } } for (int t = 0; t < nt; t++) { for (int i = 0; i < 3; i++) { indices.Add(localVertexMapping[data.Indizes()[t * 3 + i]]); } } return nt; }