Thank you for looking into this! I checked your script, it seems that Physics.Bake() will still only happen once for the lifetime of the mesh.
EDIT: Oops, I used the old Editor on my first tests, in 2020.1.0a24.3012 it works as expected! (in the current version 2019.3.03f it doesn’t)
Thank You! Can we expect this in the next stable release? I need to test this on an Android device 
Script description
I adapted your script to include Threading (see below). Just paste it, needed objects are created procedurally. I also used a larger mesh so that the baking should take some milliseconds. So you can see in the console-messages if assignments cause baking:
Controls:
Press 1: Update the mesh and Bake it in Update, the assignment in FixedUpdate should not take long
Press 2: Update the mesh without baking it, the assignment in FixedUpdate should take long
Press 3: Update the mesh and bake it in a second thread, the assignment in the callback should not take long
Space: Drop a test collider
Here is the script:
Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using BakeThreads;
public class CreateTriangle : MonoBehaviour
{
private Mesh mesh;
private MeshCollider collider;
private MeshFilter filter;
private MeshRenderer renderer;
private GameObject lagIndicator = null;
private GameObject dropCloneSource = null;
private float spawnDelay = 2.8f;
private GameObject curClone = null;
private BakeThreadHandle bakeHandle = null;
//counter for better console messages
public static int jobCounter;
public void Start()//public IEnumerator Start()
{
//Target mesh
mesh = new Mesh();
collider = gameObject.AddComponent<MeshCollider>();
filter = gameObject.AddComponent<MeshFilter>();
renderer = gameObject.AddComponent<MeshRenderer>();
filter.sharedMesh = mesh;
//LagIndicator
lagIndicator = GameObject.CreatePrimitive(PrimitiveType.Cube);
lagIndicator.transform.position = new Vector3(-5F, 0F, 0F);
//LagIndicator
dropCloneSource = GameObject.CreatePrimitive(PrimitiveType.Cube);
dropCloneSource.transform.position = new Vector3(-5F, 5F, 0F);
dropCloneSource.AddComponent<Rigidbody>();
//borrow material from primitve
renderer.sharedMaterial = lagIndicator.GetComponent<MeshRenderer>().sharedMaterial;
//init our bakehandle for threading (see bottom of this script)
bakeHandle = new BakeThreadHandle( OnFinishedThreadedMeshBake );
}
private static void Generate( Mesh targetMesh, float timeMod )
{
//generate array (COULD BE THREADED)
//https://www.patrykgalach.com/2019/07/29/procedural-terrain-pt1-plane/
int width = 180;
int depth = 180;
float scale = 0.05F;
// Defining vertices.
Vector3[] vertices = new Vector3[(width + 1) * (depth + 1)];
int i = 0;
float y = Random.Range(-15F,15F);
for (int d = 0; d <= depth; d++){
for (int w = 0; w <= width; w++){
vertices[i] = ( new Vector3(w, y, d) - new Vector3(width / 2f, 0, depth / 2f) )*scale;
i++;
}}
// Defining triangles.
int[] triangles = new int[width * depth * 2 * 3]; // 2 - polygon per quad, 3 - corners per polygon
for (int d = 0; d < depth; d++){
for (int w = 0; w < width; w++){
// quad triangles index.
int ti = (d * (width) + w) * 6; // 6 - polygons per quad * corners per polygon
// First tringle
triangles[ti] = (d * (width + 1)) + w;
triangles[ti + 1] = ((d + 1) * (width + 1)) + w;
triangles[ti + 2] = ((d + 1) * (width + 1)) + w + 1;
// Second triangle
triangles[ti + 3] = (d * (width + 1)) + w;
triangles[ti + 4] = ((d + 1) * (width + 1)) + w + 1;
triangles[ti + 5] = (d * (width + 1)) + w + 1;
}}
//assign to mesh (CANNOT BE THREADED)
targetMesh.vertices = vertices;
targetMesh.triangles = triangles;
targetMesh.RecalculateNormals();
}
public void Update()
{
lagIndicator.transform.Rotate(0F,0F,60F*Time.unscaledDeltaTime);
//BAKE in Update
if(Input.GetKeyDown(KeyCode.Alpha1)){ jobCounter++;
Debug.Log("═════Key 1═════════\nBAKING IN UPDATE, assignment should NOT take long [ job:"+jobCounter.ToString().PadLeft(4,'.')+" ]");
Generate( mesh, Time.time );//Generating vertex arrays etc. could be threaded too, but assigning .vertices|.triangles etc. to mesh, cannot be threaded
float start = Time.realtimeSinceStartup;
BakeMeshCollider( mesh );
print("[ job:"+jobCounter.ToString().PadLeft(4,'.')+" ] Meshcollider Baking in Main thread took:\n"+BakeThreadQueue.GetMS(Time.realtimeSinceStartup -start)+"" );
assignmentPending = true;
}
//Don't Bake
if(Input.GetKeyDown(KeyCode.Alpha2)){ jobCounter++;
Debug.Log("═════Key 2═════════\nDON'T BAKE assignment should take long [ job:"+jobCounter.ToString().PadLeft(4,'.')+" ]");
Generate( mesh, Time.time );//Generating vertex arrays etc. could be threaded too, but assigning .vertices|.triangles etc. to mesh, cannot be threaded
assignmentPending = true;
}
//Bake in Thread
if(Input.GetKeyDown(KeyCode.Alpha3)){ jobCounter++;
Debug.Log("═════Key 3═════════\nBAKING IN THREAD, assignment should NOT take long [ job:"+jobCounter.ToString().PadLeft(4,'.')+" ]");
if( bakeHandle.occupied ){
print("One Thread is already active");//for simplicity, only threadhandle allowed in this example
}else{
Generate( mesh, Time.time );//Generate could be threaded too
int meshID = mesh.GetInstanceID();//caching because 'targetMesh.GetInstanceID()' cant be called in worker thread!
//prepare & start thread (long version)
//ThreadStart threadstart = delegate { MeshUpdate_Threadable( meshID, bakeHandle ); };
//Thread t = new Thread( threadstart );
//t.Start();
//prepare & start thread (one line version)
new Thread( new ThreadStart( delegate { BakeMeshCollider_Threaded( meshID, bakeHandle );})).Start();
bakeHandle.startTime = Time.realtimeSinceStartup;
bakeHandle.occupied = true;
}
}
//Collison Test Object
if(Input.GetKeyDown(KeyCode.Space)){
if(curClone!= null){ Destroy(curClone); }
curClone = GameObject.Instantiate(dropCloneSource, Vector3.up *10, Quaternion.identity);
}
BakeThreadQueue.CheckFinishedThreads();
}
private bool assignmentPending = false;
public void FixedUpdate()
{
if( assignmentPending ){
float start = Time.realtimeSinceStartup;
// there will be no bake here because we baked in Update() before
collider.sharedMesh = mesh;
assignmentPending = false;
print("[ job:"+jobCounter.ToString().PadLeft(4,'.')+" ] Meshcollider assignment in FixedUpdate took:\n"+BakeThreadQueue.GetMS(Time.realtimeSinceStartup -start)+"" );
}
}
//NO THREADING
private static void BakeMeshCollider( Mesh targetMesh )
{
Physics.BakeMesh( targetMesh.GetInstanceID(), false);
}
//THREADING
private static void BakeMeshCollider_Threaded( int targetMeshInstanceID, BakeThreadHandle bakeHandle )
{
Physics.BakeMesh( targetMeshInstanceID, false);
//report that the bake has finished
lock( BakeThreadQueue.bakeFinishedQueue ){ //threads need to lock things that may be accessed by multiple threads at once
BakeThreadQueue.bakeFinishedQueue.Enqueue( bakeHandle );
}
}
//used as callback on our handle
private void OnFinishedThreadedMeshBake(){
float start = Time.realtimeSinceStartup;
collider.sharedMesh = mesh;
print("[ job:"+jobCounter.ToString().PadLeft(4,'.')+" ] Meshcollider assignment after threaded baking took:\n"+BakeThreadQueue.GetMS(Time.realtimeSinceStartup -start)+"" );
}
}
namespace BakeThreads{
public class BakeThreadHandle
{
public readonly System.Action callback;
public bool occupied;
public float startTime;
public BakeThreadHandle( System.Action callback){
this.callback = callback;
}
}
public static class BakeThreadQueue
{
public static Queue<BakeThreadHandle> bakeFinishedQueue = new Queue<BakeThreadHandle>();
public static bool allowMultipleDequeues = false;
//check every Update if thread is ready
public static void CheckFinishedThreads(){
for(int i=0; i<bakeFinishedQueue.Count; i++){
BakeThreadHandle handle = bakeFinishedQueue.Dequeue(); //get finished task handle
Debug.Log( "[ job:"+CreateTriangle.jobCounter.ToString().PadLeft(4,'.')+" ] Finished baking in another Thread!" +
"BakeThread took about \n"+GetMS(Time.realtimeSinceStartup -handle.startTime)+" to complete" ); //measure time it took
//do what must be done after baking has finsihed
handle.callback();
handle.occupied = false;
//early exit if we only want one dequeue per frame, e.g. if the callback is a large non threadable function and performing multiple might cause lag
if(!allowMultipleDequeues){ break; }
}
}
public static string GetMS( float seconds, string msDecimalPlaces = "F6" ){
return ""+(1000F *seconds).ToString( msDecimalPlaces ) +"ms";
}
}
}