I have Meshes in Unity that have multiple submeshes, and I would like to extract the submeshes and create new meshes from the submeshes via scripting, but I can’t figure out how to do it.
I’ve looked through the forums, and the most relevant seems to be this question. However, I don’t understand how to use the Mesh Maker and Saver script provided by Kurt Dekker to do what I want. His script creates a cube. I want to extract the existing submeshes, not create a new shape.
I also have no idea where to look in his MakeGeo project for what I need.
From that same thread, I have taken the solution from ChaseRLewis and put it in my project as an Editor script, but I get error messages for the ArrayUtilities.RangeSubset parts of the coding.
I also found this thread, but I’m not sure if it is relevant, and I don’t understand how to implement.
Any help would be greatly appreciated, either a way that you know, or explanation of the threads I’ve linked above.
Fastest way is to copy the entire vertex list, then ONLY copy the triangles from one submesh to your target new mesh containing only the submesh’s geometry.
A better way is a ton of bookkeeping, and it is a little bit hairy:
traverse the indices in the desired submesh (they are given as triplets of integers in a triangular mesh)
from those indices, make note of each indexed vertex and add it to your own collection that tracks “I added original vertex A with this X,Y,Z position” so that no matter how many triangles use it, you only add it once
when you process each triangle (triplet of integers), renumber its integer indices from the original index to whatever this newly-written down vertex’s index will be once it is put back into a collection and shoved into the new mesh.
copy the entire .vertices array over from your source mesh, unmodified
obtain whatever submesh you want from the source mesh, with .GetSubmesh()
directly copy ALL of that integer triplet data back to your new Mesh: you could use .SetSubmesh() or just assign it to the .triangles property
write your new Mesh to disk
If you’re not up to this fairly simple task, I’d suggest that perhaps you would be better off just using something like Blender3D to chop up your meshes into their constituent submeshes by hand.
Everyone, I figured it out by modifying the Script in this post. I copy my entire Script, which includes instructions, below if anyone would like to use it. As I say in the comments in the Script, my Script doesn’t save the meshes as Assets, but I describe my workaround, which requires you use Probuilder.
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
// This runtime Script extracts (copies) submeshes from a Gameobject's mesh, creates new GameObjects to receive them, and assigns the submeshes to the new GameObjects.
// This Script is only a slight modification of this Script: https://discussions.unity.com/t/create-a-mesh-from-a-sub-mesh/136161/3 (My modifications allow you to iterate through submeshes.)
// Put this Script on a GameObject in your Hierarchy whose Submeshes you wish to extract, and then press Play.
// IMPORTANT NOTE: This Script does not save the meshes as Assets.
// To save the Meshes: While in Play mode, I Probuilderize each of the new GameObjects, and then drag-and-drop them into an Asset folder to save them as Prefabs.
// This is probably bad practice, but I'm mostly self-taught. (You have to Probuilderize before dragging and dropping, or your meshes won't be saved when you exit Play mode.)
// I also tried to have the original materials assigned to the new GameObjects, but that didn't work. The pertinent code is noted near the end of this Script.
public class ExtractSubmeshes : MonoBehaviour
{
private Mesh originalMesh;
private Material[] originalMats;
void Awake()
{
originalMesh = GetComponent<MeshFilter>().mesh;
originalMats = GetComponent<MeshRenderer>().materials; // I was going to use this to assign materials, but right now I just assign the material names to the submesh objects
}
private void Start()
{
StartCoroutine(CreateMeshCoroutine());
}
public IEnumerator CreateMeshCoroutine()
{
for (int i = 0; i < originalMesh.subMeshCount; i++) // This is the iteration part I added
{
Mesh newMesh = new Mesh();
int[] oldTrianges = originalMesh.GetTriangles(i);
int count = 0;
Dictionary<int, int> dictionary = new Dictionary<int, int>();
for (int x = 0; x < oldTrianges.Length; x++)
{
int current = oldTrianges[x];
if (!dictionary.ContainsKey(current))
{
dictionary.Add(current, count);
count = count + 1;
}
}
int[] newTriangles = new int[oldTrianges.Length];
for (int x = 0; x < oldTrianges.Length; x++)
{
newTriangles[x] = dictionary[oldTrianges[x]];
}
Vector3[] oldVerts = originalMesh.vertices;
Vector3[] newVerts = new Vector3[count];
foreach (KeyValuePair<int, int> pair in dictionary)
{
int oldVertIndex = pair.Key;
int newVertIndex = pair.Value;
newVerts[newVertIndex] = oldVerts[oldVertIndex];
}
newMesh.vertices = newVerts;
newMesh.triangles = newTriangles;
newMesh.uv = new Vector2[newVerts.Length];
newMesh.RecalculateNormals();
newMesh.Optimize();
// My additions
GameObject newObj = new(originalMats[i].name);
MeshFilter meshFilter = newObj.AddComponent<MeshFilter>();
meshFilter.mesh = newMesh;
/// If you add Scripting to save the new Meshes as Assets, this should assign the Materials associated with the Submeshes in the original GameObject.
MeshRenderer meshRenderer = newObj.AddComponent<MeshRenderer>();
meshRenderer.material = originalMats[i];
yield return null;
}
}
}
// Create Mesh from Submeshes: https://discussions.unity.com/t/create-a-mesh-from-a-sub-mesh/136161/3
// Related post: https://discussions.unity.com/t/submesh-creation-from-script/50960/4