public MeshData RemoveMesh(Vector3i position) { MeshData mesh; if (_meshes.TryGetValue(position, out mesh)) _meshes.Remove(position); return mesh; }
// lod and transition parameter? public MeshData GetMesh(Vector3i position) { MeshData mesh; if (!_meshes.TryGetValue(position, out mesh)) { mesh = GenerateMesh(); _meshes.Add(position, mesh); } return mesh; }
public ReuseCell GetReusedIndex(Vector3i pos, byte rDir) { int rx = rDir & 0x01; int rz = (rDir >> 1) & 0x01; int ry = (rDir >> 2) & 0x01; int dx = pos.X - rx; int dy = pos.Y - ry; int dz = pos.Z - rz; Debug.Assert(dx >= 0 && dy >= 0 && dz >= 0); return _cache[dx & 1][dy * chunkSize + dz]; }
public void Sub(Vector3i v) { X -= v.X; Y -= v.Y; Z -= v.Z; }
public bool Equals(Vector3i other) { return other.Y == Y && other.Z == Z && other.X == X; }
public void Add(Vector3i v) { X += v.X; Y += v.Y; Z += v.Z; }
private void EliminateLodPositionShift(int lod, ref sbyte d0, ref sbyte d1, ref long t, ref Vector3i iP0, ref Vector3f P0, ref Vector3i iP1, ref Vector3f P1) { for (int k = 0; k < lod - 1; k++) { Vector3f vm = (P0 + P1) / 2.0f; Vector3i pm = (iP0 + iP1) / 2; sbyte sm = volume[pm]; if ((d0 & 0x8F) != (d1 & 0x8F)) { P1 = vm; iP1 = pm; d1 = sm; } else { P0 = vm; iP0 = pm; d0 = sm; } } if (d1 == d0) // ????????????? return; t = (d1 << 8) / (d1 - d0); // recalc }
private static Vector3f Interp(Vector3f v0, Vector3f v1, Vector3i p0, Vector3i p1, IVolumeData samples, byte lodIndex = 0) { sbyte s0 = samples[p0]; sbyte s1 = samples[p1]; int t = (s1 << 8) / (s1 - s0); int u = 0x0100 - t; if ((t & 0x00ff) == 0) { // The generated vertex lies at one of the corners so there // is no need to subdivide the interval. if (t == 0) { return v1; } return v0; } else { for (int i = 0; i < lodIndex; ++i) { Vector3f vm = (v0 + v1) / 2; Vector3i pm = (p0 + p1) / 2; sbyte sm = samples[pm]; // Determine which of the sub-intervals that contain // the intersection with the isosurface. if (Sign(s0) != Sign(sm)) { v1 = vm; p1 = pm; s1 = sm; } else { v0 = vm; p0 = pm; s0 = sm; } } t = (s1 << 8) / (s1 - s0); u = 0x0100 - t; return v0 * t * S + v1 * u * S; } }
private static Vector3f Interp(Vector3i v0, Vector3i v1, Vector3i p0, Vector3i p1, IVolumeData samples, byte lodIndex = 0) { return Interp((Vector3f)v0, (Vector3f)v1, p0, p1, samples, lodIndex); }
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 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; }
internal void PolygonizeCell(Vector3i offsetPos, Vector3i pos, ref Mesh mesh, int lod) { Debug.Assert(lod >= 1, "Level of Detail must be greater than 1"); offsetPos += pos * lod; byte directionMask = (byte)((pos.X > 0 ? 1 : 0) | ((pos.Z > 0 ? 1 : 0) << 1) | ((pos.Y > 0 ? 1 : 0) << 2)); sbyte[] density = new sbyte[8]; for (int i = 0; i < density.Length; i++) { density[i] = volume[offsetPos + Tables.CornerIndex[i] * lod]; } byte caseCode = getCaseCode(density); if ((caseCode ^ ((density[7] >> 7) & 0xFF)) == 0) //for this cases there is no triangulation return; Vector3f[] cornerNormals = new Vector3f[8]; for (int i = 0; i < 8; i++) { var p = offsetPos + Tables.CornerIndex[i] * lod; float nx = (volume[p + Vector3i.UnitX] - volume[p - Vector3i.UnitX]) * 0.5f; float ny = (volume[p + Vector3i.UnitY] - volume[p - Vector3i.UnitY]) * 0.5f; float nz = (volume[p + Vector3i.UnitZ] - volume[p - Vector3i.UnitZ]) * 0.5f; //cornerNormals[i] = new Vector3f(nx, ny, nz); cornerNormals[i].X = nx; cornerNormals[i].Y = ny; cornerNormals[i].Z = nz; cornerNormals[i].Normalize(); } byte regularCellClass = Tables.RegularCellClass[caseCode]; ushort[] vertexLocations = Tables.RegularVertexData[caseCode]; Tables.RegularCell c = Tables.RegularCellData[regularCellClass]; long vertexCount = c.GetVertexCount(); long triangleCount = c.GetTriangleCount(); byte[] indexOffset = c.Indizes(); //index offsets for current cell ushort[] mappedIndizes = new ushort[indexOffset.Length]; //array with real indizes for current cell for (int i = 0; i < vertexCount; i++) { byte edge = (byte)(vertexLocations[i] >> 8); byte reuseIndex = (byte)(edge & 0xF); //Vertex id which should be created or reused 1,2 or 3 byte rDir = (byte)(edge >> 4); //the direction to go to reach a previous cell for reusing byte v1 = (byte)((vertexLocations[i]) & 0x0F); //Second Corner Index byte v0 = (byte)((vertexLocations[i] >> 4) & 0x0F); //First Corner Index sbyte d0 = density[v0]; sbyte d1 = density[v1]; //Vector3f n0 = cornerNormals[v0]; //Vector3f n1 = cornerNormals[v1]; Debug.Assert(v1 > v0); int t = (d1 << 8) / (d1 - d0); int u = 0x0100 - t; float t0 = t / 256f; float t1 = u / 256f; int index = -1; if (UseCache && v1 != 7 && (rDir & directionMask) == rDir) { Debug.Assert(reuseIndex != 0); ReuseCell cell = cache.GetReusedIndex(pos, rDir); index = cell.Verts[reuseIndex]; } if (index == -1) { Vector3f normal = cornerNormals[v0] * t0 + cornerNormals[v1] * t1; GenerateVertex(ref offsetPos, ref pos, mesh, lod, t, ref v0, ref v1, ref d0, ref d1, normal); index = mesh.LatestAddedVertIndex(); } if ((rDir & 8) != 0) { cache.SetReusableIndex(pos, reuseIndex, mesh.LatestAddedVertIndex()); } mappedIndizes[i] = (ushort)index; } for (int t = 0; t < triangleCount; t++) { for (int i = 0; i < 3; i++) { mesh.AddIndex(mappedIndizes[c.Indizes()[t * 3 + i]]); } } }
private void GenerateVertex(ref Vector3i offsetPos, ref Vector3i pos, Mesh mesh, int lod, long t, ref byte v0, ref byte v1, ref sbyte d0, ref sbyte d1, Vector3f normal) { Vector3i iP0 = (offsetPos + Tables.CornerIndex[v0] * lod); Vector3f P0;// = new Vector3f(iP0.X, iP0.Y, iP0.Z); P0.X = iP0.X; P0.Y = iP0.Y; P0.Z = iP0.Z; Vector3i iP1 = (offsetPos + Tables.CornerIndex[v1] * lod); Vector3f P1;// = new Vector3f(iP1.X, iP1.Y, iP1.Z); P1.X = iP1.X; P1.Y = iP1.Y; P1.Z = iP1.Z; //EliminateLodPositionShift(lod, ref d0, ref d1, ref t, ref iP0, ref P0, ref iP1, ref P1); Vector3f Q = InterpolateVoxelVector(t, P0, P1); mesh.AddVertex(Q, normal); }
public bool Equals(Vector3i other) { return(other.Y == Y && other.Z == Z && other.X == X); }
public ReuseCell this[Vector3i v] { set { this[v.X, v.Y, v.Z] = value; } }
internal void SetReusableIndex(Vector3i pos, byte reuseIndex, ushort p) { _cache[pos.X & 1][pos.Y * chunkSize + pos.Z].Verts[reuseIndex] = p; }
public sbyte this[Vector3i v] { get { return this[v.X, v.Y, v.Z]; } set { this[v.X, v.Y, v.Z] = value; } }