# Generating Custom Geometry ![](images/2D_SpriteShape_CustomGeometry.png)
The **Custom Geometry** feature is found in the [Sprite Shape Controller](SSController.md). It allows you to use a custom script to generate or modify Sprite Shape geometry. The custom script is written as a [ScriptableObject](https://docs.unity3d.com/Manual/class-ScriptableObject.html) and is reusable. ## API Examples ###Generating new geometry To generate new geometry, refer to the following example code. ```c# public abstract class SpriteShapeGeometryCreator : ScriptableObject { public abstract int GetVertexArrayCount(SpriteShapeController spriteShapeController); public abstract JobHandle MakeCreatorJob(SpriteShapeController spriteShapeController, NativeArray indices, NativeSlice positions, NativeSlice texCoords, NativeSlice tangents, NativeArray segments, NativeArray colliderData); } ``` **Note:** The default generator script that ships with the Sprite Shape package is itself written as a `SpriteShapeGeometryCreator` (refer to `Runtime/SpriteShapeDefaultCreator.cs`). Any custom `SpriteShapeGeometryCreator` set through the script or Inspector will override this default Object. ###Modifying existing geometry To modify generated geometry, refer to the following example code. ```c# public abstract class SpriteShapeGeometryModifier : ScriptableObject { public abstract JobHandle MakeModifierJob(JobHandle generator, SpriteShapeController spriteShapeController, NativeArray indices, NativeSlice positions, NativeSlice texCoords, NativeSlice tangents, NativeArray segments, NativeArray colliderData); } ``` **Note:** `SpriteShapeGeometryModifier` is only applicable when either: 1. `SpriteShapeDefaultCreator` is used and the vertex data only needs modification. 2. Or a custom `SpriteShapeGeometryCreator` is used with default channels accepted in `MakeCreatorJob`. `SpriteShapeGeometryModifier` cannot be used when `MakeCreatorJob` creates a Job with custom Channel data. ## Examples by usage Creating a simple quad with the size of the Bounds with `SpriteShapeGeometryCreator`: ```c# // A simple C# job to generate a quad. public struct CreatorJob : IJob { // Indices of the generated triangles. public NativeArray indices; // Vertex positions. public NativeSlice positions; // Texture Coordinates. public NativeSlice texCoords; // Sub-meshes of generated geometry. public NativeArray segments; // Input Bounds. public Bounds bounds; public void Execute() { // Generate Positions/TexCoords/Indices for the Quad. positions[0] = bounds.min; texCoords[0] = Vector2.zero; positions[1] = bounds.max; texCoords[1] = Vector2.one; positions[2] = new Vector3(bounds.min.x, bounds.max.y, 0); texCoords[2] = new Vector2(0, 1); positions[3] = new Vector3(bounds.max.x, bounds.min.y, 0); texCoords[3] = new Vector2(1, 0); indices[0] = indices[3] = 0; indices[1] = indices[4] = 1; indices[2] = 2; indices[5] = 3; // Set the only sub-mesh (quad) var seg = segments[0]; seg.geomIndex = seg.spriteIndex = 0; seg.indexCount = 6; seg.vertexCount = 4; segments[0] = seg; // Reset other sub-meshes. seg.geomIndex = seg.indexCount = seg.spriteIndex = seg.vertexCount = 0; for (int i = 1; i < segments.Length; ++i) segments[i] = seg; } } [CreateAssetMenu(fileName = "SpriteShapeQuad", menuName = "ScriptableObjects/SpriteShapeQuad", order = 1)] public class SpriteShapeQuad : SpriteShapeGeometryCreator { public override int GetVertexArrayCount(SpriteShapeController sc) { // Set the maximum size required for the Vertices. return 64; } public override JobHandle MakeCreatorJob(SpriteShapeController sc, NativeArray indices, NativeSlice positions, NativeSlice texCoords, NativeSlice tangents, NativeArray segments, NativeArray colliderData) { NativeArray bounds = sc.spriteShapeRenderer.GetBounds(); var spline = sc.spline; int pointCount = spline.GetPointCount(); Bounds bds = new Bounds(spline.GetPosition(0), spline.GetPosition(0)); for (int i = 0; i < pointCount; ++i) bds.Encapsulate(spline.GetPosition(i)); bounds[0] = bds; var cj = new CreatorJob() {indices = indices, positions = positions, texCoords = texCoords, segments = segments, bounds = bds}; var jh = cj.Schedule(); return jh; } } ``` Changing UV data with `SpriteShapeGeometryModifier`: ```c# // A simple C# job to move the UV along the x-axis. If this is called repeatedly each frame it creates UV Animation effect. To get this called each frame, use RefreshSpriteShape API of SpriteShapeController. public struct UVAnimatorJob : IJob { // We are only modifying UV data. public NativeSlice uvs; // Offset to move x coordinates of UV. public float offset; public void Execute() { // Move x coordinates of UV data. for (int i = 0; i < uvs.Length; ++i) { var uv = uvs[i]; uv.x = (uv.x + offset) % 1.0f; uvs[i] = uv; } } } [CreateAssetMenu(fileName = "SpriteShapeUVAnimator", menuName = "ScriptableObjects/SpriteShapeUVAnimator", order = 1)] public class SpriteShapeUVAnimator : SpriteShapeGeometryModifier { public override JobHandle MakeModifierJob(JobHandle generator, SpriteShapeController spriteShapeController, NativeArray indices, NativeSlice positions, NativeSlice texCoords, NativeSlice tangents, NativeArray segments, NativeArray colliderData) { var mj = new UVAnimatorJob(){ uvs = texCoords, offset = UnityEngine.Time.time}; var jh = mj.Schedule(generator); return jh; } } ``` ## Advanced usage The following is an example of advanced usage of the API by creating geometry with vertex colors. In the function `MakeCreatorJob` below, `GetChannels` is invoked to get additional colors other than the default. The input parameters for `MakeCreatorJob` are overwritten by the `GetChannels` function. **Note:** Only `SpriteShapeGeometryCreator` can be used when updating any other channels that are not part of the `MakeCreatorJob` parameters. ```c# public struct ColorCreatorJob : IJob { // Indices of the generated triangles. public NativeArray indices; // Vertex positions. public NativeSlice positions; // Texture Coordinates. public NativeSlice texCoords; // Color of Vertces. public NativeSlice colors; // Sub-meshes of generated geometry. public NativeArray segments; // Input Bounds. public Bounds bounds; public void Execute() { // Generate Positions/TexCoords/Indices for the Quad. positions[0] = bounds.min; texCoords[0] = Vector2.zero; colors[0] = Color.red; positions[1] = bounds.max; texCoords[1] = Vector2.one; colors[1] = Color.blue; positions[2] = new Vector3(bounds.min.x, bounds.max.y, 0); texCoords[2] = new Vector2(0, 1); colors[2] = Color.green; positions[3] = new Vector3(bounds.max.x, bounds.min.y, 0); texCoords[3] = new Vector2(1, 0); colors[3] = Color.yellow; indices[0] = indices[3] = 0; indices[1] = indices[4] = 1; indices[2] = 2; indices[5] = 3; // Set the only sub-mesh (quad) var seg = segments[0]; seg.geomIndex = seg.spriteIndex = 0; seg.indexCount = 6; seg.vertexCount = 4; segments[0] = seg; // Reset other sub-meshes. seg.geomIndex = seg.indexCount = seg.spriteIndex = seg.vertexCount = 0; for (int i = 1; i < segments.Length; ++i) segments[i] = seg; } } [CreateAssetMenu(fileName = "SpriteShapeColorQuad", menuName = "ScriptableObjects/SpriteShapeColorQuad", order = 1)] public class SpriteShapeColorQuad : SpriteShapeGeometryCreator { public override int GetVertexArrayCount(SpriteShapeController sc) { return 64; } public override JobHandle MakeCreatorJob(SpriteShapeController sc, NativeArray indices, NativeSlice positions, NativeSlice texCoords, NativeSlice tangents, NativeArray segments, NativeArray colliderData) { NativeArray bounds = sc.spriteShapeRenderer.GetBounds(); var spline = sc.spline; int pointCount = spline.GetPointCount(); Bounds bds = new Bounds(spline.GetPosition(0), spline.GetPosition(0)); for (int i = 0; i < pointCount; ++i) bds.Encapsulate(spline.GetPosition(i)); NativeSlice colors = default(NativeSlice); sc.spriteShapeRenderer.GetChannels(32000, out indices, out positions, out texCoords, out colors); var cj = new ColorCreatorJob() {indices = indices, positions = positions, texCoords = texCoords, colors = colors, segments = segments, bounds = bds}; var jh = cj.Schedule(); return jh; } } ```