Main Page

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(Deformation Smooth Filters for Meshes)
Line 25: Line 25:
  
  
== TestSmoothFilter ==
+
== TestSmoothFilter.cs ==
 
<javascript>
 
<javascript>
 
using UnityEngine;
 
using UnityEngine;

Revision as of 12: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>

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox