Main Page
(Deformation Smooth Filters for Meshes) |
|||
Line 25: | Line 25: | ||
− | == TestSmoothFilter == | + | == TestSmoothFilter.cs == |
<javascript> | <javascript> | ||
using UnityEngine; | using UnityEngine; |
Revision as of 10:24, 10 January 2011
Contents |
Introduction
This c# code demonstrates how to deformate a mesh at runtime using smoothing algorithms.
To solve this problem in a reasonable computational time a good algorithm is needed to solve the adjacent vertex problem. Unity lacks mesh manipulation utilities and some manual effort is required to achieve this.
(Note: I'm no unity expert so please don't shoot the piano player he is trying his best and improve anything.)
Basic Theory
Given a Mesh/SkinnedMeshRenderer Vector3[] v = mesh.vertices, and int[] t = mesh.triangles.
For each vertex i in the mesh, find the set of neighboring vertices.
Computing the Laplacian Smooth Filter p[i] = ( 1 / number of adjacent vertices ) * summation of the adjacent vertices.
Laplacian smoothing introduces shrinkage, so the HC-Algorithm is applied as an extension to reduce this issue. (More computationally expensive though !).
General Discussion
Although the code demonstrates smoothing meshes, it provides a more generic strategy for manipulating meshes in Unity as many deformation algorithms could be applied. Have Fun!
TestSmoothFilter.cs
<javascript> using UnityEngine; using System.Collections;
/* Apply to any meshed gameobject for smoothing.
Works also by replacing MeshFilter with SkinnedMeshRenderer and use sharedMesh
At present tests Laplacian Smooth Filter and HC Reduced Shrinkage Variant Filter
- /
public class TestSmoothFilter : MonoBehaviour {
private Mesh sourceMesh; private Mesh workingMesh;
void Start () { MeshFilter meshFilter = gameObject.GetComponentInChildren<MeshFilter>();
// Clone the cloth mesh to work on sourceMesh = new Mesh(); // Get the sourceMesh sourceMesh = meshFilter.mesh; // Clone the sourceMesh workingMesh = CloneMesh(sourceMesh); // Reference workingMesh to see deformations skinnedmesh.mesh = workingMesh;
// Apply Laplacian Smoothing Filter to Mesh
int iterations = 1;
for(int i=0; i<iterations; i++)
workingMesh.vertices = SmoothFilter.laplacianFilter(workingMesh.vertices, workingMesh.triangles);
//workingMesh.vertices = hcFilter(sourceMesh.vertices, workingMesh.vertices, workingMesh.triangles, 0.0f, 0.5f);
}
// Clone a mesh
private static Mesh CloneMesh(Mesh mesh) { Mesh clone = new Mesh(); clone.vertices = mesh.vertices; clone.normals = mesh.normals; clone.tangents = mesh.tangents; clone.triangles = mesh.triangles; clone.uv = mesh.uv; clone.uv1 = mesh.uv1; clone.uv2 = mesh.uv2; clone.bindposes = mesh.bindposes; clone.boneWeights = mesh.boneWeights; clone.bounds = mesh.bounds; clone.colors = mesh.colors; clone.name = mesh.name; //TODO : Are we missing anything? return clone; }
}</javascript>
SmoothFilter.cs
<javascript> using UnityEngine; using System.Collections; using System.Collections.Generic;
/*
MeshSmoothTest
Laplacian Smooth Filter, HC-Smooth Filter
MarkGX, Jan 2011
- /
public class SmoothFilter : MonoBehaviour { /* Standard Laplacian Smooth Filter */ public static Vector3[] laplacianFilter(Vector3[] sv, int[] t) { Vector3[] wv = new Vector3[sv.Length]; List<Vector3> adjacentVertices = new List<Vector3>();
float dx = 0.0f; float dy = 0.0f; float dz = 0.0f;
for (int vi=0; vi< sv.Length; vi++) { // Find the sv neighboring vertices adjacentVertices = MeshUtils.findAdjacentNeighbors (sv, t, sv[vi]);
if (adjacentVertices.Count != 0) { dx = 0.0f; dy = 0.0f; dz = 0.0f;
//Debug.Log("Vertex Index Length = "+vertexIndexes.Length); // Add the vertices and divide by the number of vertices for (int j=0; j<adjacentVertices.Count; j++) { dx += adjacentVertices[j].x; dy += adjacentVertices[j].y; dz += adjacentVertices[j].z; }
wv[vi].x = dx / adjacentVertices.Count; wv[vi].y = dy / adjacentVertices.Count; wv[vi].z = dz / adjacentVertices.Count; } }
return wv; }
/* HC (Humphrey’s Classes) Smooth Algorithm - Reduces Shrinkage of Laplacian Smoother
Where sv - original points pv - previous points, alpha [0..1] influences previous points pv, e.g. 0 beta [0..1] e.g. > 0.5 */ public static Vector3[] hcFilter(Vector3[] sv, Vector3[] pv, int[] t, float alpha, float beta) { Vector3[] wv = new Vector3[sv.Length]; Vector3[] bv = new Vector3[sv.Length];
// Perform Laplacian Smooth wv = laplacianFilter(sv, t);
// Compute Differences for(int i=0; i<wv.Length; i++) { bv[i].x = wv[i].x - (alpha * sv[i].x + ( 1 - alpha ) * sv[i].x ); bv[i].y = wv[i].y - (alpha * sv[i].y + ( 1 - alpha ) * sv[i].y ); bv[i].z = wv[i].z - (alpha * sv[i].z + ( 1 - alpha ) * sv[i].z ); }
List<int> adjacentIndexes = new List<int>();
float dx = 0.0f; float dy = 0.0f; float dz = 0.0f;
for(int j=0; j<bv.Length; j++) { adjacentIndexes.Clear();
// Find the bv neighboring vertices adjacentIndexes = MeshUtils.findAdjacentNeighborIndexes (sv, t, sv[j]);
dx = 0.0f; dy = 0.0f; dz = 0.0f;
for (int k=0; k<adjacentIndexes.Count; k++) { dx += bv[adjacentIndexes[k]].x; dy += bv[adjacentIndexes[k]].y; dz += bv[adjacentIndexes[k]].z;
}
wv[j].x -= beta * bv[j].x + ((1 - beta) / adjacentIndexes.Count) * dx; wv[j].y -= beta * bv[j].y + ((1 - beta) / adjacentIndexes.Count) * dy; wv[j].z -= beta * bv[j].z + ((1 - beta) / adjacentIndexes.Count) * dz; }
return wv; } }</javascript>
MeshUtils.cs
<javascript> using UnityEngine; using System.Collections; using System.Collections.Generic;
/* Useful mesh functions
- /
public class MeshUtils : MonoBehaviour { // Finds a set of adjacent vertices for a given vertex // Note the success of this routine expects only the set of neighboring faces to eacn contain one vertex corresponding // to the vertex in question public static List<Vector3> findAdjacentNeighbors ( Vector3[] v, int[] t, Vector3 vertex ) { List<Vector3>adjacentV = new List<Vector3>(); List<int>facemarker = new List<int>(); int facecount = 0;
// Find matching vertices for (int i=0; i<v.Length; i++) if (Mathf.Approximately (vertex.x, v[i].x) && Mathf.Approximately (vertex.y, v[i].y) && Mathf.Approximately (vertex.z, v[i].z)) { int v1 = 0; int v2 = 0; bool marker = false;
// Find vertex indices from the triangle array for(int k=0; k<t.Length; k=k+3) if(facemarker.Contains(k) == false) { v1 = 0; v2 = 0; marker = false;
if(i == t[k]) { v1 = t[k+1]; v2 = t[k+2]; marker = true; }
if(i == t[k+1]) { v1 = t[k]; v2 = t[k+2]; marker = true; }
if(i == t[k+2]) { v1 = t[k]; v2 = t[k+1]; marker = true; }
facecount++; if(marker) { // Once face has been used mark it so it does not get used again facemarker.Add(k);
// Add non duplicate vertices to the list if ( isVertexExist(adjacentV, v[v1]) == false ) { adjacentV.Add(v[v1]); //Debug.Log("Adjacent vertex index = " + v1); }
if ( isVertexExist(adjacentV, v[v2]) == false ) { adjacentV.Add(v[v2]); //Debug.Log("Adjacent vertex index = " + v2); } marker = false; } } }
//Debug.Log("Faces Found = " + facecount);
return adjacentV;
}
// Finds a set of adjacent vertices for a given vertex
// Note the success of this routine expects only the set of neighboring faces to eacn contain one vertex corresponding
// to the vertex in question
public static List<int> findAdjacentNeighborIndexes ( Vector3[] v, int[] t, Vector3 vertex )
{
List<int>adjacentIndexes = new List<int>();
List<Vector3>adjacentV = new List<Vector3>();
List<int>facemarker = new List<int>();
int facecount = 0;
// Find matching vertices for (int i=0; i<v.Length; i++) if (Mathf.Approximately (vertex.x, v[i].x) && Mathf.Approximately (vertex.y, v[i].y) && Mathf.Approximately (vertex.z, v[i].z)) { int v1 = 0; int v2 = 0; bool marker = false;
// Find vertex indices from the triangle array for(int k=0; k<t.Length; k=k+3) if(facemarker.Contains(k) == false) { v1 = 0; v2 = 0; marker = false;
if(i == t[k]) { v1 = t[k+1]; v2 = t[k+2]; marker = true; }
if(i == t[k+1]) { v1 = t[k]; v2 = t[k+2]; marker = true; }
if(i == t[k+2]) { v1 = t[k]; v2 = t[k+1]; marker = true; }
facecount++; if(marker) { // Once face has been used mark it so it does not get used again facemarker.Add(k);
// Add non duplicate vertices to the list if ( isVertexExist(adjacentV, v[v1]) == false ) { adjacentV.Add(v[v1]); adjacentIndexes.Add(v1); //Debug.Log("Adjacent vertex index = " + v1); }
if ( isVertexExist(adjacentV, v[v2]) == false ) { adjacentV.Add(v[v2]); adjacentIndexes.Add(v2); //Debug.Log("Adjacent vertex index = " + v2); } marker = false; } } }
//Debug.Log("Faces Found = " + facecount);
return adjacentIndexes;
}
// Does the vertex v exist in the list of vertices static bool isVertexExist(List<Vector3>adjacentV, Vector3 v) { bool marker = false; foreach (Vector3 vec in adjacentV) if (Mathf.Approximately(vec.x,v.x) && Mathf.Approximately(vec.y,v.y) && Mathf.Approximately(vec.z,v.z)) { marker = true; break; }
return marker; } } </javascript>