Skip to content

Instantly share code, notes, and snippets.

@keenanwoodall
Created August 1, 2019 16:48
Show Gist options
  • Save keenanwoodall/99470af37d9c926b271a24f42a3d65fb to your computer and use it in GitHub Desktop.
Save keenanwoodall/99470af37d9c926b271a24f42a3d65fb to your computer and use it in GitHub Desktop.
A test for deforming meshes without using a deformable component
using UnityEngine;
using UnityEditor;
using Unity.Jobs;
using Deform;
public class AutomatedDeformationTestWindow : EditorWindow
{
private Mesh mesh;
[MenuItem("Tools/Automated Deformation Window")]
public static void Open()
{
GetWindow<AutomatedDeformationTestWindow>("Deformableless Deformation");
}
private void OnGUI()
{
using (var check = new EditorGUI.ChangeCheckScope())
{
var newMesh = (Mesh)EditorGUILayout.ObjectField("Mesh", mesh, typeof(Mesh), false);
if (check.changed)
{
Undo.RecordObject(this, "Changed Mesh");
mesh = newMesh;
}
}
if (GUILayout.Button("Deform"))
SaveMesh(Deform(mesh));
}
private Mesh Deform(Mesh mesh)
{
// Make a copy of the mesh
var newMesh = Instantiate(mesh);
// Generate managed and native data from the mesh
var managedData = new ManagedMeshData(newMesh);
var nativeData = new NativeMeshData(managedData);
var vertexData = nativeData.VertexBuffer.Length;
// Schedule a job that will deform the native data's vertex buffer with 3D simplex noise
var handle = new SimplexNoiseDeformer._3DNoiseJob
{
frequency = 3f,
magnitude = 0.1f,
// meshToAxis is used to make the relative to another transform, but in this case it doesn't need to be relative to anything
// so I'm creating a matrix that won't affect the vertices at all
meshToAxis = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one),
offset = Vector4.zero,
vertices = nativeData.VertexBuffer
}.Schedule(vertexData, Deformer.DEFAULT_BATCH_COUNT);
/*
* To add another deformer, just create and schedule another job with 'handle' as the dependecy.
* It will look something like this:
*
* handle = new SomeDeformer.SomeDeformerJob
* {
* someFloatParameter = 1f,
* someVectorParameter = Vector3.one
* }.Schedule(vertexData, Deformer.DEFAULT_BATCH_COUNT, handle);
*/
// Schedule normal recalculation after the noise finishes
handle = MeshUtils.RecalculateNormals(nativeData, handle);
// Schedule bounds recalculation after the normal recalculation finishes
handle = MeshUtils.RecalculateBounds(nativeData, handle);
// Force the jobs to complete
handle.Complete();
// Copy the native data to the managed data
DataUtils.CopyNativeDataToManagedData(managedData, nativeData, DataFlags.All);
// Dispose of the native data so we don't leak memory
nativeData.Dispose();
// Assign all of the new managed data to the new mesh
newMesh.vertices = managedData.Vertices;
newMesh.normals = managedData.Normals;
newMesh.tangents = managedData.Tangents;
newMesh.uv = managedData.UVs;
newMesh.colors = managedData.Colors;
newMesh.triangles = managedData.Triangles;
newMesh.bounds = managedData.Bounds;
// Return the modified mesh
return newMesh;
}
// I'd recommend you use your own mesh serializer as what I'm doing gere is kinda gross.
// If that doesn't bother you, this is a good method for saving a mesh.
private void SaveMesh(Mesh mesh)
{
// C:/...<ProjectName>/Assets/
var projectPath = Application.dataPath + "/";
// We have to generate the full asset path starting from the Assets folder for GeneratorUniqueAssetPath to work,
var assetPath = EditorUtility.SaveFilePanelInProject("Save Obj", $"{mesh.name}.obj", "obj", "");
if (string.IsNullOrEmpty(assetPath))
return;
// Now that we have a unique asset path we can remove the "Assets/" and ".obj" to get the unique name.
var fileName = assetPath;
// It's pretty gross, but it works and this code doesn't need to be performant.
fileName = fileName.Remove(0, 7);
fileName = fileName.Remove(fileName.Length - 4, 4);
ObjExporter.SaveMesh(mesh, null, projectPath, fileName);
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment