From 76e4be1dc1359dca67f15beadb18781edcd5d326 Mon Sep 17 00:00:00 2001 From: celisej567 Date: Thu, 23 Mar 2023 19:18:07 +0300 Subject: [PATCH] A lot of diffrerent things Now containers will have different meshes. Added Bouncing Ball in context menu. Added Metaballs namespace. Added QuakeFastSqrt function instead default sqrt in .compute shader. Fixed some bugs. Bug: actuall you can't use more then one Conteiner, because for some reason only one work at one time. It will show, but not updates. --- Assets/metaball/Scripts/BouncingBall.cs | 127 +++-- Assets/metaball/Scripts/Container.cs | 307 ++++++---- Assets/metaball/Scripts/ContextItem.cs | 29 +- Assets/metaball/Scripts/CubeGrid.cs | 539 ++++++++++-------- Assets/metaball/Scripts/MetaBall.cs | 57 +- .../MarchingCubesComputeShader.compute | 20 +- 6 files changed, 645 insertions(+), 434 deletions(-) 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);