/** * Metaball implementation v0.2.2 * Copyright 2007 by Brian R. Cowan http://www.briancowan.net/ * Marching Cube tables tables by Paul Bourke at http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ * Examples at http://www.briancowan.net/unity/fx * * Code provided as-is. You agree by using this code that I am not liable for any damage * it could possibly cause to you, your machine, or anything else. And the code is not meant * to be used for any medical uses or to run nuclear reactors or robots or such and so. * * Should be easily portable to any other language, all Unity Specific code is labeled so, * adapt it to any other environment. To use, attach the script to an empty object with a * Mesh Renderer and Mesh Filter. The created cubes will be one unit in size in XYZ. * Modify Update() and Start() to change the amount and movement and size of the blobs. * * Id love to see any project you use the code in. * * Mail any comments to: brian@briancowan.net (Vector on #IRC) * * Cheers & God bless */ /*Unity Specific*/ using UnityEngine; using System.Collections; public class MCBlob: MonoBehaviour { /*Amount of cubes in X/Y/Z directions, Dimension will always be from -.5f to .5f in XYZ remember to call Regen() if changing! */ int _dimX=30; int _dimY=30; int _dimZ=30; public int dimX { get {return _dimX; } set {_dimX=value; Regen(); } } public int dimY { get {return _dimY; } set {_dimY=value; Regen(); } } public int dimZ { get {return _dimZ; } set {_dimZ=value; Regen(); } } /*Blobs are a staggered array of floats, where first index is blob, and second is 0=x, 1=y 2=z 3=power Multidim might be slightly faster, but staggered made the code a little cleaner IMO*/ public float[][] blobs; /*Cutoff intensity, where the surface of mesh will be created*/ public float isoLevel=.5f; /*Scratch buffers for Vertices/Normals/Tris */ private Vector3[] newVertex; private Vector3[] newNormal; private Vector2[] newUV; private int[] newTri; /*Pointer into scratch buffers for tris and vertices(also used for UVs and Normals) at the end of a frame it will give the total amount of each*/ private int triP=0; private int vertP=0; /*Generated at startup for dimX,dimY,dimZ, all the points, edges, cubes in the 3D lattice*/ private mcPoint[] _points; private mcEdge[] _edges; private mcCube[] _cubes; /*Scratch buffers for use within functions, to eliminate the usage of new almost entirely each frame*/ private Vector3[] tada; private Vector2[] tada2; private int tadac,tadac2; private int tadam=50000; /*Current frame counter*/ private int pctr=0; /*Cube Class*/ private class mcCube { public mcCube() { cntr=0; edges=new mcEdge[12]; for(int i=0;i<12;i++) { edges[i]=null; } points=new mcPoint[8]; } /*12 Edges, see march() for their positioning*/ public mcEdge[] edges; /*8 Points, see march() for their positioning*/ public mcPoint[] points; /*last frame this cube was processed*/ public int cntr; /*Pointers into the latice array*/ public int px; public int py; public int pz; } /*Edge class*/ private class mcEdge { /*the vector of the calculated point*/ public Vector3 v3; /*index into newVertex/Normal/Uv of calculated point*/ public int vi; /*Last frame this was calculated at*/ public int cntr; /*axis of edge*/ public int axisI; public mcEdge(int axisI) { this.cntr=0; this.axisI=axisI; } } /*Point (in lattice) class*/ public class mcPoint { /*Calculated Intensity or Power of point*/ public float _i; public int px,py,pz; private MCBlob mcblob; public int cntr; /*Object Space position of point*/ public float[] index; public mcPoint(float x,float y,float z,int px,int py,int pz,MCBlob thismcblob) { this.index=new float[3]; index[0]=x;index[1]=y;index[2]=z; this.px=px; this.py=py; this.pz=pz; this.cntr=0; this.mcblob=thismcblob; } /*Axis letter accessors*/ public float x{ get{ return index[0]; } set{ index[0]=value; } } public float y{ get{ return index[1]; } set{ index[1]=value; } } public float z{ get{ return index[2]; } set{ index[2]=value; } } /*Calculate the power of a point only if it hasn't been calculated already for this frame*/ public float i() { float pwr; if(cntr=dimX || y>=dimY || z>=dimZ) {return null;} return _cubes[z+(y*(dimZ))+(x*(dimZ)*(dimY))]; } /*Given xyz indices into lattice, return referring vertex */ private mcPoint getPoint(int x,int y,int z) { if(x<0 || y<0 || z < 0 || x>dimX || y>dimY || z>dimZ) {return null;} return _points[z+(y*(dimZ+1))+(x*(dimZ+1)*(dimY+1))]; } /*Return the interpolated position of point on an Axis*/ private Vector3 mPos(mcPoint a,mcPoint b,int axisI) { float mu = (isoLevel - a.i()) / (b.i() - a.i()); Vector3 tmp=tada[tadac++]; tmp[0]=a[0];tmp[1]=a[1];tmp[2]=a[2]; tmp[axisI]=a[axisI]+(mu*(b[axisI]-a[axisI])); return tmp; } /*If an edge of a cube has not been processed, find the interpolated point for that edge (assumes the boundary crosses the edge) and compute the normal for that point, as well as assigning it an index into the vertex list*/ private void genEdge(mcCube cube,int edgei,int p1i,int p2i) { Vector3 v; mcEdge e=cube.edges[edgei]; if(e.cntrisoLevel) {cubeIndex|=1;} if(cube.points[1].i()>isoLevel) {cubeIndex|=2;} if(cube.points[2].i()>isoLevel) {cubeIndex|=4;} if(cube.points[3].i()>isoLevel) {cubeIndex|=8;} if(cube.points[4].i()>isoLevel) {cubeIndex|=16;} if(cube.points[5].i()>isoLevel) {cubeIndex|=32;} if(cube.points[6].i()>isoLevel) {cubeIndex|=64;} if(cube.points[7].i()>isoLevel) {cubeIndex|=128;} int edgeIndex=edgeTable[cubeIndex]; edgec+=edgeIndex; if(edgeIndex!=0) { if( (edgeIndex & 1) > 0) { genEdge(cube,0,0,1); } if( (edgeIndex & 2) > 0) { genEdge(cube,1,1,2);} if( (edgeIndex & 4) > 0) { genEdge(cube,2,2,3); } if( (edgeIndex & 0x8) > 0) { genEdge(cube,3,3,0); } if( (edgeIndex & 0x10) > 0) { genEdge(cube,4,4,5); } if( (edgeIndex & 0x20) > 0) { genEdge(cube,5,5,6); } if( (edgeIndex & 0x40) > 0) { genEdge(cube,6,6,7); } if( (edgeIndex & 0x80) > 0) { genEdge(cube,7,7,4); } if( (edgeIndex & 0x100) > 0) { genEdge(cube,8,0,4); } if( (edgeIndex & 0x200) > 0) { genEdge(cube,9,1,5); } if( (edgeIndex & 0x400) > 0) { genEdge(cube,10,2,6); } if( (edgeIndex & 0x800) > 0) { genEdge(cube,11,3,7); } int tpi=0; int tmp; while(triTable[cubeIndex,tpi]!=-1) { tmp=cube.edges[triTable[cubeIndex,tpi+2]].vi; newTri[triP++]=tmp;vertc+=tmp; tmp=cube.edges[triTable[cubeIndex,tpi+1]].vi; newTri[triP++]=tmp;vertc+=tmp; tmp=cube.edges[triTable[cubeIndex,tpi]].vi; newTri[triP++]=tmp;vertc+=tmp; tpi+=3; } return true; } else { return false; } } /*Recurse all the neighboring cubes where thy contain part of the surface*/ /*Counter to see how many cubes where processed*/ int cubec; private void recurseCube(mcCube cube) { mcCube nCube; int jx,jy,jz; jx=cube.px; jy=cube.py; jz=cube.pz; cubec++; /* Test 6 axis cases. This seems to work well, no need to test all 26 cases */ nCube=getCube(jx+1,jy,jz); if(nCube!=null && nCube.cntr=0) { mcCube cube=getCube(jx,jy,jz); if(cube!=null && cube.cntr