Generating Custom Geometry
The Custom Geometry feature is found in the Sprite Shape Controller. It allows you to use a custom script to generate or modify Sprite Shape geometry. The custom script is written as a ScriptableObject and is reusable.
API Examples
###Generating new geometry To generate new geometry, refer to the following example code.
public abstract class SpriteShapeGeometryCreator : ScriptableObject
{
public abstract int GetVertexArrayCount(SpriteShapeController spriteShapeController);
public abstract JobHandle MakeCreatorJob(SpriteShapeController spriteShapeController, NativeArray<ushort> indices,
NativeSlice<Vector3> positions, NativeSlice<Vector2> texCoords, NativeSlice<Vector4> tangents,
NativeArray<SpriteShapeSegment> segments, NativeArray<float2> 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.
public abstract class SpriteShapeGeometryModifier : ScriptableObject
{
public abstract JobHandle MakeModifierJob(JobHandle generator, SpriteShapeController spriteShapeController, NativeArray<ushort> indices,
NativeSlice<Vector3> positions, NativeSlice<Vector2> texCoords, NativeSlice<Vector4> tangents,
NativeArray<SpriteShapeSegment> segments, NativeArray<float2> colliderData);
}
Note: SpriteShapeGeometryModifier
is only applicable when either:
SpriteShapeDefaultCreator
is used and the vertex data only needs modification.- Or a custom
SpriteShapeGeometryCreator
is used with default channels accepted inMakeCreatorJob
.SpriteShapeGeometryModifier
cannot be used whenMakeCreatorJob
creates a Job with custom Channel data.
Examples by usage
Creating a simple quad with the size of the Bounds with SpriteShapeGeometryCreator
:
// A simple C# job to generate a quad.
public struct CreatorJob : IJob
{
// Indices of the generated triangles.
public NativeArray<ushort> indices;
// Vertex positions.
public NativeSlice<Vector3> positions;
// Texture Coordinates.
public NativeSlice<Vector2> texCoords;
// Sub-meshes of generated geometry.
public NativeArray<UnityEngine.U2D.SpriteShapeSegment> 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<ushort> indices, NativeSlice<Vector3> positions, NativeSlice<Vector2> texCoords,
NativeSlice<Vector4> tangents, NativeArray<UnityEngine.U2D.SpriteShapeSegment> segments, NativeArray<float2> colliderData)
{
NativeArray<Bounds> 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
:
// 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<Vector2> 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<ushort> indices,
NativeSlice<Vector3> positions, NativeSlice<Vector2> texCoords, NativeSlice<Vector4> tangents, NativeArray<SpriteShapeSegment> segments, NativeArray<float2> 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.
public struct ColorCreatorJob : IJob
{
// Indices of the generated triangles.
public NativeArray<ushort> indices;
// Vertex positions.
public NativeSlice<Vector3> positions;
// Texture Coordinates.
public NativeSlice<Vector2> texCoords;
// Color of Vertces.
public NativeSlice<Color32> colors;
// Sub-meshes of generated geometry.
public NativeArray<UnityEngine.U2D.SpriteShapeSegment> 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<ushort> indices, NativeSlice<Vector3> positions, NativeSlice<Vector2> texCoords,
NativeSlice<Vector4> tangents, NativeArray<UnityEngine.U2D.SpriteShapeSegment> segments, NativeArray<float2> colliderData)
{
NativeArray<Bounds> 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<Color32> colors = default(NativeSlice<Color32>);
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;
}
}