private static Vector3f ProjectNormal(Vector3f n, Vector3f delta) { //return Vector3f.Cross(n, delta); var mat = new Matrix3X3( 1.0f - n.X * n.X, -n.X * n.Y, -n.X * n.Z, -n.X * n.Y, 1.0f - n.Y * n.Y, -n.Y * n.Z, -n.X * n.Z, -n.Y * n.Z, 1.0f - n.Z * n.Z); return mat * delta; }
public static int PolygonizeTransitionCell(Vector3f offset, Vector3i origin, Vector3i localX, Vector3i localY, Vector3i localZ, int x, int y, float cellSize, byte lodIndex, byte axis, byte directionMask, IVolumeData samples, ref IList<Vertex> verts, ref IList<int> indices, ref TransitionCache cache) { int lodStep = 1 << lodIndex; int sampleStep = 1 << (lodIndex - 1); int lodScale = 1 << lodIndex; int last = 16 * lodScale; 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 (origin[i] == 0) { near |= (byte)(1 << (i * 2 + 0)); } // Vertex close to positive face. if (origin[i] == last) { near |= (byte)(1 << (i * 2 + 1)); } } Vector3i[] coords = { new Vector3i(0,0,0), new Vector3i(1,0,0), new Vector3i(2,0,0), // High-res lower row new Vector3i(0,1,0), new Vector3i(1,1,0), new Vector3i(2,1,0), // High-res middle row new Vector3i(0,2,0), new Vector3i(1,2,0), new Vector3i(2,2,0), // High-res upper row new Vector3i(0,0,2), new Vector3i(2,0,2), // Low-res lower row new Vector3i(0,2,2), new Vector3i(2,2,2) // Low-res upper row }; // TODO: Implement Matrix Math var mx = localX * sampleStep; var my = localY * sampleStep; var mz = localZ * sampleStep; Matrix3X3 basis = new Matrix3X3((Vector3f)mx, (Vector3f)my, (Vector3f)mz); Vector3i[] pos = { origin + basis * coords[0x00], origin + basis * coords[0x01], origin + basis * coords[0x02], origin + basis * coords[0x03], origin + basis * coords[0x04], origin + basis * coords[0x05], origin + basis * coords[0x06], origin + basis * coords[0x07], origin + basis * coords[0x08], origin + basis * coords[0x09], origin + basis * coords[0x0A], origin + basis * coords[0x0B], origin + basis * coords[0x0C] }; Vector3f[] normals = new Vector3f[13]; for (int i = 0; i < 9; i++) { Vector3i p = pos[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; normals[i] = new Vector3f(nx, ny, nz); normals[i].Normalize(); } normals[0x9] = normals[0]; normals[0xA] = normals[2]; normals[0xB] = normals[6]; normals[0xC] = normals[8]; Vector3i[] samplePos = { pos[0], pos[1], pos[2], pos[3], pos[4], pos[5], pos[6], pos[7], pos[8], pos[0], pos[2], pos[6], pos[8] }; uint caseCode = (uint)(Sign(samples[pos[0]]) * 0x001 | Sign(samples[pos[1]]) * 0x002 | Sign(samples[pos[2]]) * 0x004 | Sign(samples[pos[5]]) * 0x008 | Sign(samples[pos[8]]) * 0x010 | Sign(samples[pos[7]]) * 0x020 | Sign(samples[pos[6]]) * 0x040 | Sign(samples[pos[3]]) * 0x080 | Sign(samples[pos[4]]) * 0x100); if (caseCode == 0 || caseCode == 511) return 0; cache[x, y].CaseIndex = (byte)caseCode; byte classIndex = Tables.TransitionCellClass[caseCode]; // Equivalence class index. var data = Tables.TransitionRegularCellData[classIndex & 0x7F]; bool inverse = (classIndex & 128) != 0; int[] localVertexMapping = new int[12]; int nv = (int)data.GetVertexCount(); int nt = (int)data.GetTriangleCount(); Debug.Assert(nv <= 12); var vert = new Vertex(); for (int i = 0; i < nv; i++) { ushort edgeCode = Tables.TransitionVertexData[caseCode][i]; byte v0 = HiNibble((byte)edgeCode); byte v1 = LoNibble((byte)edgeCode); bool lowside = (v0 > 8) && (v1 > 8); int d0 = samples[samplePos[v0]]; int d1 = samples[samplePos[v1]]; int t = (d1 << 8) / (d1 - d0); int u = 0x0100 - t; float t0 = t * S; float t1 = u * S; Vector3f n0 = normals[v0]; Vector3f n1 = normals[v1]; vert.Near = near; vert.Normal = n0 * t0 + n1 * t1; if ((t & 0x00ff) != 0) { // Use the reuse information in transitionVertexData byte dir = HiNibble((byte)(edgeCode >> 8)); byte idx = LoNibble((byte)(edgeCode >> 8)); bool present = (dir & directionMask) == dir; if (present) { // The previous cell is available. Retrieve the cached cell // from which to retrieve the reused vertex index from. var prev = cache[x - (dir & 1), y - ((dir >> 1) & 1)]; if (prev.CaseIndex == 0 || prev.CaseIndex == 511) { // Previous cell does not contain any geometry. localVertexMapping[i] = -1; } else { // Reuse the vertex index from the previous cell. localVertexMapping[i] = prev.Verts[idx]; } } if (!present || localVertexMapping[i] < 0) { Vector3f p0 = (Vector3f)pos[v0]; Vector3f p1 = (Vector3f)pos[v1]; Vector3f pi = Interp(p0, p1, samplePos[v0], samplePos[v1], samples, lowside ? (byte)lodIndex : (byte)(lodIndex - 1)); if (lowside) { switch (axis) { case 0: pi.X = (float)origin.X; break; case 1: pi.Y = (float)origin.Y; break; case 2: pi.Z = (float)origin.Z; break; } Vector3f delta = ComputeDelta(pi, lodIndex, 16); Vector3f proj = ProjectNormal(vert.Normal, delta); vert.Primary = Unused; vert.Secondary = offset + pi + proj; } else { vert.Near = 0; vert.Primary = offset + pi; vert.Secondary = Unused; } localVertexMapping[i] = verts.Count; verts.Add(vert); if ((dir & 8) != 0) { // The vertex can be reused. cache[x, y].Verts[idx] = localVertexMapping[i]; } } } else { // Try to reuse corner vertex from a preceding cell. // Use the reuse information in transitionCornerData. byte v = t == 0 ? v1 : v0; byte cornerData = Tables.TransitionCornerData[v]; byte dir = HiNibble(cornerData); // High nibble contains direction code. byte idx = LoNibble((cornerData)); // Low nibble contains storage slot for vertex. bool present = (dir & directionMask) == dir; if (present) { // The previous cell is available. Retrieve the cached cell // from which to retrieve the reused vertex index from. var prev = cache[x - (dir & 1), y - ((dir >> 1) & 1)]; if (prev.CaseIndex == 0 || prev.CaseIndex == 511) { // Previous cell does not contain any geometry. localVertexMapping[i] = -1; } else { // Reuse the vertex index from the previous cell. localVertexMapping[i] = prev.Verts[idx]; } } if (!present || localVertexMapping[i] < 0) { // A vertex has to be created. Vector3f pi = (Vector3f)pos[v]; if (v > 8) { // On low-resolution side. // Necessary to translate the intersection point to the // high-res side so that it is transformed the same way // as the vertices in the regular cell. switch (axis) { case 0: pi.X = (float)origin.X; break; case 1: pi.Y = (float)origin.Y; break; case 2: pi.Z = (float)origin.Z; break; } Vector3f delta = ComputeDelta(pi, lodIndex, 16); Vector3f proj = ProjectNormal(vert.Normal, delta); vert.Primary = Unused; vert.Secondary = offset + pi + proj; } else { // On high-resolution side. vert.Near = 0; // Vertices on high-res side are never moved. vert.Primary = offset + pi; vert.Secondary = Unused; } localVertexMapping[i] = verts.Count; cache[x, y].Verts[idx] = localVertexMapping[i]; verts.Add(vert); } } } for (long t = 0; t < nt; ++t) { if (inverse) { indices.Add(localVertexMapping[data.Indizes()[t * 3 + 2]]); indices.Add(localVertexMapping[data.Indizes()[t * 3 + 1]]); indices.Add(localVertexMapping[data.Indizes()[t * 3 + 0]]); //indices.push_back(localVertexMapping[ptr[2]]); //indices.push_back(localVertexMapping[ptr[1]]); //indices.push_back(localVertexMapping[ptr[0]]); } else { indices.Add(localVertexMapping[data.Indizes()[t * 3 + 0]]); indices.Add(localVertexMapping[data.Indizes()[t * 3 + 1]]); indices.Add(localVertexMapping[data.Indizes()[t * 3 + 2]]); // indices.push_back(localVertexMapping[ptr[0]]); // indices.push_back(localVertexMapping[ptr[1]]); // indices.push_back(localVertexMapping[ptr[2]]); } } return nt; }