diff --git a/Assets/metaball/Scripts/BouncingBall.cs b/Assets/metaball/Scripts/BouncingBall.cs index 691e5c2..d8344e3 100644 --- a/Assets/metaball/Scripts/BouncingBall.cs +++ b/Assets/metaball/Scripts/BouncingBall.cs @@ -1,66 +1,81 @@ using UnityEngine; using System.Collections; -public class BouncingBall : MetaBall { - public float speed; - - private Container container; - private Vector3 direction; - - private Vector3 lastPos; - - override public void OnDrawGizmos() +namespace MetaBalls +{ + public class BouncingBall : MetaBall { - base.OnDrawGizmos(); - } - public override void Start() { - base.Start(); - this.direction = Random.onUnitSphere; - this.container = this.GetComponentInParent(); - } + public float speed; - override public void Update() - { - base.Update(); - if (Application.isPlaying) + private Container container; + private Vector3 direction; + + private Vector3 lastPos; + + override public void OnDrawGizmos() { - if (transform.position != lastPos) + base.OnDrawGizmos(); + } + public override void Start() + { + base.Start(); + this.direction = Random.onUnitSphere; + this.container = this.GetComponentInParent(); + } + + override public void Update() + { + base.Update(); + if (Application.isPlaying) { - lastPos = transform.position; - this.updatePosition(Time.deltaTime); + if (transform.position != lastPos && speed != 0) + { + lastPos = transform.position; + this.updatePosition(Time.deltaTime); + } } } + + public void updatePosition(float dt) + { + float posX = this.transform.position.x, posY = this.transform.position.y, posZ = this.transform.position.z; + Vector3 containerPosition = this.container.transform.position; + Vector3 containerScale = this.container.transform.localScale; + + if (posX + this.radius + this.container.safeZone > containerPosition.x + containerScale.x / 2) + { + posX -= 0.01f; + this.direction = Vector3.Reflect(this.direction, Vector3.left); + } + else if (posX - this.radius - this.container.safeZone < containerPosition.x - containerScale.x / 2) + { + posX += 0.01f; + this.direction = Vector3.Reflect(this.direction, Vector3.right); + } + + if (posY + this.radius + this.container.safeZone > containerPosition.y + containerScale.y / 2) + { + posY -= 0.01f; + this.direction = Vector3.Reflect(this.direction, Vector3.down); + } + else if (posY - this.radius - this.container.safeZone < containerPosition.y - containerScale.y / 2) + { + posY += 0.01f; + this.direction = Vector3.Reflect(this.direction, Vector3.up); + } + + if (posZ + this.radius + this.container.safeZone > containerPosition.z + containerScale.z / 2) + { + posZ -= 0.01f; + this.direction = Vector3.Reflect(this.direction, Vector3.back); + } + else if (posZ - this.radius - this.container.safeZone < containerPosition.z - containerScale.z / 2) + { + posZ += 0.01f; + this.direction = Vector3.Reflect(this.direction, Vector3.forward); + } + + this.transform.position = new Vector3(posX, posY, posZ) + this.direction * speed * dt; + } } - - public void updatePosition(float dt) { - float posX = this.transform.position.x, posY = this.transform.position.y, posZ = this.transform.position.z; - Vector3 containerPosition = this.container.transform.position; - Vector3 containerScale = this.container.transform.localScale; - - if(posX + this.radius + this.container.safeZone > containerPosition.x + containerScale.x / 2) { - posX -= 0.01f; - this.direction = Vector3.Reflect(this.direction, Vector3.left); - } else if(posX - this.radius - this.container.safeZone < containerPosition.x - containerScale.x / 2) { - posX += 0.01f; - this.direction = Vector3.Reflect(this.direction, Vector3.right); - } - - if(posY + this.radius + this.container.safeZone > containerPosition.y + containerScale.y / 2) { - posY -= 0.01f; - this.direction = Vector3.Reflect(this.direction, Vector3.down); - } else if(posY - this.radius - this.container.safeZone < containerPosition.y - containerScale.y / 2) { - posY += 0.01f; - this.direction = Vector3.Reflect(this.direction, Vector3.up); - } - - if(posZ + this.radius + this.container.safeZone > containerPosition.z + containerScale.z / 2) { - posZ -= 0.01f; - this.direction = Vector3.Reflect(this.direction, Vector3.back); - } else if(posZ - this.radius - this.container.safeZone < containerPosition.z - containerScale.z / 2) { - posZ += 0.01f; - this.direction = Vector3.Reflect(this.direction, Vector3.forward); - } - - this.transform.position = new Vector3(posX, posY, posZ) + this.direction * speed * dt; - } -} +} \ No newline at end of file diff --git a/Assets/metaball/Scripts/Container.cs b/Assets/metaball/Scripts/Container.cs index 54d49b4..664bc06 100644 --- a/Assets/metaball/Scripts/Container.cs +++ b/Assets/metaball/Scripts/Container.cs @@ -1,142 +1,237 @@ using UnityEngine; -using UnityEditor; using System.Collections.Generic; -[ExecuteInEditMode] -public class Container : MonoBehaviour { - public float safeZone = 0.8f; - public float resolution = 0.2f; - public float threshold = 1; - public float updateDelay = 0.03f; - public ComputeShader computeShader; - public bool calculateNormals = true; - public Material material; +namespace MetaBalls +{ + [ExecuteInEditMode] + public class Container : MonoBehaviour + { - float lastsafeZone = 0.04f; - float lastresolution = 0.15f; - float lastthreshold = 1; - float nextUpdate; - Vector3 lastscale = new Vector3(1,1,1); + [Header("Variables")] + public float safeZone = 0.8f; + public float threshold = 1; + public float updateDelay = 0.03f; - MetaBall[] balls; //hehe - Vector3[] lastBallsPos; - bool update; - List lastPosBalls = new List(); + public ComputeShader computeShader; + public bool calculateNormals = true; + public Material material; - private bool message = true; + public float resolutionX = 0.2f; + public float resolutionY = 0.2f; + public float resolutionZ = 0.2f; - private CubeGrid grid; + float lastsafeZone = 0.04f; + float lastResolutionX = 0.2f; + float lastResolutionY = 0.2f; + float lastResolutionZ = 0.2f; + float lastthreshold = 1; + float nextUpdate; + Vector3 lastscale = new Vector3(1, 1, 1); - public void Start() { - balls = GetComponentsInChildren(); - nextUpdate = Time.time; - for (int i = 0; i<=balls.Length-1; i++) + MetaBall[] balls; //hehe + Vector3[] lastBallsPos; + List lastPosBalls = new List(); + MeshFilter meshFilter; + + private bool message = true; + private CubeGrid grid; + + Mesh mesh; + Mesh mySharedMesh; + Mesh myMesh; + + bool CheckForChangedResolution() { - lastPosBalls.Add(balls[i].transform.position); + if ((resolutionX != lastResolutionX) || (resolutionY != lastResolutionY) || (resolutionZ != lastResolutionZ)) + { + lastResolutionX = resolutionX; + lastResolutionY = resolutionY; + lastResolutionZ = resolutionZ; + return true; + } + return false; } - this.grid = new CubeGrid(this, this.computeShader); - } - - private void Awake() - { - Start(); - } - - private void OnDrawGizmos() - { - Gizmos.color = Color.yellow; - Gizmos.DrawWireCube(gameObject.transform.position, gameObject.transform.localScale); - - Gizmos.color = Color.red; - Vector3 safeScale = new Vector3(gameObject.transform.localScale.x - safeZone, gameObject.transform.localScale.y - safeZone, gameObject.transform.localScale.z - safeZone); - Gizmos.DrawWireCube(gameObject.transform.position, safeScale); - - Gizmos.color = Color.blue; - Gizmos.DrawLine(transform.position, transform.position + (transform.forward * 2)); - - Gizmos.color = Color.red; - Gizmos.DrawLine(transform.position, transform.position + (transform.right * 2)); - - Gizmos.color = Color.green; - Gizmos.DrawLine(transform.position, transform.position + (transform.up * 2)); - } - - public void LateUpdate() - { - if (Time.time >= nextUpdate) + bool CheckForBallsPos() { - if (Application.isPlaying) + for (int i = 0; i <= balls.Length - 1; i++) { - for (int i = 0; i <= balls.Length - 1; i++) + if (lastPosBalls[i] != balls[i].transform.position) { - if (lastPosBalls[i] != balls[i].transform.position) - { - update = true; - lastPosBalls[i] = balls[i].gameObject.transform.position; - } + lastPosBalls[i] = balls[i].gameObject.transform.position; + return true; } + } + return false; + } - if (update || (lastsafeZone != safeZone) || (lastresolution != resolution) || (lastthreshold != threshold) || (lastscale != transform.localScale)) + Vector3[] Smooth(Vector3[] verts, int[] triangles) + { + Vector3[] normals = new Vector3[verts.Length]; + List[] vertexNormals = new List[verts.Length]; + for (int i = 0; i < vertexNormals.Length; i++) + { + vertexNormals[i] = new List(); + } + for (int i = 0; i < triangles.Length; i += 3) + { + Vector3 currNormal = Vector3.Cross( + (verts[triangles[i + 1]] - verts[triangles[i]]).normalized, + (verts[triangles[i + 2]] - verts[triangles[i]]).normalized); + + vertexNormals[triangles[i]].Add(currNormal); + vertexNormals[triangles[i + 1]].Add(currNormal); + vertexNormals[triangles[i + 2]].Add(currNormal); + } + for (int i = 0; i < vertexNormals.Length; i++) + { + normals[i] = Vector3.zero; + float numNormals = vertexNormals[i].Count; + for (int j = 0; j < numNormals; j++) { - if (grid == null || (lastscale != transform.localScale) || (resolution != lastresolution)) + normals[i] += vertexNormals[i][j]; + } + normals[i] /= numNormals; + } + + return normals; + } + + Vector3[] CalculateNormals(Mesh mesh) + { + + Vector3[] normals = mesh.normals; + int[] trigs = mesh.triangles; + + for (int i = 0; i < trigs.Length; i += 3) + { + + Vector3 avg = (normals[trigs[i]] + normals[trigs[i + 1]] + normals[trigs[i + 2]]) / 3; + normals[trigs[i]] = avg; + normals[trigs[i + 1]] = avg; + normals[trigs[i + 2]] = avg; + + } + + return normals; + + } + + public void Start() + { + balls = GetComponentsInChildren(); + + meshFilter = GetComponent(); + + mesh = meshFilter.sharedMesh; + + mesh = new Mesh(); + mesh.name = "MetaBalls " + Random.Range(int.MinValue, int.MaxValue); + + this.GetComponent().sharedMesh = mesh; + this.GetComponent().mesh = mesh; + + nextUpdate = Time.time; + for (int i = 0; i <= balls.Length - 1; i++) + { + lastPosBalls.Add(balls[i].transform.position); + } + + lastResolutionX = resolutionX; + lastResolutionY = resolutionY; + lastResolutionZ = resolutionZ; + } + + private void Awake() + { + Start(); + } + + private void OnDrawGizmos() + { + + Vector3 scale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z); + + Gizmos.color = Color.yellow; + Gizmos.DrawWireCube(gameObject.transform.position, scale); + + Gizmos.color = Color.red; + Vector3 safeScale = new Vector3(scale.x - safeZone, scale.y - safeZone, scale.z - safeZone); + Gizmos.DrawWireCube(gameObject.transform.position, safeScale); + + Gizmos.color = Color.blue; + Gizmos.DrawLine(transform.position, transform.position + (transform.forward * 2)); + + Gizmos.color = Color.red; + Gizmos.DrawLine(transform.position, transform.position + (transform.right * 2)); + + Gizmos.color = Color.green; + Gizmos.DrawLine(transform.position, transform.position + (transform.up * 2)); + } + + public void LateUpdate() + { + if (Time.time >= nextUpdate) + { + if (Application.isPlaying) + { + + if (CheckForBallsPos() || (lastsafeZone != safeZone) || (lastthreshold != threshold) || (lastscale != transform.localScale) || CheckForChangedResolution()) { - this.grid = new CubeGrid(this, this.computeShader); + if (grid == null || (lastscale != transform.localScale)) + { + this.grid = new CubeGrid(this, this.computeShader); + } + + + this.grid.evaluateAll(balls); + + GetComponent().material = material; + + mesh.Clear(); + mesh.vertices = this.grid.vertices.ToArray(); + mesh.triangles = this.grid.getTriangles(); + meshFilter.mesh = mesh; + + if (this.calculateNormals) + { + mesh.RecalculateNormals(); + } + + lastsafeZone = safeZone; + lastthreshold = threshold; + lastscale = transform.localScale; } + } + else + { + balls = GetComponentsInChildren(); + if (balls == null) + return; + + GetComponent().sharedMaterial = material; + + this.grid = new CubeGrid(this, this.computeShader); + this.grid.evaluateAll(balls); - Mesh mesh = this.GetComponent().mesh; mesh.Clear(); mesh.vertices = this.grid.vertices.ToArray(); mesh.triangles = this.grid.getTriangles(); + meshFilter.sharedMesh = mesh; if (this.calculateNormals) { mesh.RecalculateNormals(); } - lastsafeZone = safeZone; - lastresolution = resolution; - lastthreshold = threshold; - lastscale = transform.localScale; - update = false; + } + nextUpdate = Time.time + updateDelay; + } - else - { - balls = GetComponentsInChildren(); - if (balls == null) - return; - - if (GetComponent().material != material) - GetComponent().material = material; - - this.grid = new CubeGrid(this, this.computeShader); - - this.grid.evaluateAll(balls); - Mesh mesh; - - mesh = this.GetComponent().mesh; - if (!Application.isPlaying && message) - { - Debug.Log("If you got \"Please use MeshFilter.sharedMesh instead.\" warning - ignore it."); - message = false; - } - - mesh.Clear(); - mesh.vertices = this.grid.vertices.ToArray(); - mesh.triangles = this.grid.getTriangles(); - - if (this.calculateNormals) - { - mesh.RecalculateNormals(); - } - } - - nextUpdate = Time.time + updateDelay; - } } - } \ No newline at end of file +} \ No newline at end of file diff --git a/Assets/metaball/Scripts/ContextItem.cs b/Assets/metaball/Scripts/ContextItem.cs index a651e34..4acf2c2 100644 --- a/Assets/metaball/Scripts/ContextItem.cs +++ b/Assets/metaball/Scripts/ContextItem.cs @@ -3,14 +3,15 @@ using System; using UnityEngine; using UnityEditor; using UnityEditor.SceneManagement; +using MetaBalls; public class ContextItem { [MenuItem("GameObject/Metaballs/Volume")] public static void CreateMetaballVoid(MenuCommand menuCommand) { - GameObject newObject = ObjectFactory.CreateGameObject("Metaballs", typeof(Container)); // spawn - newObject.AddComponent(); newObject.AddComponent(); newObject.GetComponent().material = null; //Adding things for render + GameObject newObject = ObjectFactory.CreateGameObject("Metaballs", typeof(MeshRenderer)); // spawn + newObject.AddComponent(); newObject.GetComponent().material = null; newObject.AddComponent(); //Adding things for render newObject.transform.localScale = new Vector3(5, 5, 5); SceneView lastView = SceneView.lastActiveSceneView; @@ -19,12 +20,13 @@ public class ContextItem StageUtility.PlaceGameObjectInCurrentStage(newObject); GameObjectUtility.EnsureUniqueNameForSibling(newObject); - EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); + if(!Application.isPlaying) + EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } [MenuItem("GameObject/Metaballs/Ball")] - public static void CreateBallVoid(MenuCommand menuCommand) + public static void CreateBall(MenuCommand menuCommand) { GameObject newObject = ObjectFactory.CreateGameObject("Ball", typeof(MetaBall)); // spawn @@ -35,7 +37,24 @@ public class ContextItem GameObjectUtility.EnsureUniqueNameForSibling(newObject); - EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); + if (!Application.isPlaying) + EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); + } + + [MenuItem("GameObject/Metaballs/Bouncing Ball")] + public static void CreateBouncingBall(MenuCommand menuCommand) + { + GameObject newObject = ObjectFactory.CreateGameObject("Ball", typeof(BouncingBall)); // spawn + + SceneView lastView = SceneView.lastActiveSceneView; + newObject.transform.position = lastView ? lastView.pivot : Vector3.zero; // spawn position + + StageUtility.PlaceGameObjectInCurrentStage(newObject); + GameObjectUtility.EnsureUniqueNameForSibling(newObject); + + + if (!Application.isPlaying) + EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } } #endif diff --git a/Assets/metaball/Scripts/CubeGrid.cs b/Assets/metaball/Scripts/CubeGrid.cs index 0c729d0..9f6413b 100644 --- a/Assets/metaball/Scripts/CubeGrid.cs +++ b/Assets/metaball/Scripts/CubeGrid.cs @@ -2,152 +2,177 @@ using System.Collections; using System.Collections.Generic; -struct GPUEdgeValues { - public float edge0Val, edge1Val, edge2Val, edge3Val, edge4Val, edge5Val, edge6Val, edge7Val; -} - -struct GPUPositions { - public Vector3 centerPos; - public Vector3 edge0Pos, edge1Pos, edge2Pos, edge3Pos, edge4Pos, edge5Pos, edge6Pos, edge7Pos; -} - -struct GPUBall { - public float factor; - public Vector3 position; -} - -struct GPUEdgeVertices { - public int index; - public Vector3 edge0, edge1, edge2, edge3, edge4, edge5, edge6, edge7, edge8, edge9, edge10, edge11; -}; - -public class CubeGrid { - public List vertices; - public int width, height, depth; - - private Container container; - - private ComputeShader shader; - private int shaderKernel; - - private ComputeBuffer positionsBuffer; - private ComputeBuffer valuesBuffer; - private ComputeBuffer metaballsBuffer; - private ComputeBuffer edgeMapBuffer; - private ComputeBuffer verticesBuffer; - - private GPUPositions[] precomputedPositions; - - private bool initized; - - // - // Constructor - // - - public CubeGrid(Container container, ComputeShader shader) { - this.container = container; - this.shader = shader; - - this.width = Mathf.RoundToInt(container.transform.localScale.x / this.container.resolution); - this.height = Mathf.RoundToInt(container.transform.localScale.y / this.container.resolution); - this.depth = Mathf.RoundToInt(container.transform.localScale.z / this.container.resolution); - - this.vertices = new List(); - - this.initized = false; +namespace MetaBalls +{ + struct GPUEdgeValues + { + public float edge0Val, edge1Val, edge2Val, edge3Val, edge4Val, edge5Val, edge6Val, edge7Val; } - // - // Public methods - // + struct GPUPositions + { + public Vector3 centerPos; + public Vector3 edge0Pos, edge1Pos, edge2Pos, edge3Pos, edge4Pos, edge5Pos, edge6Pos, edge7Pos; + } - public void evaluateAll(MetaBall[] metaballs) { - if(!this.initized) { + struct GPUBall + { + public float factor; + public Vector3 position; + } - this.init(); - } + struct GPUEdgeVertices + { + public int index; + public Vector3 edge0, edge1, edge2, edge3, edge4, edge5, edge6, edge7, edge8, edge9, edge10, edge11; + }; - this.vertices.Clear(); - - // write info about metaballs in format readable by compute shaders - GPUBall[] gpuBalls = new GPUBall[metaballs.Length]; - for(int i = 0; i < metaballs.Length; i++) { - MetaBall metaball = metaballs[i]; - gpuBalls[i].position = metaball.transform.localPosition; - gpuBalls[i].factor = metaball.factor; - } - - // magic happens here - GPUEdgeVertices[] edgeVertices = this.runComputeShader(gpuBalls); + public class CubeGrid + { + public List vertices; + public readonly int width, height, depth; - // perform rest of the marching cubes algorithm - for (int x = 0; x < this.width; x++) + private Container container; + + private ComputeShader shader; + private int shaderKernel; + + private ComputeBuffer positionsBuffer; + private ComputeBuffer valuesBuffer; + private ComputeBuffer metaballsBuffer; + private ComputeBuffer edgeMapBuffer; + private ComputeBuffer verticesBuffer; + + private GPUPositions[] precomputedPositions; + + private bool initized; + + // + // Constructor + // + + public CubeGrid(Container container, ComputeShader shader) { - for (int y = 0; y < this.height; y++) + this.container = container; + this.shader = shader; + + this.width = Mathf.RoundToInt(container.transform.localScale.x / this.container.resolutionX); + this.height = Mathf.RoundToInt(container.transform.localScale.y / this.container.resolutionY); + this.depth = Mathf.RoundToInt(container.transform.localScale.z / this.container.resolutionZ); + + this.vertices = new List(this.width * this.height * this.depth * 3); + + this.initized = false; + } + + // + // Public methods + // + + public void evaluateAll(MetaBall[] metaballs) + { + if (!this.initized) { - for (int z = 0; z < this.depth; z++) + + this.init(); + } + + this.vertices.Clear(); + + // write info about metaballs in format readable by compute shaders + GPUBall[] gpuBalls = new GPUBall[metaballs.Length]; + for (int i = 0; i < metaballs.Length; i++) + { + MetaBall metaball = metaballs[i]; + gpuBalls[i].position = metaball.transform.localPosition; + gpuBalls[i].factor = metaball.factor; + } + + // magic happens here + GPUEdgeVertices[] edgeVertices = this.runComputeShader(gpuBalls); + + // perform rest of the marching cubes algorithm + int hh = 0; + for (int x = 0; x < this.width; x++) + { + for (int y = 0; y < this.height; y++) { - this.updateVertices2(edgeVertices[x + this.width * (y + this.height * z)]); + for (int z = 0; z < this.depth; z++) + { + this.updateVertices2(edgeVertices[x + this.width * (y + this.height * z)]); + } } } + + } + public int[] getTriangles() + { + int num = this.vertices.Count; - } - - public int[] getTriangles() { - int num = this.vertices.Count; + if (this.triangleBuffer == null) + { + // nothing in buffer, create it + this.triangleBuffer = new List(); + for (int i = 0; i < num; i++) + { + this.triangleBuffer.Add(i); + } - if(this.triangleBuffer == null) { - // nothing in buffer, create it - this.triangleBuffer = new List(); - for(int i = 0; i < num; i++) { - this.triangleBuffer.Add(i); + return this.triangleBuffer.ToArray(); } + else if (this.triangleBuffer.Count < num) + { + // missing elements in buffer, add them + for (int i = this.triangleBuffer.Count; i < num; i++) + { + this.triangleBuffer.Add(i); + } - return this.triangleBuffer.ToArray(); - } else if(this.triangleBuffer.Count < num) { - // missing elements in buffer, add them - for(int i = this.triangleBuffer.Count; i < num; i++) { - this.triangleBuffer.Add(i); + return this.triangleBuffer.ToArray(); } + else if (this.triangleBuffer.Count == num) + { + // buffer is of perfect size, just return it - return this.triangleBuffer.ToArray(); - } else if(this.triangleBuffer.Count == num) { - // buffer is of perfect size, just return it + return this.triangleBuffer.ToArray(); + } + else + { + // buffer is too long, return slice - return this.triangleBuffer.ToArray(); - } else { - // buffer is too long, return slice - - return this.triangleBuffer.GetRange(0, num).ToArray(); + return this.triangleBuffer.GetRange(0, num).ToArray(); + } } - } - public void destroy() { - this.positionsBuffer.Release(); - this.valuesBuffer.Release(); - this.metaballsBuffer.Release(); - this.edgeMapBuffer.Release(); - this.verticesBuffer.Release(); - this.triangleBuffer = null; - } + public void destroy() + { + this.positionsBuffer.Release(); + this.valuesBuffer.Release(); + this.metaballsBuffer.Release(); + this.edgeMapBuffer.Release(); + this.verticesBuffer.Release(); + this.triangleBuffer = null; + } - // - // Setup - // + // + // Setup + // - private void init() { - this.instantiateEdgeMap(); - this.instantiatePositionMap(); - this.instantiateGPUPositions(); - this.instantiateComputeShader(); + private void init() + { + this.instantiateEdgeMap(); + this.instantiatePositionMap(); + this.instantiateGPUPositions(); + this.instantiateComputeShader(); - this.initized = true; - } + this.initized = true; + } - private void instantiateEdgeMap() { - this.edgeMap = new Vector3[] { + private void instantiateEdgeMap() + { + this.edgeMap = new Vector3[] { new Vector3(-1, -1, -1), new Vector3(1, -1, -1), new Vector3(1, 1, -1), @@ -158,152 +183,185 @@ public class CubeGrid { new Vector3(-1, 1, 1) }; - // scale edge map - for(int i = 0; i < 8; i++) { - this.edgeMap[i] /= 2; - this.edgeMap[i] = new Vector3(this.edgeMap[i].x / ((float) this.width), - this.edgeMap[i].y / ((float) this.height), - this.edgeMap[i].z / ((float) this.depth)); + // scale edge map + for (int i = 0; i < 8; i++) + { + this.edgeMap[i] /= 2; + this.edgeMap[i] = new Vector3(this.edgeMap[i].x / ((float)this.width), + this.edgeMap[i].y / ((float)this.height), + this.edgeMap[i].z / ((float)this.depth)); + } } - } - private void instantiatePositionMap() { - this.positionMap = new Vector3[width,height,depth]; + private void instantiatePositionMap() + { + this.positionMap = new Vector3[width, height, depth]; - for(int x = 0; x < this.width; x++) { - for(int y = 0; y < this.height; y++) { - for(int z = 0; z < this.depth; z++) { + for (int x = 0; x < this.width; x++) + { + for (int y = 0; y < this.height; y++) + { + for (int z = 0; z < this.depth; z++) + { - float xCoord = (((float) x) / ((float) this.width)) - 0.475f ; - float yCoord = (((float) y) / ((float) this.height)) - 0.475f; - float zCoord = (((float) z) / ((float) this.depth)) - 0.46f; + float xCoord = (((float)x) / ((float)this.width)) - 0.475f; + float yCoord = (((float)y) / ((float)this.height)) - 0.475f; + float zCoord = (((float)z) / ((float)this.depth)) - 0.46f; - this.positionMap[x , y , z] = new Vector3(xCoord, yCoord, zCoord); + this.positionMap[x, y, z] = new Vector3(xCoord, yCoord, zCoord); + } } } } - } - private void instantiateGPUPositions() - { - this.precomputedPositions = new GPUPositions[this.width * this.height * this.depth]; - - for (int x = 0; x < this.width; x++) + private void instantiateGPUPositions() { - for (int y = 0; y < this.height; y++) + this.precomputedPositions = new GPUPositions[this.width * this.height * this.depth]; + + for (int x = 0; x < this.width; x++) { - for (int z = 0; z < this.depth; z++) + for (int y = 0; y < this.height; y++) { - Vector3 centerPoint = this.positionMap[x, y, z]; - this.precomputedPositions[x + this.width * (y + this.height * z)].centerPos = centerPoint; - - this.precomputedPositions[x + this.width * (y + this.height * z)].edge0Pos = centerPoint + this.edgeMap[0]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge1Pos = centerPoint + this.edgeMap[1]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge2Pos = centerPoint + this.edgeMap[2]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge3Pos = centerPoint + this.edgeMap[3]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge4Pos = centerPoint + this.edgeMap[4]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge5Pos = centerPoint + this.edgeMap[5]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge6Pos = centerPoint + this.edgeMap[6]; - this.precomputedPositions[x + this.width * (y + this.height * z)].edge7Pos = centerPoint + this.edgeMap[7]; - } + for (int z = 0; z < this.depth; z++) + { + Vector3 centerPoint = this.positionMap[x, y, z]; + this.precomputedPositions[x + this.width * (y + this.height * z)].centerPos = centerPoint; + + this.precomputedPositions[x + this.width * (y + this.height * z)].edge0Pos = centerPoint + this.edgeMap[0]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge1Pos = centerPoint + this.edgeMap[1]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge2Pos = centerPoint + this.edgeMap[2]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge3Pos = centerPoint + this.edgeMap[3]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge4Pos = centerPoint + this.edgeMap[4]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge5Pos = centerPoint + this.edgeMap[5]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge6Pos = centerPoint + this.edgeMap[6]; + this.precomputedPositions[x + this.width * (y + this.height * z)].edge7Pos = centerPoint + this.edgeMap[7]; + } + } } } - } - private void instantiateComputeShader() { - // setup buffers - this.positionsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 108); - this.positionsBuffer.SetData(this.precomputedPositions); + private void instantiateComputeShader() + { + // setup buffers + this.positionsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 108); + this.positionsBuffer.SetData(this.precomputedPositions); - this.edgeMapBuffer = new ComputeBuffer(8, 12); - this.edgeMapBuffer.SetData(this.edgeMap); + this.edgeMapBuffer = new ComputeBuffer(8, 12); + this.edgeMapBuffer.SetData(this.edgeMap); - this.verticesBuffer = new ComputeBuffer(this.precomputedPositions.Length, 148); - this.metaballsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 16); + this.verticesBuffer = new ComputeBuffer(this.precomputedPositions.Length, 148); + this.metaballsBuffer = new ComputeBuffer(this.precomputedPositions.Length, 16); - // and assign them to compute shader buffer - this.shaderKernel = this.shader.FindKernel("Calculate"); + // and assign them to compute shader buffer + this.shaderKernel = this.shader.FindKernel("Calculate"); - this.shader.SetBuffer(this.shaderKernel, "positions", this.positionsBuffer); - this.shader.SetBuffer(this.shaderKernel, "metaballs", this.metaballsBuffer); - this.shader.SetBuffer(this.shaderKernel, "edgeMap", this.edgeMapBuffer); - this.shader.SetBuffer(this.shaderKernel, "edgeVertices", this.verticesBuffer); - } - - // - // GPU metaball falloff function summator & part of marching cubes algorithm - // - - private GPUEdgeVertices[] runComputeShader(GPUBall[] gpuBalls) { - // pass data to the compute shader - this.metaballsBuffer.SetData(gpuBalls); - this.shader.SetInt("numMetaballs", gpuBalls.Length); - this.shader.SetInt("width", this.width); - this.shader.SetInt("height", this.height); - this.shader.SetFloat("threshold", this.container.threshold); - - // Run, Forrest, run! - this.shader.Dispatch(this.shaderKernel, this.width, this.height / 8, this.depth / 8); - - // parse returned vertex data and return it - GPUEdgeVertices[] output = new GPUEdgeVertices[this.verticesBuffer.count]; - this.verticesBuffer.GetData(output); - return output; - } - - // - // Rest of marching cubes algorithm (on CPU) - // - - private void updateVertices2(GPUEdgeVertices vert) { - int cubeIndex = vert.index; - - for(int k = 0; triTable[cubeIndex][k] != -1; k += 3) { - this.vertices.Add(this.findVertex(vert, this.triTable[cubeIndex][k])); - this.vertices.Add(this.findVertex(vert, this.triTable[cubeIndex][k + 2])); - this.vertices.Add(this.findVertex(vert, this.triTable[cubeIndex][k + 1])); + this.shader.SetBuffer(this.shaderKernel, "positions", this.positionsBuffer); + this.shader.SetBuffer(this.shaderKernel, "metaballs", this.metaballsBuffer); + this.shader.SetBuffer(this.shaderKernel, "edgeMap", this.edgeMapBuffer); + this.shader.SetBuffer(this.shaderKernel, "edgeVertices", this.verticesBuffer); } - } - private Vector3 findVertex(GPUEdgeVertices vert, int i) { - if(i == 0) { - return vert.edge0; - } else if(i == 1) { - return vert.edge1; - } else if(i == 2) { - return vert.edge2; - } else if(i == 3) { - return vert.edge3; - } else if(i == 4) { - return vert.edge4; - } else if(i == 5) { - return vert.edge5; - } else if(i == 6) { - return vert.edge6; - } else if(i == 7) { - return vert.edge7; - } else if(i == 8) { - return vert.edge8; - } else if(i == 9) { - return vert.edge9; - } else if(i == 10) { - return vert.edge10; - } else { - return vert.edge11; + // + // GPU metaball falloff function summator & part of marching cubes algorithm + // + + private GPUEdgeVertices[] runComputeShader(GPUBall[] gpuBalls) + { + // pass data to the compute shader + this.metaballsBuffer.SetData(gpuBalls); + this.shader.SetInt("numMetaballs", gpuBalls.Length); + this.shader.SetInt("width", this.width); + this.shader.SetInt("height", this.height); + this.shader.SetFloat("threshold", this.container.threshold); + + // Run, Forrest, run! + this.shader.Dispatch(this.shaderKernel, this.width, this.height, this.depth); + + // parse returned vertex data and return it + GPUEdgeVertices[] output = new GPUEdgeVertices[this.verticesBuffer.count]; + this.verticesBuffer.GetData(output); + return output; } - } - // - // LOOKUP TABLES - // + // + // Rest of marching cubes algorithm (on CPU) + // - private List triangleBuffer; + private void updateVertices2(GPUEdgeVertices vert) + { + int cubeIndex = vert.index; - private Vector3[,,] positionMap; + for (int k = 0; triTable[cubeIndex][k] != -1; k += 3) + { + this.vertices.Add(this.findVertex(vert, CubeGrid.triTable[cubeIndex][k])); + this.vertices.Add(this.findVertex(vert, CubeGrid.triTable[cubeIndex][k + 2])); + this.vertices.Add(this.findVertex(vert, CubeGrid.triTable[cubeIndex][k + 1])); + } + } - private Vector3[] edgeMap; - - private int[][] triTable = { + private Vector3 findVertex(GPUEdgeVertices vert, int i) + { + if (i == 0) + { + return vert.edge0; + } + else if (i == 1) + { + return vert.edge1; + } + else if (i == 2) + { + return vert.edge2; + } + else if (i == 3) + { + return vert.edge3; + } + else if (i == 4) + { + return vert.edge4; + } + else if (i == 5) + { + return vert.edge5; + } + else if (i == 6) + { + return vert.edge6; + } + else if (i == 7) + { + return vert.edge7; + } + else if (i == 8) + { + return vert.edge8; + } + else if (i == 9) + { + return vert.edge9; + } + else if (i == 10) + { + return vert.edge10; + } + else + { + return vert.edge11; + } + } + + // + // LOOKUP TABLES + // + + private List triangleBuffer; + + private Vector3[,,] positionMap; + + private Vector3[] edgeMap; + + static private readonly int[][] triTable = { new int[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, new int[] {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, new int[] {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, @@ -561,4 +619,5 @@ public class CubeGrid { new int[] {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, new int[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} }; + } } diff --git a/Assets/metaball/Scripts/MetaBall.cs b/Assets/metaball/Scripts/MetaBall.cs index 9b27488..00e0e13 100644 --- a/Assets/metaball/Scripts/MetaBall.cs +++ b/Assets/metaball/Scripts/MetaBall.cs @@ -1,37 +1,42 @@ using UnityEngine; using System.Collections; -[ExecuteInEditMode] -public class MetaBall : MonoBehaviour { - public float radius = 0.09f; - public bool negativeBall; - - [HideInInspector] - public float factor = 1; - Vector3 voidSize; - - public bool DrawGizmos; - - public virtual void OnDrawGizmos() +namespace MetaBalls +{ + [ExecuteInEditMode] + public class MetaBall : MonoBehaviour { - if (!DrawGizmos) - return; + public float radius = 0.09f; + public bool negativeBall; - if(voidSize != GetComponentInParent().transform.localScale) + [HideInInspector] + public float factor = 1; + Vector3 voidSize; + + public bool DrawGizmos; + + public virtual void OnDrawGizmos() { - voidSize = GetComponentInParent().transform.localScale; + if (!DrawGizmos) + return; + + if (voidSize != GetComponentInParent().transform.localScale) + { + voidSize = GetComponentInParent().transform.localScale; + } + + Gizmos.color = Color.green; + Gizmos.DrawWireSphere(transform.position, radius * Mathf.Min(voidSize.x, voidSize.y, voidSize.z)); } - Gizmos.color = Color.green; - Gizmos.DrawWireSphere(transform.position, radius * Mathf.Min(voidSize.x, voidSize.y, voidSize.z)); - } + public virtual void Start() + { + this.factor = (this.negativeBall ? -1 : 1) * this.radius * this.radius; + } - public virtual void Start() { - this.factor = (this.negativeBall ? -1 : 1) * this.radius * this.radius; - } - - public virtual void Update() - { - this.factor = (this.negativeBall ? -1 : 1) * this.radius * this.radius; + public virtual void Update() + { + this.factor = (this.negativeBall ? -1 : 1) * this.radius * this.radius; + } } } diff --git a/Assets/metaball/Shaders/MarchingCubesComputeShader.compute b/Assets/metaball/Shaders/MarchingCubesComputeShader.compute index 8b1d0d0..9831a55 100644 --- a/Assets/metaball/Shaders/MarchingCubesComputeShader.compute +++ b/Assets/metaball/Shaders/MarchingCubesComputeShader.compute @@ -38,6 +38,7 @@ RWStructuredBuffer edgeVertices; int width; int height; + int numMetaballs; float threshold; @@ -176,12 +177,26 @@ Values zeroValuesStruct() { // field function evaluation // +float QuakeFastSqrt(float original_number) +{ + float orig_half = 0.5f * original_number; + + int i = (int) original_number; + i = 0x5f3759df - (i >> 1); + + original_number = (float)i; + original_number = original_number * ( 1.5f - ( orig_half * original_number * original_number)); + return original_number; + +} + float metaballFalloffFunction(float factor, float3 dist) { #ifdef GENERIC_METABALL_FUNCTION return factor / (dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); #endif #ifdef ELECTRIC_POTENTIAL_FUNCTION - return 8.987551788e9 * factor / sqrt(dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); + //return 8.987551788e9 * factor / sqrt(dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); + return 8.987551788e9 * factor / QuakeFastSqrt(dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); #endif } @@ -223,8 +238,11 @@ Values evaluateFieldFunction(int pos) { [numthreads(8,8,8)] void Calculate(uint3 id : SV_DispatchThreadID) { + + int pos = id.x + width * (id.y + height * id.z); + // find scalar values of a field on this position Values edgeValues = evaluateFieldFunction(pos);