{"id":13663415,"url":"https://github.com/adcimon/mesh-deformer","last_synced_at":"2025-04-25T17:30:42.203Z","repository":{"id":133923024,"uuid":"143543155","full_name":"adcimon/mesh-deformer","owner":"adcimon","description":"Mesh deformation using the Unity Job System.","archived":false,"fork":false,"pushed_at":"2024-06-02T14:09:01.000Z","size":719,"stargazers_count":70,"open_issues_count":0,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-10T20:37:23.360Z","etag":null,"topics":["unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adcimon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-04T16:03:09.000Z","updated_at":"2024-07-12T09:02:54.000Z","dependencies_parsed_at":"2024-02-01T15:19:09.686Z","dependency_job_id":"5ebd9873-7afa-4734-a947-8a95e06cee38","html_url":"https://github.com/adcimon/mesh-deformer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcimon%2Fmesh-deformer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcimon%2Fmesh-deformer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcimon%2Fmesh-deformer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcimon%2Fmesh-deformer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adcimon","download_url":"https://codeload.github.com/adcimon/mesh-deformer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250861924,"owners_count":21499185,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["unity"],"created_at":"2024-08-02T05:02:27.025Z","updated_at":"2025-04-25T17:30:41.737Z","avatar_url":"https://github.com/adcimon.png","language":"C#","funding_links":[],"categories":["C\\#"],"sub_categories":[],"readme":"# Mesh Deformer\n\nMesh deformation using the Unity Job System.\n\n\u003cp align=\"center\"\u003e\n\t\u003cimg align=\"center\" src=\"example.gif\" title=\"Beware the mutant bunnies.\"\u003e\u003cbr\u003e\n\u003c/p\u003e\n\nThis project is a proof of concept application that deforms a mesh using the new Unity Job System. The \u003ca href=\"https://docs.unity3d.com/Manual/JobSystem.html\"\u003eUnity Job System\u003c/a\u003e is a way to write \u003ca href=\"https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)\"\u003emultithreaded\u003c/a\u003e code in the CPU providing high performance boost to the games using it. It is integrated with the Unity’s native job system which creates a thread per CPU core and manages small units of work named jobs. This design avoids the thread context switches that cause a waste of CPU resources.\u003cbr\u003e\n\nTo create a new job you need to implement one interface corresponding to the type of job you want to execute. There are several types of jobs, `IJob`, `IJobParallelFor` and `IJobParallelForTransform` are the most common. The basic one, `IJob`, allows you to execute the code in the secondary threads. It is also very common to want to execute the same operations on large collections of data, for this task you have the job `IJobParallelFor` (which is the one used in this example). The last one, `IJobParallelForTransform`, is another parallel job that is designed for operations using `Transform` components.\u003cbr\u003e\n\nAnother important thing to consider when writing high performance code is the memory layout of your data. Memory allocations are slow and to gain meaningful speed ups you have to control the lifecycle of your data, avoiding the garbage collector. A new set of native collections of \u003ca href=\"https://en.wikipedia.org/wiki/Blittable_types\"\u003eblittable types\u003c/a\u003e are exposed to the managed side of Unity to achieve this.\u003cbr\u003e\n\nThe namespaces that are necessary to use the Job System and the native collections are the following ones:\n```\nusing UnityEngine.Jobs;\nusing Unity.Collections;\nusing Unity.Jobs;\n```\n\nThe job that performs the vertex displacement is an `IJobParallelFor` job and receives the following inputs:\n\u003cul\u003e\n\t\u003cli\u003e\u003cstrong\u003edeltaTime\u003c/strong\u003e. Time in seconds it took to complete the last frame.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003ecenter\u003c/strong\u003e. Center of the sphere.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eradius\u003c/strong\u003e. Radius of the sphere.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eforce\u003c/strong\u003e. Force that is going to be applied to offset the vertices.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003enormals\u003c/strong\u003e. The normal for each vertex to obtain the displacement direction (read only).\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003evertices\u003c/strong\u003e. The vertex positions that are going to be updated.\u003c/li\u003e\n\u003c/ul\u003e\n\nIt is also important to highlight that the delta time must be copied because the jobs are \u003ca href=\"https://en.wikipedia.org/wiki/Asynchrony_(computer_programming)\"\u003easynchronous\u003c/a\u003e and don't have the concept of frame.\nThe operation that is executed is a vertex inside sphere check and a displacement across the normal with the given force.\n\n```\npublic struct MeshDeformerJob : IJobParallelFor\n{\n\t[ReadOnly] public float deltaTime;\n\t[ReadOnly] public Vector3 center;\n\t[ReadOnly] public float radius;\n\t[ReadOnly] public float force;\n\t[ReadOnly] public NativeArray\u003cVector3\u003e normals;\n\n\tpublic NativeArray\u003cVector3\u003e vertices;\n\n\tpublic void Execute(int index)\n\t{\n\t\tVector3 vertex = vertices[index];\n\n\t\tfloat a = Mathf.Pow(vertex.x - center.x, 2);\n\t\tfloat b = Mathf.Pow(vertex.y - center.y, 2);\n\t\tfloat c = Mathf.Pow(vertex.z - center.z, 2);\n\t\tif (a + b + c \u003c Mathf.Pow(radius, 2))\n\t\t{\n\t\t\tvertex += normals[index] * force * deltaTime;\n\t\t\tvertices[index] = vertex;\n\t\t}\n\t}\n}\n```\n\nThe execution of this job is performed in the `MeshDeformer.cs` script after the helper class `Pointer.cs` calls it when the mouse button is pressed. The class declares 2 native arrays for the normals and vertices and a `Mesh` that will be shared by the `MeshFilter` and the `MeshCollider`.\u003cbr\u003e\n\n```\npublic class MeshDeformer : MonoBehaviour\n{\n\tprivate Mesh mesh;\n\tprivate MeshCollider meshCollider;\n\n\tprivate NativeArray\u003cVector3\u003e vertices;\n\tprivate NativeArray\u003cVector3\u003e normals;\n\n\tprivate bool scheduled = false;\n\tprivate MeshDeformerJob job;\n\tprivate JobHandle handle;\n\n\tprivate void Start()\n\t{\n\t\tmesh = gameObject.GetComponent\u003cMeshFilter\u003e().mesh;\n\t\tmesh.MarkDynamic();\n\n\t\tmeshCollider = gameObject.GetComponent\u003cMeshCollider\u003e();\n\t\tmeshCollider.sharedMesh = null;\n\t\tmeshCollider.sharedMesh = mesh;\n\n\t\tvertices = new NativeArray\u003cVector3\u003e(mesh.vertices, Allocator.Persistent);\n\t\tnormals = new NativeArray\u003cVector3\u003e(mesh.normals, Allocator.Persistent);\n\t}\n\n\t...\n}\n```\n\nEach time the method `public void Deform(Vector3 point, float radius, float force)` is called, the job is scheduled for execution.\u003cbr\u003e\n\n```\npublic void Deform(Vector3 point, float radius, float force)\n{\n\tjob = new MeshDeformerJob();\n\tjob.deltaTime = Time.deltaTime;\n\tjob.center = transform.InverseTransformPoint(point);\n\tjob.radius = radius;\n\tjob.force = force;\n\tjob.vertices = vertices;\n\tjob.normals = normals;\n\n\thandle = job.Schedule(vertices.Length, 64);\n}\n```\n\nThe job is completed in the `LateUpdate`, the vertices are copied from the job's native array to the mesh and the bounds are recalculated.\u003cbr\u003e\n```\nprivate void LateUpdate()\n{\n\thandle.Complete();\n\tjob.vertices.CopyTo(vertices);\n\tmesh.vertices = vertices.ToArray();\n\tmesh.RecalculateBounds();\n}\n```\n\nLastly, don't forget to free resources when the process is done, remember that the native collections are not managed.\n```\nprivate void OnDestroy()\n{\n\tvertices.Dispose();\n\tnormals.Dispose();\n}\n```\n\nReferences.\n\u003e \u003ca href=\"https://docs.unity3d.com/Manual/JobSystem.html\"\u003eUnity Manual: C# Job System\u003c/a\u003e\u003cbr\u003e\n\u003e \u003ca href=\"https://www.youtube.com/watch?v=AXUvnk7Jws4\"\u003eUnite Europe 2017 - C# job system \u0026 compiler\u003c/a\u003e\u003cbr\u003e\n\u003e \u003ca href=\"https://www.youtube.com/watch?v=tGmnZdY5Y-E\"\u003eUnite Austin 2017 - Writing High Performance C# Scripts\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadcimon%2Fmesh-deformer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadcimon%2Fmesh-deformer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadcimon%2Fmesh-deformer/lists"}