Strip PB scripts in Editor code? Or extract mesh from ProBuilderMesh?

TLDR: Can’t I can StripProBuilderObjects from within some other Editor script?

I’m using a now-deprecated Asset Store offering to fracture objects into pieces. I’ve found that in many cases, the resulting object is kind of a mess, with way too many verts, and a lot of overlapping verts. This results into terrible UV maps. I’ve found that if I ProBuilderize it, and Weld Vertices, the result is very good. However, doing that manually is a pain, as sometimes there are hundreds of small pieces.

In the Editor code that generates these pieces, I can add a ProBuilderMesh, and I can call WeldVertices on it, and that works great. But I haven’t found a way to strip off the PB objects after doing that, so that the object just has its plain mesh. It seems that all of the code to Strip PB Objects is in private/protected classes, not accessible from other Editor code.

Another approach would be if I could somehow get the underlying mesh from a ProBuilderMesh, and assign that to the objects’s Mesh Filter. But I don’t see a way to do that, as the mesh of a ProBuilderMesh is internal.

Note that I’m only doing this stuff in an Editor script, not a build/runtime script, so I’m hoping that makes it more likely that this can be done.

In short, I’m starting with a plain old mesh + Mesh Renderer, and I want to run this through PB’s Weld Vertices, but end up with a plain old mesh + Mesh Renderer when I’m done. Is it possible to script that?

You should be able to replicate the behaviour of the script stripping action by setting the preserveMeshAssetOnDestroy flag to true on the ProBuilderMesh before destroying the component.

https://docs.unity3d.com/Packages/com.unity.probuilder@4.2/api/UnityEngine.ProBuilder.ProBuilderMesh.html?q=probuildermesh#UnityEngine_ProBuilder_ProBuilderMesh_preserveMeshAssetOnDestroy

1 Like

That worked great. Thanks.

My use-case is probably kind of weird, but the following code uses PB to basically clean up a messy, generated mesh (generated via some 3rd party code I’m using that, unfortunately, isn’t great…). This looks a little silly, and I’m probably calling some things redundantly here, but the end result is that it cleans up mesh nicely, even for prefabs whose mesh data is stored as an asset.

private static void Probuilderize2(GameObject objectToProbuilderize, bool destroyAfter)
{
    var filter = objectToProbuilderize.GetComponent<MeshFilter>();
    var renderer = objectToProbuilderize.GetComponent<Renderer>();
    Mesh originalMesh = filter.sharedMesh;

    var settings = new MeshImportSettings()
    {
        quads = true,
        smoothing = true,
        smoothingAngle = 1
    };

    ProBuilderMesh pb = Undo.AddComponent<ProBuilderMesh>(objectToProbuilderize);

    MeshImporter meshImporter = new MeshImporter(pb);
    meshImporter.Import(filter.sharedMesh, materials: renderer.sharedMaterials, importSettings: settings);

    // if this was previously a pb_Object, or similarly any other instance asset, destroy it.
    // if it is backed by saved asset, leave the mesh asset alone but assign a new mesh to the
    // renderer so that we don't modify the asset.
    if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(originalMesh)))
    {
        GameObject.DestroyImmediate(originalMesh);
    }
    else
    {
        objectToProbuilderize.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    }

    pb.ToMesh();
    pb.Refresh();
    pb.Optimize();

    // Clean up the objects by welding. This is probably overkill, but we seem to need to call this a
    // couple of times depending on how messy the original object was.
    pb.WeldVertices(Enumerable.Range(0, pb.vertexCount - 1).ToArray(), 0.01f);
    pb.WeldVertices(Enumerable.Range(0, pb.vertexCount - 1).ToArray(), 0.01f);
    pb.WeldVertices(Enumerable.Range(0, pb.vertexCount - 1).ToArray(), 0.01f);
    pb.WeldVertices(Enumerable.Range(0, pb.vertexCount - 1).ToArray(), 0.01f);
    pb.WeldVertices(Enumerable.Range(0, pb.vertexCount - 1).ToArray(), 0.01f);

    pb.Refresh();
    pb.Optimize();



    if (destroyAfter)
    {
        // preserveMeshAssetOnDestroy must be true in order to keep the PB mesh on the object
        // and assign it to the prefab.
        pb.preserveMeshAssetOnDestroy = true;
        GameObject.DestroyImmediate(pb);
    }
}