First off, I apologize if I use incorrect terminology…this is my first time tackling any kind of procedural mesh generation and delving into how mesh/sprites work in general. This was a fun problem to work on, would love it if I could finish it up with the communities help!
My goal is to create a ‘thick sprite’. Essentially I wanted to convert 2d sprites into a 3d mesh version with depth. Imagine taking a sprite in the xy plane and extruding it out along the z plane. The front and back face (I’ll call these sprite faces) of this mesh would be 1:1 mapped to the sprite and the edge meshes would just be whatever color the outer vertices are.
I got this working, but with a very naïve algorithm that fully connects all edges of the sprite face’s triangles instead of just creating triangles to bridge the outer edges of the sprite faces. The end result does properly create triangles around the outer edges that connect the front and back sprite faces, but also creates triangles running through the inside of the mesh as well. This creates lighting issues if I recalculate normals of the mesh, as well as subtle lighting issue even without recalculating normals. I assume this will create perf issues eventually too. I’ll walk through my current approach below, but my question is: Is there a good way to draw only the required triangles connecting the two sprite faces?
[187691-unity-stickman.png*|187691]
[187692-unity-stickman-wire.png*|187692]
Pseudocode
I’ll post the actual code below, but here’s the pseudocode to convert the sprite to a mesh:
Create a new mesh using vertices, triangles, and uvs derived from the Sprite object
** -Vertices**
- create a list of vertices from Sprite.vertices
- duplicate this list to create the ‘back vertices’
- shift the back vertices in the z
- combine the two lists and assign to a temp mesh object
-Triangles
- create a list of triangles from Sprite.triangles
- duplicate that list to create the back triangles
- Process the back triangles to make sure the ints representing index in the vert array point towards the backface vertices
- Flip the normals on the back triangles
- Create a new list of triangles to connect the two planar meshes. As discussed, this naïve approach will connect all front edges to back edges rather than just the outer edges.
- combine triangle lists into array and assign to temp mesh
- Notice that I run mesh.recalculateNormals before adding the middle triangles, otherwise lighting gets messed up
-UVs
- grab uvs from Sprite.uvs, duplicate them to account for the back face, and assign that back to the temp mesh
What I would love to do is figure out how to only create the triangles on the outer edges instead of having to connect all edges. The problem I face is that the Sprite.vertices seems to not be ordered in any patter that I can interpret. In order for me to only create outer triangles, I would need to need to know which vertices neighbor eachother on the front and back face. If I could somehow reorder the vertices array (and then in turn the indices in the triangles) then I could potentially iterate through the vertices and create triangles connecting to the neighbors.
I cannot think of a clever way to do this, so I’m asking the community! This was a rather interesting problem and I’d love to hear from someone with more knowledge in the field than me.
For reference, the below code is put on a gameobject in scene as a component. I then have an editor script that creates a button that calls the ‘MakeMesh’ function. m_pokeSprite is populated at editor time by draggin the desired sprite into the field. m_tempMesh is an object in scene with a MeshFilter and MeshRenderer component. The material on the renderer is an opaque standard shader material with the desired sprite in the ‘albedo’ field.
public class MeshMaker : MonoBehaviour
{
public Sprite m_pokeSprite;
public MeshFilter m_tempMesh;
public void MakeMesh()
{
Mesh mesh = new Mesh();
//create vertices
List<Vector3> vertices = new List<Vector3>();
vertices.AddRange(Array.ConvertAll(m_pokeSprite.vertices, i => (Vector3)i));
List<Vector3> backVertices = new List<Vector3>(vertices);
backVertices = ShiftVertices(backVertices);
vertices.AddRange(backVertices);
mesh.vertices = vertices.ToArray();
//create triangles
List<int> triangles = new List<int>();
triangles.AddRange(Array.ConvertAll(m_pokeSprite.triangles, i => (int)i));
List<int> backTriangles = new List<int>(triangles);
backTriangles = ShiftAndFlipTriangleIndexes(backTriangles, backVertices.Count);
List<int> middleTriangels = CreateMiddleTriangles(triangles, backTriangles);
triangles.AddRange(backTriangles);
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
triangles.AddRange(middleTriangels);
mesh.triangles = triangles.ToArray();
//create uvs
List<Vector2> uvs = new List<Vector2>(m_pokeSprite.uv);
List<Vector2> newUvs = new List<Vector2>(uvs);
uvs.AddRange(newUvs);
mesh.uv = uvs.ToArray();
//assign to mesh in scene and save mesh as asset
m_tempMesh.sharedMesh = mesh;
AssetDatabase.CreateAsset(mesh, "Assets/meshSaved.asset");
}
private List<Vector3> ShiftVertices(List<Vector3> verticies)
{
float dist = 1f;
for (int i = 0; i < verticies.Count; i++)
{
verticies <em>= new Vector3(verticies<em>.x, verticies_.y, verticies*.z + dist);*_</em></em>
}
return verticies;
}
private List ShiftAndFlipTriangleIndexes(List indecies, int shiftAmount)
{
List toReturn = new List(indecies);
for (int i = 0; i < toReturn.Count; i++)
{
toReturn += shiftAmount;
}
for (int i = 0; i < toReturn.Count; i+=3)
{
int i0 = toReturn*;*
int i2 = toReturn[i+2];
toReturn = i2;
toReturn[i + 2] = i0;
}
return toReturn;
}
private List CreateMiddleTriangles(List frontTriangles, List backTriangles)
{
List toReturn = new List();
for (int i = 0; i < frontTriangles.Count; i += 3)
{
toReturn.AddRange(MakeQuad(frontTriangles*, frontTriangles[i + 1], backTriangles[i + 2], backTriangles[i + 1]));*
toReturn.AddRange(MakeQuad(frontTriangles[i + 1], frontTriangles[i + 2], backTriangles[i + 1], backTriangles*));*
toReturn.AddRange(MakeQuad(frontTriangles[i + 2], frontTriangles_, backTriangles*, backTriangles[i + 2]));
}*_
return toReturn;
}
private List MakeQuad(int x0, int x1, int y0, int y1)
{
List toReturn = new List();
toReturn.Add(x0);
toReturn.Add(y0);
toReturn.Add(y1);
toReturn.Add(x0);
toReturn.Add(y1);
toReturn.Add(x1);
return toReturn;
}
}
*
*