Deleting faces or triangles from an object by material on load scene

I have objects that have multiple materials imported from blender via FBX.

I’d like to find for some objects what materials are used and destroy faces when a certain material name is used (“!null” in my case)

I know this is not they correct way of working as I should delete the faces in blender but as I am building quite complex geometry and sometimes need to keep faces in blender, I don’t want the unity engine to render, it seems the only way to go. I tried “cutout” but that does not make any difference in rendering times although they are invisible.

Is this at all possible?

Sure, each material corresponds to a certain submesh. So what you have to do is:

  • identify the material(s) in the “sharedMaterials” array of the MeshRenderer. All you need to know is the index into the sharedMaterials array.

  • Use GetTriangles in a for loop and get the triangle arrays for each submesh you want to keep. So ignore the indices with the materials you want to remove.

  • Finally remove those materials from the sharedMaterials array.

  • Set the submeshCount to the new submesh count

  • use SetTriangles in a for loop to put the triangles back in.

So the easiest way is to use two Lists one for the materials and one for the triangles. Then just remove the unwanted materials and triangles in pairs (so remove the material and the corresponding triangle array).

void Start()
{
    MeshRenderer renderer = GetComponent<MeshRenderer>();
    Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
    Material[] materials = renderer.sharedMaterials;
    
    List<Material> mats = new List<Material>();
    List<int[]> tris = new List<int[]>();
    
    for(int i = 0; i < materials.Length; i++)
    {
        if (keepMaterial)
        {
            tris.Add(mesh.GetTriangles(i));
            mats.Add(materials*);*

}
}
mesh.subMeshCount = tris.Count;
for (int n = 0; n < tris.Count; n++)
{
mesh.SetTriangles(tris[n],n);
}
renderer.sharedMaterials = mats.ToArray();
}
The important thing you have to change is the if (keepMaterial) condition. If you want to check the material name you might want to do something like
if(mats*.name != “!null”)*
Though you should test the condition if it works properly. Keep in mind that string comparisons are case sensitive. “!null” is not the same as “!Null” or “!NULL”

This feels so wrong. You solved my problem for 99%.

I don’t want to take credit for your work I ran into a problem using your code.
The most important being Unity not restoring my objects to default.

I understand you wrote it without testing, and I find that awesome.

I will publish the code tried and tested into the public domain for all to use.

using UnityEngine;
//using System.Collections;
using System.Collections.Generic;
using System.Linq;
//using System.Text;

/// <summary>
/// Class: ClearNull Derived from MonoBehaviour
/// 
/// Purpose: Debuging of objects not yet completely finished in blender.
///          This class will destroy any face marked with !NULL to avoid them to render
///          New: Can be attached to game manager or any master object to destroy null ffaces in 
///               children or entire schene
///          
/// Parameters DestroyString by default "!NULL" *** Warning this is case insensitive***
/// 
/// Notes: THe code was inspired by Bunny83 in thread 
///        http://answers.unity3d.com/questions/1232964/deleting-faces-or-triangles-from-an-object-by-mate.html
///        
///        This code should only be use for debugging and not for final products
///        
///        Make scene wide check on load to destroy every !NULLL face in the scene.
///        Now supports hecking children
///         
/// Further develepment ideas: 
///         Make it so that it can be a tool to destroy !null textures fpr all prefabs permanently
/// 
/// Copyright: Code originaly by Bunny83 adaped by
///            Napivo on 2016/08/23 
///            Napivo on 2016/08/24
///            Napivo on 2016/08/25
///            free for anyone... Hope It help you as much as it does me
/// </summary>
public class ClearNull : MonoBehaviour
{
    // Use this for initialization
    public static void Clear(GameObject gObj, string materialName, bool caseSensative = false)
    {
        //get the renderer
        MeshRenderer renderer = gObj.GetComponent<MeshRenderer>();

        if (!renderer)
            return;

        //GetComponent the mesh
        Mesh mesh = gObj.GetComponent<MeshFilter>().mesh;
        List<Material> mats = new List<Material>();
        List<int[]> tris = new List<int[]>();


        for (int i = 0; i < renderer.materials.Length; i++)
        {
            if (((!renderer.materials*.name.ToUpper().StartsWith(materialName.ToUpper())) && !caseSensative) ||*

((!renderer.materials*.name.StartsWith(materialName)) && caseSensative))*
{
tris.Add(mesh.GetTriangles(i));
mats.Add(renderer.materials*);*
}
}

mesh.subMeshCount = tris.Count; //Set the new triangle count
for (int n = 0; n < tris.Count; n++) //Set the new triangles without those lost
{
mesh.SetTriangles(tris[n], n);
}

renderer.materials = mats.ToArray(); //Set the material without thoose lost
}

public static void ClearChildren(GameObject gObj, string materialName, bool caseSensative = false)
{
Clear(gObj, materialName, caseSensative);

foreach (Transform t in gObj.transform)
{
if (t.childCount > 0)
{
ClearChildren(t.gameObject, materialName, caseSensative);
}
else
{
Clear(t.gameObject, materialName, caseSensative);
}
}
}

// untested should only clear pbjects loaded from a certain scene (for multiload)
public static void ClearScene(string sceneName, string materialName = “!NULL”, bool caseSensative = false)
{
var objs = from GameObject go in GameObject.FindObjectsOfType() where go.scene.name == sceneName select go;

foreach (GameObject gObj in objs)
{
foreach (Transform t in gObj.transform)
{
Clear(t.gameObject, materialName, caseSensative);
}
}
}

public static void ClearScene(string materialName = “!NULL”, bool caseSensative = false)
{
var objs = GameObject.FindObjectsOfType();

foreach (GameObject gObj in objs)
{
Clear(gObj, materialName, caseSensative);

foreach (Transform t in gObj.transform)
{
Clear(t.gameObject, materialName, caseSensative);
}
}
}

public string DestroyString = “!NULL”;
public bool clearChildren = true; // if true child objects will be cleared too
public bool clearScene = false; // used for game managers will clear entire scene

// Use this for initialization
void Start()
{
if (clearScene)
{
ClearScene(DestroyString, false);
}
else
if (clearChildren)
{
ClearChildren(gameObject, DestroyString);
}
else
{
Clear(gameObject, DestroyString);
}
}
}