/* * Given a grid cell and an isolevel, calculate the triangular * facets required to represent the isosurface through the cell. * Return the number of triangular facets, the array "triangles" * will be loaded up with the vertices at most 5 triangular facets. * 0 will be returned if the grid cell is either totally above * of totally below the isolevel. */ public static int Polygonise(GRIDCELL grid, float isolevel, TRIANGLE[] triangles) { int i, ntriang; int cubeindex; Vector3[] vertlist = new Vector3[12]; int[] edgeTable = MarchingCubesConstants.edgeTable; int[,] triTable = MarchingCubesConstants.triTable; /* * Determine the index into the edge table which * tells us which vertices are inside of the surface */ cubeindex = 0; if (grid.val[0] < isolevel) { cubeindex |= 1; } if (grid.val[1] < isolevel) { cubeindex |= 2; } if (grid.val[2] < isolevel) { cubeindex |= 4; } if (grid.val[3] < isolevel) { cubeindex |= 8; } if (grid.val[4] < isolevel) { cubeindex |= 16; } if (grid.val[5] < isolevel) { cubeindex |= 32; } if (grid.val[6] < isolevel) { cubeindex |= 64; } if (grid.val[7] < isolevel) { cubeindex |= 128; } /* Cube is entirely in/out of the surface */ if (edgeTable[cubeindex] == 0) { return(0); } /* Find the vertices where the surface intersects the cube */ if ((edgeTable[cubeindex] & 1) != 0) { vertlist[0] = VertexInterp(isolevel, grid.p[0], grid.p[1], grid.val[0], grid.val[1]); } if ((edgeTable[cubeindex] & 2) != 0) { vertlist[1] = VertexInterp(isolevel, grid.p[1], grid.p[2], grid.val[1], grid.val[2]); } if ((edgeTable[cubeindex] & 4) != 0) { vertlist[2] = VertexInterp(isolevel, grid.p[2], grid.p[3], grid.val[2], grid.val[3]); } if ((edgeTable[cubeindex] & 8) != 0) { vertlist[3] = VertexInterp(isolevel, grid.p[3], grid.p[0], grid.val[3], grid.val[0]); } if ((edgeTable[cubeindex] & 16) != 0) { vertlist[4] = VertexInterp(isolevel, grid.p[4], grid.p[5], grid.val[4], grid.val[5]); } if ((edgeTable[cubeindex] & 32) != 0) { vertlist[5] = VertexInterp(isolevel, grid.p[5], grid.p[6], grid.val[5], grid.val[6]); } if ((edgeTable[cubeindex] & 64) != 0) { vertlist[6] = VertexInterp(isolevel, grid.p[6], grid.p[7], grid.val[6], grid.val[7]); } if ((edgeTable[cubeindex] & 128) != 0) { vertlist[7] = VertexInterp(isolevel, grid.p[7], grid.p[4], grid.val[7], grid.val[4]); } if ((edgeTable[cubeindex] & 256) != 0) { vertlist[8] = VertexInterp(isolevel, grid.p[0], grid.p[4], grid.val[0], grid.val[4]); } if ((edgeTable[cubeindex] & 512) != 0) { vertlist[9] = VertexInterp(isolevel, grid.p[1], grid.p[5], grid.val[1], grid.val[5]); } if ((edgeTable[cubeindex] & 1024) != 0) { vertlist[10] = VertexInterp(isolevel, grid.p[2], grid.p[6], grid.val[2], grid.val[6]); } if ((edgeTable[cubeindex] & 2048) != 0) { vertlist[11] = VertexInterp(isolevel, grid.p[3], grid.p[7], grid.val[3], grid.val[7]); } /* Create the triangle */ ntriang = 0; for (i = 0; triTable[cubeindex, i] != -1; i += 3) { triangles[ntriang].p = new Vector3[3]; triangles[ntriang].p[0] = vertlist[triTable[cubeindex, i]]; triangles[ntriang].p[1] = vertlist[triTable[cubeindex, i + 1]]; triangles[ntriang].p[2] = vertlist[triTable[cubeindex, i + 2]]; ntriang++; } return(ntriang); }
/* Linearly interpolate the position where an isosurface cuts an edge between two vertices, each with their own scalar value */ static Vector3 VLerp(ref GRIDCELL curCell, double isolevel, int ia,int ib, ref Vector3 normal,ref Vector2 tex) { Vector3 p1=curCell.p[ia]; Vector3 p2=curCell.p[ib]; Vector3 n1=curCell.n[ia]; Vector3 n2=curCell.n[ib]; Vector2 t1=curCell.t[ia]; Vector2 t2=curCell.t[ib]; double valp1=curCell.val[ia]; double valp2=curCell.val[ib]; float mu; Vector3 p = new Vector3 (); if (Math.Abs (isolevel - valp1) < 0.00001) { p=p1; normal=n1; tex=t1; }else if (Math.Abs (isolevel - valp2) < 0.00001) { p=p2; normal=n2; tex=t2; }else if (Math.Abs (valp1 - valp2) < 0.00001) { p=p1; normal=n1; tex=t1; }else{ mu = (float)((isolevel - valp1) / (valp2 - valp1)); p=p1+mu*(p2-p1); normal=n1+mu*(n2-n1); tex=t1+mu*(t2-t1); } normal.Normalize (); return(p); }
//Build a unity mesh from a distance function public static Mesh BuildUnityMesh(Vector3 fieldMin, float fieldSize, int gridDims, DistanceFunction func) { //calc size of 1 cell float cellSize = fieldSize / gridDims; //allocate and initialize position / value for every grid vertex int vertDims = gridDims + 1; Vector3[] gridVertPos = new Vector3[vertDims * vertDims * vertDims]; float[] gridVertVal = new float[vertDims * vertDims * vertDims]; for (int x = 0; x < vertDims; x++) { for (int y = 0; y < vertDims; y++) { for (int z = 0; z < vertDims; z++) { int idx = x + (y + vertDims * z) * vertDims; Vector3 pos = new Vector3(x, y, z); gridVertPos[idx] = pos * cellSize + fieldMin; gridVertVal[idx] = func(gridVertPos[idx]); } } } //vertices and indices we build List <Vector3> vertices = new List <Vector3>(); List <int> indices = new List <int>(); //buffer to contain the generated triangles TRIANGLE[] tempTriangles = new TRIANGLE[5]; //iterate over cells GRIDCELL[] cells = new GRIDCELL[gridDims * gridDims * gridDims]; for (int x = 0; x < gridDims; x++) { for (int y = 0; y < gridDims; y++) { for (int z = 0; z < gridDims; z++) { //calc cell index int idx = x + (y + z * gridDims) * gridDims; Vector3i cellCoord = new Vector3i(x, y, z); //fill cell with corners of unit cube GRIDCELL cell = new GRIDCELL(); //transform cell corners + calculate field values cell.p = new Vector3[8]; cell.val = new float[cell.p.Length]; for (int i = 0; i < cell.p.Length; i++) { int globalVertIdx = CellVertToGlobalVert(gridDims, cellCoord, i); cell.p[i] = gridVertPos[globalVertIdx]; cell.val[i] = gridVertVal[globalVertIdx]; } //polygonise and store vertices (note: had to tweak winding order to make unity happy!) int numTriangles = Polygonise(cell, 0.0f, tempTriangles); for (int i = 0; i < numTriangles; i++) { int vidx = vertices.Count; indices.Add(vidx + 0); indices.Add(vidx + 1); indices.Add(vidx + 2); vertices.Add(tempTriangles[i].p[0]); vertices.Add(tempTriangles[i].p[1]); vertices.Add(tempTriangles[i].p[2]); } } } } //build normals using central differences of field float epsilon = 0.0001f; Vector3 xd = Vector3.right * epsilon; Vector3 yd = Vector3.up * epsilon; Vector3 zd = Vector3.forward * epsilon; List <Vector3> normals = new List <Vector3>(vertices.Count); for (int i = 0; i < vertices.Count; i++) { Vector3 n = new Vector3( func(vertices[i] + xd) - func(vertices[i] - xd), func(vertices[i] + yd) - func(vertices[i] - yd), func(vertices[i] + zd) - func(vertices[i] - zd)); normals.Add(n.normalized); } //fill and return unity mesh Mesh m = new Mesh(); m.Clear(); m.SetVertices(vertices); m.SetNormals(normals); m.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0); return(m); }
/* Linearly interpolate the position where an isosurface cuts an edge between two vertices, each with their own scalar value */ static Vector3 VertexInterp(ref GRIDCELL curCell, double isolevel, Vector3 p1, Vector3 p2, double valp1, double valp2, ref Vector3 normal) { float mu; Vector3 p = new Vector3 (); normal.x = (float)(curCell.val [0] - curCell.val [1] ); normal.y = (float)(curCell.val [0] - curCell.val [3] ); normal.z = (float)(curCell.val [0] - curCell.val [4] ); normal.Normalize (); if (Math.Abs (isolevel - valp1) < 0.00001) return(p1); if (Math.Abs (isolevel - valp2) < 0.00001) return(p2); if (Math.Abs (valp1 - valp2) < 0.00001) return(p1); mu = (float)((isolevel - valp1) / (valp2 - valp1)); p.x = p1.x + mu * (p2.x - p1.x); p.y = p1.y + mu * (p2.y - p1.y); p.z = p1.z + mu * (p2.z - p1.z); return(p); }
public static void Polygonise2(GRIDCELL grid, double isolevel, ref Vector3[] vertlist, ref Vector3[] normals, ref Vector2[] uvs, ref int[] triangles, ref int vertBase, ref int triBase) { int i; int cubeindex; //Vector3[] vertlist=new Vector3[12]; /* Determine the index into the edge table which tells us which vertices are inside of the surface */ cubeindex = 0; if (grid.val [0] < isolevel) cubeindex |= 1; if (grid.val [1] < isolevel) cubeindex |= 2; if (grid.val [2] < isolevel) cubeindex |= 4; if (grid.val [3] < isolevel) cubeindex |= 8; if (grid.val [4] < isolevel) cubeindex |= 16; if (grid.val [5] < isolevel) cubeindex |= 32; if (grid.val [6] < isolevel) cubeindex |= 64; if (grid.val [7] < isolevel) cubeindex |= 128; /* Cube is entirely in/out of the surface */ if (edgeTable [cubeindex] == 0) return;//(0); int[] remap = new int[12]; int mapTop = 0; int[] edgeMap = { 0,1, 1,2, 2,3, 3,0, 4,5, 5,6, 6,7, 7,4, 0,4, 1,5, 2,6, 3,7 }; int i2=0; for (i=0; i<12; i++) { if ((edgeTable [cubeindex] & (1 << i)) != 0) { remap [mapTop++] = i; normals [vertBase + i] = new Vector3 (); int e1=edgeMap[i2]; int e2=edgeMap[i2+1]; vertlist [vertBase + i] = VLerp (ref grid, isolevel, e1,e2, ref normals [vertBase + i],ref uvs[vertBase+i]); } i2+=2; } /* Find the vertices where the surface intersects the cube */ /* if ((edgeTable[cubeindex] & 1)!=0){ remap[mapTop++]=0; normals[vertBase+0]=new Vector3(); vertlist[vertBase+0] = VertexInterp(ref grid,isolevel,grid.p[0],grid.p[1],grid.val[0],grid.val[1],ref normals[vertBase+0]); } if ((edgeTable[cubeindex] & 2)!=0){ remap[mapTop++]=1; normals[vertBase+1]=new Vector3(); vertlist[vertBase+1] = VertexInterp(ref grid,isolevel,grid.p[1],grid.p[2],grid.val[1],grid.val[2],ref normals[vertBase+1]); } if ((edgeTable[cubeindex] & 4)!=0){ remap[mapTop++]=2; normals[vertBase+2]=new Vector3(); vertlist[vertBase+2] = VertexInterp(ref grid,isolevel,grid.p[2],grid.p[3],grid.val[2],grid.val[3],ref normals[vertBase+2]); } if ((edgeTable[cubeindex] & 8)!=0){ remap[mapTop++]=3; normals[vertBase+3]=new Vector3(); vertlist[vertBase+3] = VertexInterp(ref grid,isolevel,grid.p[3],grid.p[0],grid.val[3],grid.val[0],ref normals[vertBase+3]); } if ((edgeTable[cubeindex] & 16)!=0){ remap[mapTop++]=4; normals[vertBase+4]=new Vector3(); vertlist[vertBase+4] = VertexInterp(ref grid,isolevel,grid.p[4],grid.p[5],grid.val[4],grid.val[5],ref normals[vertBase+4]); } if ((edgeTable[cubeindex] & 32)!=0){ remap[mapTop++]=5; normals[vertBase+5]=new Vector3(); vertlist[vertBase+5] = VertexInterp(ref grid,isolevel,grid.p[5],grid.p[6],grid.val[5],grid.val[6],ref normals[vertBase+5]); } if ((edgeTable[cubeindex] & 64)!=0){ remap[mapTop++]=6; normals[vertBase+6]=new Vector3(); vertlist[vertBase+6] = VertexInterp(ref grid,isolevel,grid.p[6],grid.p[7],grid.val[6],grid.val[7],ref normals[vertBase+6]); } if ((edgeTable[cubeindex] & 128)!=0){ remap[mapTop++]=7; normals[vertBase+7]=new Vector3(); vertlist[vertBase+7] = VertexInterp(ref grid,isolevel,grid.p[7],grid.p[4],grid.val[7],grid.val[4],ref normals[vertBase+7]); } if ((edgeTable[cubeindex] & 256)!=0){ remap[mapTop++]=8; normals[vertBase+8]=new Vector3(); vertlist[vertBase+8] = VertexInterp(ref grid,isolevel,grid.p[0],grid.p[4],grid.val[0],grid.val[4],ref normals[vertBase+8]); } if ((edgeTable[cubeindex] & 512)!=0){ remap[mapTop++]=9; normals[vertBase+9]=new Vector3(); vertlist[vertBase+9] = VertexInterp(ref grid,isolevel,grid.p[1],grid.p[5],grid.val[1],grid.val[5],ref normals[vertBase+9]); } if ((edgeTable[cubeindex] & 1024)!=0){ remap[mapTop++]=10; normals[vertBase+10]=new Vector3(); vertlist[vertBase+10] = VertexInterp(ref grid,isolevel,grid.p[2],grid.p[6],grid.val[2],grid.val[6],ref normals[vertBase+10]); } if ((edgeTable[cubeindex] & 2048)!=0){ remap[mapTop++]=11; normals[vertBase+11]=new Vector3(); vertlist[vertBase+11] = VertexInterp(ref grid,isolevel,grid.p[3],grid.p[7],grid.val[3],grid.val[7],ref normals[vertBase+11]); } */ //for (i=0; i<12; i++) { // uvs [vertBase + i] = new Vector2 (vertlist [vertBase + i].x * 0.1f, vertlist [vertBase + i].z * 0.1f);// //} /* Create the triangle */ for (i=0; triTable[cubeindex,i]!=-1; i+=3) { triangles [triBase++] = triTable [cubeindex, i] + vertBase; triangles [triBase++] = triTable [cubeindex, i + 1] + vertBase; triangles [triBase++] = triTable [cubeindex, i + 2] + vertBase; } vertBase += 12; }
public static void Polygonise(GRIDCELL grid, double isolevel, ref Vector3[] vertlist, ref Vector3[] normals, ref Vector2[] uvs, ref int[] triangles, ref int vertBase, ref int triBase) { int i; int cubeindex; //Vector3[] vertlist=new Vector3[12]; /* Determine the index into the edge table which tells us which vertices are inside of the surface */ cubeindex = 0; if (grid.val [0] < isolevel) cubeindex |= 1; if (grid.val [1] < isolevel) cubeindex |= 2; if (grid.val [2] < isolevel) cubeindex |= 4; if (grid.val [3] < isolevel) cubeindex |= 8; if (grid.val [4] < isolevel) cubeindex |= 16; if (grid.val [5] < isolevel) cubeindex |= 32; if (grid.val [6] < isolevel) cubeindex |= 64; if (grid.val [7] < isolevel) cubeindex |= 128; /* Cube is entirely in/out of the surface */ if (edgeTable [cubeindex] == 0) return;//(0); int mapTop = 0; int[] edgeMap = { 0,1, 1,2, 2,3, 3,0, 4,5, 5,6, 6,7, 7,4, 0,4, 1,5, 2,6, 3,7 }; int i2=0; /* Find the vertices where the surface intersects the cube */ for (i=0; i<12; i++) { int vi=remap[i]=mapTop++; if ((edgeTable [cubeindex] & (1 << i)) != 0) { //normals [vertBase + vi] = new Vector3 (); int e1=edgeMap[i2]; int e2=edgeMap[i2+1]; vertlist [vertBase + vi] = VLerp (ref grid, isolevel, e1,e2, ref normals [vertBase + vi],ref uvs[vertBase+vi]); } i2+=2; } // for (i=0; i<12; i++) { //Store material values in the uv channels.. //Reflective.. // uvs [vertBase + i] = new Vector2 (vertlist [vertBase + i].x * 0.1f, vertlist [vertBase + i].z * 0.1f); // } /* Create the triangle */ for (i=0; triTable[cubeindex,i]!=-1; i+=3) { triangles [triBase++] = triTable [cubeindex, i] + vertBase; triangles [triBase++] = triTable [cubeindex, i + 1] + vertBase; triangles [triBase++] = triTable [cubeindex, i + 2] + vertBase; } vertBase += 12; }
public MarchingCubes(Vector3 MaxSize, int resolution_) { resolution = resolution_; gridSize = new Vector3(resolution, resolution, resolution); //gxgy = resolution * resolution; //numxyz = resolution * resolution * resolution; // totalNumberOfPoints worlddim = MaxSize; worldstride = new Vector3(gridSize.x / MaxSize.x, gridSize.y / MaxSize.y, gridSize.z / MaxSize.z); datastride = new Vector3(MaxSize.x / gridSize.x, MaxSize.y / gridSize.y, MaxSize.z / gridSize.z); worldcenter = new Vector3(MaxSize.x / 2.0f, MaxSize.y / 2.0f, MaxSize.z / 2.0f); WorldSize = new Vector3(resolution, resolution, resolution); for (int i = 0; i < 5; i++) { triangles.Add(new TRIANGLE()); } //isolevel = 0.0161f; grid = new GRIDCELL(); }
//Build a unity mesh from a distance function public static Mesh BuildUnityMesh(Vector3 fieldMin, float fieldSize, int gridDims, DistanceFunction func) { //vertices and indices we build List <Vector3> vertices = new List <Vector3>(); List <int> indices = new List <int>(); //buffer to contain the generated triangles TRIANGLE[] tempTriangles = new MarchingCubesBasicPort.TRIANGLE[5]; //calc size of 1 cell float cellSize = fieldSize / gridDims; //iterate over cells GRIDCELL[] cells = new MarchingCubesBasicPort.GRIDCELL[gridDims * gridDims * gridDims]; for (int x = 0; x < gridDims; x++) { for (int y = 0; y < gridDims; y++) { for (int z = 0; z < gridDims; z++) { //calc cell index int idx = x + (y + z * gridDims) * gridDims; //fill cell with corners of unit cube GRIDCELL cell = new GRIDCELL(); cell.p = new Vector3[] { new Vector3(x + 0, y + 0, z + 0), new Vector3(x + 1, y + 0, z + 0), new Vector3(x + 1, y + 0, z + 1), new Vector3(x + 0, y + 0, z + 1), new Vector3(x + 0, y + 1, z + 0), new Vector3(x + 1, y + 1, z + 0), new Vector3(x + 1, y + 1, z + 1), new Vector3(x + 0, y + 1, z + 1) }; //transform cell corners + calculate field values cell.val = new float[cell.p.Length]; for (int i = 0; i < cell.p.Length; i++) { cell.p[i] = cell.p[i] * cellSize + fieldMin; cell.val[i] = func(cell.p[i]); } //polygonise and store vertices int numTriangles = Polygonise(cell, 0.0f, tempTriangles); for (int i = 0; i < numTriangles; i++) { int vidx = vertices.Count; indices.Add(vidx + 0); indices.Add(vidx + 1); indices.Add(vidx + 2); vertices.Add(tempTriangles[i].p[0]); vertices.Add(tempTriangles[i].p[1]); vertices.Add(tempTriangles[i].p[2]); } } } } //build normals using central differences of field float epsilon = 0.0001f; Vector3 xd = Vector3.right * epsilon; Vector3 yd = Vector3.up * epsilon; Vector3 zd = Vector3.forward * epsilon; List <Vector3> normals = new List <Vector3>(vertices.Count); for (int i = 0; i < vertices.Count; i++) { Vector3 n = new Vector3( func(vertices[i] + xd) - func(vertices[i] - xd), func(vertices[i] + yd) - func(vertices[i] - yd), func(vertices[i] + zd) - func(vertices[i] - zd)); normals.Add(n.normalized); } //fill and return unity mesh Mesh m = new Mesh(); m.Clear(); m.SetVertices(vertices); m.SetNormals(normals); m.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0); return(m); }