// for reference: public static void generateNegativeXTransitionCells( VFVoxelChunkData chunkData, float cellSize, TransVertices verts, List <int> indices) { int dirIndex = 1; TransitionCache cache = new TransitionCache(); for (int y = 0; y < 16; ++y) // random map only 64 unit height { for (int x = 0; x < 16; ++x) { PolygonizeTransitionCell(x, y, dirIndex, cellSize, chunkData, verts, indices, cache); } } }
public static void BuildTransitionCells(int faceMask, VFVoxelChunkData chunkData, float cellSize, TransVertices verts, List <int> indices) { for (int i = 0; i < 6; i++) { if (0 == (faceMask & (1 << i))) { continue; } int dirIndex = i; TransitionCache cache = new TransitionCache(); int len = 16; for (int y = 0; y < len; ++y) // random map only 64 unit height { for (int x = 0; x < len; ++x) { PolygonizeTransitionCell(x, y, dirIndex, cellSize, chunkData, verts, indices, cache); } } } }
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); }
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; }
public static int PolygonizeTransitionCell( int x, int y, // The x and y position of the cell within the block face. int dirIndex, // ref to TransitionFaceCoords float cellSize, // The width of a cell in world scale. VFVoxelChunkData chunkData, TransVertices verts, List <int> indices, // output verts' pos is relative to cur chunk TransitionCache cache) { int lod2Sample = chunkData.LOD; int sampleStep = 1; // << lod2Sample; int spacing = 1 << 1; // Spacing between low-res corners. int scale = 1 << lod2Sample; IntVector3 xAxis = Tables.TransitionFaceCoords[dirIndex, 0]; IntVector3 yAxis = Tables.TransitionFaceCoords[dirIndex, 1]; IntVector3 zAxis = Tables.TransitionFaceCoords[dirIndex, 2]; IntVector3 axisExtend = Tables.TransitionFaceCoords[dirIndex, 4]; // mesh width. IntVector3 relOrigin = Tables.TransitionFaceCoords[dirIndex, 3] * ChunkWidth + xAxis * (x * spacing) + yAxis * (y * spacing); // Origin in sample space(current cell). // Rotate to change coordinate Matrix3X3 basis = new Matrix3X3(xAxis * sampleStep, yAxis * sampleStep, zAxis * sampleStep); IntVector3[] relPos = { relOrigin + basis * Tables.TransitionCornerCoords[0x00], relOrigin + basis * Tables.TransitionCornerCoords[0x01], relOrigin + basis * Tables.TransitionCornerCoords[0x02], relOrigin + basis * Tables.TransitionCornerCoords[0x03], relOrigin + basis * Tables.TransitionCornerCoords[0x04], relOrigin + basis * Tables.TransitionCornerCoords[0x05], relOrigin + basis * Tables.TransitionCornerCoords[0x06], relOrigin + basis * Tables.TransitionCornerCoords[0x07], relOrigin + basis * Tables.TransitionCornerCoords[0x08], relOrigin + basis * Tables.TransitionCornerCoords[0x09], relOrigin + basis * Tables.TransitionCornerCoords[0x0A], relOrigin + basis * Tables.TransitionCornerCoords[0x0B], relOrigin + basis * Tables.TransitionCornerCoords[0x0C] }; // Compute case code(indexing as described in Figure4.17) VFVoxel[] voxels = { chunkData[relPos[0]], chunkData[relPos[1]], chunkData[relPos[2]], chunkData[relPos[3]], chunkData[relPos[4]], chunkData[relPos[5]], chunkData[relPos[6]], chunkData[relPos[7]], chunkData[relPos[8]], }; uint caseCode = (uint)((voxels[0].Volume >> 7) & 0x001 | (voxels[1].Volume >> 6) & 0x002 | (voxels[2].Volume >> 5) & 0x004 | (voxels[5].Volume >> 4) & 0x008 | (voxels[8].Volume >> 3) & 0x010 | (voxels[7].Volume >> 2) & 0x020 | (voxels[6].Volume >> 1) & 0x040 | (voxels[3].Volume) & 0x080 | (voxels[4].Volume << 1) & 0x100); if (caseCode == 0 || caseCode == 511) { return(0); } cache[x, y].CaseIndex = (byte)caseCode; byte[] vols = { voxels[0].Volume, voxels[1].Volume, voxels[2].Volume, voxels[3].Volume, voxels[4].Volume, voxels[5].Volume, voxels[6].Volume, voxels[7].Volume, voxels[8].Volume, voxels[0].Volume, voxels[2].Volume, voxels[6].Volume, voxels[8].Volume }; byte[] types = { voxels[0].Type, voxels[1].Type, voxels[2].Type, voxels[3].Type, voxels[4].Type, voxels[5].Type, voxels[6].Type, voxels[7].Type, voxels[8].Type, voxels[0].Type, voxels[2].Type, voxels[6].Type, voxels[8].Type }; // Compute normal based on volumes Vector3[] normals = new Vector3[13]; for (int i = 0; i < 9; i++) { IntVector3 p = relPos[i]; float nx = p.x >= 1 ? (chunkData[p + IntVector3.UnitX].Volume - chunkData[p - IntVector3.UnitX].Volume) : (chunkData[p + IntVector3.UnitX].Volume - vols[i]); float ny = p.y >= 1 ? (chunkData[p + IntVector3.UnitY].Volume - chunkData[p - IntVector3.UnitY].Volume) : (chunkData[p + IntVector3.UnitY].Volume - vols[i]); float nz = p.z >= 1 ? (chunkData[p + IntVector3.UnitZ].Volume - chunkData[p - IntVector3.UnitZ].Volume) : (chunkData[p + IntVector3.UnitZ].Volume - vols[i]); normals[i] = new Vector3(nx, ny, nz); //normals[i].Normalize(); } normals[0x9] = normals[0]; normals[0xA] = normals[2]; normals[0xB] = normals[6]; normals[0xC] = normals[8]; // Compute which of the six faces of the block that the vertex // is near. (near is defined as being in boundary cell.) byte near = 0; if (relOrigin.x == 0) { near |= (byte)(1 << 0); } // Vertex close to negativeX face. if (relOrigin.x == ChunkWidth) { near |= (byte)(1 << 1); } // Vertex close to positiveX face. if (relOrigin.y == 0) { near |= (byte)(1 << 2); } // Vertex close to negativeY face. if (relOrigin.y == ChunkWidth) { near |= (byte)(1 << 3); } // Vertex close to positiveY face. if (relOrigin.z == 0) { near |= (byte)(1 << 4); } // Vertex close to negativeZ face. if (relOrigin.z == ChunkWidth) { near |= (byte)(1 << 5); } // Vertex close to positiveZ face. byte directionMask = (byte)((x > 0 ? 1 : 0) | ((y > 0 ? 1 : 0) << 1)); // Used to determine which previous cells that are available.on edge, dirmask will be cut byte classIndex = Tables.TransitionCellClass[caseCode]; // Equivalence class index. var data = Tables.TransitionRegularCellData[classIndex & 0x7F]; bool inverse = (classIndex & 0x80) != 0; int[] localVertexMapping = new int[12]; //TransitionRegularCellData's hinibble means vertex count(max is 0x0c) int nv = (int)data.GetVertexCount(); int nt = (int)data.GetTriangleCount(); for (int i = 0; i < nv; i++) { // HiByte:reuse data shown in Figure 4.18; LoByte:2 end points shown in Figure 4.16 ushort edgeCode = Tables.TransitionVertexData[caseCode][i]; byte pointCode = (byte)edgeCode; byte reuseCode = (byte)(edgeCode >> 8); // v0, v1: 2 end points. v0<v1 byte v0 = HiNibble(pointCode); byte v1 = LoNibble(pointCode); //Vector3 n0 = normals[v0]; //Vector3 n1 = normals[v1]; byte d0 = vols[v0]; byte d1 = vols[v1]; int t = ((IsoLevel - d0) << 8) / (d1 - d0); int u = 0x0100 - t; float t0 = u * S; float t1 = t * S; byte v_ = 0; byte dir, idx; bool bLowSide, bAddCache, bCorner; if ((t & 0x00ff) != 0) { // Use the reuse information in transitionVertexData, shown in Figure 4.18 // directionMask is voxel's dir in current processing planar: (byte)((x > 0 ? 1 : 0) | ((y > 0 ? 1 : 0) << 1)) // dir is voxel's dir in current cell dir = HiNibble(reuseCode); idx = LoNibble(reuseCode); bLowSide = ((v0 > 8) && (v1 > 8)); bAddCache = (dir & 8) != 0; bCorner = false; } else { // Try to reuse corner vertex from a preceding cell. // Use the reuse information in transitionCornerData. v_ = t == 0 ? v0 : v1; byte cornerData = Tables.TransitionCornerData[v_]; dir = HiNibble(cornerData); idx = LoNibble((cornerData)); bLowSide = v_ > 8; bAddCache = true; bCorner = true; } bool present = (dir & directionMask) == dir; // dir is 1 or 2 && not a edge voxel, then the verts is available 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) { localVertexMapping[i] = verts.Count; if (bAddCache) // The vertex can be reused. { cache[x, y].Verts[idx] = localVertexMapping[i]; } verts.IsLowside.Add(bLowSide); // half resolution side byte typev_ = types[v_]; byte typev0 = types[v0]; byte typev1 = types[v1]; if (typev_ == 0 || typev0 == 0 || typev1 == 0) // type 0 will gen black tri { if (typev_ == 0) { typev_ = typev0; } if (typev_ == 0) { typev_ = typev0 = typev1; } if (typev_ == 0) { typev_ = typev0 = typev1 = 1; } else { if (typev0 == 0) { typev0 = typev_; } if (typev1 == 0) { typev1 = typev_; } } } byte curType = bCorner ? typev_ : (t0 < 0.5f ? typev1 : typev0); Vector3 vNormal = normals[v1] * t1 + normals[v0] * t0; verts.Normal_t.Add(new Vector4(vNormal.x / 256.0f, vNormal.y / 256.0f, vNormal.z / 256.0f, curType)); Vector3 pi = bCorner ? (Vector3)relPos[v_] : ((Vector3)relPos[v1]) * t1 + ((Vector3)relPos[v0]) * t0; if (bLowSide) { // Variant algo for PE's lod data // 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. Vector3 offset = Vector3.zero; switch (axisExtend.x) { case 0: offset.x = axisExtend.y * cellSize; pi.x = (float)(relOrigin.x); break; case 1: offset.y = axisExtend.y * cellSize; pi.y = (float)(relOrigin.y); break; case 2: offset.z = axisExtend.y * cellSize; pi.z = (float)(relOrigin.z); break; } #if DELTA_ENABLE deltaCnt++; if (deltaCnt == 17) { deltaCnt = 17; } Vector3 delta = ComputeDelta(pi, lodIndex, ChunkWidth); Vector3 proj = ProjectNormal(vert.Normal, delta); verts.Near.Add(near); verts.Position.Add((offset + pi + proj) * scale); #else verts.Near.Add(near); verts.Position.Add((offset + pi) * scale); #endif } else { // On high-resolution side. verts.Near.Add(0); // Vertices on high-res side are never moved. verts.Position.Add(pi * scale); } } } for (int t = 0; t < nt; ++t) { if (inverse) { indices.Add(localVertexMapping[data[t * 3 + 0]]); indices.Add(localVertexMapping[data[t * 3 + 1]]); indices.Add(localVertexMapping[data[t * 3 + 2]]); } else { indices.Add(localVertexMapping[data[t * 3 + 2]]); indices.Add(localVertexMapping[data[t * 3 + 1]]); indices.Add(localVertexMapping[data[t * 3 + 0]]); } } return(nt); }