How to flush mesh batch in editor OR how to draw a mesh for single frame in editor?

Short version: I’m drawing mesh from EditorWindow in edit mode, scene view and the mesh is not drawn for single frame, but instead it remains in scene until certain event (like saving scene) occurs - how can I make the mesh to be rendered for single frame? (For next frame I move it, so I need to be rendered in different place so original mesh is not needed to be rendered anymore.)

See the image bellow: I want to draw the blue box only once!

Long version: I want to add following functionality: user selects a Game Object (from either current scene or prefabs) and then puts it into the scene. But when the object is put there (and only at that time), I want to run some code to transform it (especially position, but I have other reasons (currently not implemented) why drag and drop from Prefabs is not enough).

For this purpose I created a EditorWindow object which creates gui to select the object to be added (I plan in future to make a list for simple fast selection) and this window also renders the mesh as preview when user moves mouse over scene view. For this reason, I do not want to create the object before user confirms it by clicking.

This is code I use (please note that instantiating the new object and transforming it is not implemented yet):

using UnityEngine;
using System.Collections;
using UnityEditor;

[ExecuteInEditMode]
public class BlockAdderWindow : EditorWindow
{
	struct CurrentSelection
	{
		public bool isSelected;
		public Vector3 position;
	}
	
	[SerializeField]
	Material previewMaterial;
	
	[SerializeField]
	bool enabled;
	
	[SerializeField]
	GameObject parentObject = null;
	
	[SerializeField]
	GameObject templateObject = null;
	
	Mesh renderMesh;
	
	CurrentSelection currentSelection;
	
	[MenuItem("Window/Block Adder")]
	static void Init()
	{
		EditorWindow.GetWindow(typeof(BlockAdderWindow)).Show();
	}
	
	void OnEnable()
	{
		SceneView.onSceneGUIDelegate += OnSceneGUI;
	}
	
	void OnDisable()
	{
		SceneView.onSceneGUIDelegate -= OnSceneGUI;
	}
	
	void OnSceneGUI(SceneView sceneView)
	{
		if(enabled)
		{
			Camera camera = sceneView.camera;
			Vector2 mousePosition = Event.current.mousePosition;
			mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y;
			CalcSelection(camera, mousePosition);
		}
		
		RenderSelection();
	}
	
	void Update()
	{
		//RenderSelection();
	}

	void OnGUI()
	{
		EditorGUILayout.BeginVertical();
		{
			GameObject oldTemplate = templateObject;
			
			enabled = EditorGUILayout.Toggle("Enabled", enabled);
			previewMaterial = EditorGUILayout.ObjectField("Preview material", previewMaterial, typeof(Material), true) as Material;
			parentObject = EditorGUILayout.ObjectField("Parent", parentObject, typeof(GameObject), true) as GameObject;
			templateObject = EditorGUILayout.ObjectField("Template", templateObject, typeof(GameObject), true) as GameObject;
			
			renderMesh = EditorGUILayout.ObjectField("Render mesh", renderMesh, typeof(Mesh), true) as Mesh;
			
			if( (oldTemplate != templateObject) || ((templateObject != null) && (renderMesh == null)) )
			{
				renderMesh = null;
				
				if(templateObject)
				{
					MeshFilter meshFilter = templateObject.GetComponent<MeshFilter>();
					
					if(meshFilter)
					{
						renderMesh = meshFilter.sharedMesh;
					}
				}
				
				enabled = enabled && (renderMesh != null);
			}
		}
		EditorGUILayout.EndVertical();
	}
	
	void CalcSelection(Camera camera, Vector2 mousePosition)
	{
		//currentSelection.isSelected = false;
		
		if(camera != null)
		{
			Ray ray = camera.ScreenPointToRay(mousePosition);
			RaycastHit hit;
			
			if(Physics.Raycast(ray, out hit))
			{
				currentSelection.isSelected = true;
				currentSelection.position = hit.point;
				//Debug.Log("Selected " + hit.collider.gameObject.name);
			}
		}
	}
	
	void RenderSelection()
	{
		if(enabled && currentSelection.isSelected)
		{
			Graphics.DrawMesh(renderMesh, currentSelection.position,
				Quaternion.identity, previewMaterial, 0);
		}
	}
}

Problem is with line Graphics.DrawMesh(…) - it does not draw the mesh for single frame only - instead, it remains there so when user moves with mouse, it looks like adding multiple objects:

Image on left displays standard scene, image on right displays the problem - I moved the mouse over the scene (only one blue box is expected).

All the meshes disappears when:

  • I save the scene
  • I select item which is displayed in inspector but it wasn’t selected before
  • Game is started

When the game is running and I switch to Scene View, the code works as expected: only one mesh in a frame is rendered.

I created simple project for reproducing the error, you can [19196-littleproject.zip|19196].

To reproduce the problem, follow these steps:

  • Open the project
  • Open scene Assets/Scenes/Scene
  • Open my window: Window / Block Adder
  • Drag and drop Game Object named “Box” to “Template” field in the Block Adder Window
  • Drag and drop Assets/Materials/preview_mat to “Preview material” field in the Block Adder Window
  • Toggle “Enabled” (in the Block Adder Window) to enable the functionality
  • Move the mouse over the scene in Scene View Window.
  • Observe the issue.

My environment:

  • Windows 7 Professional SP1
  • Unity 4.3.0f4 (Free version currently)

I found the solution:

MonoBehaviour.OnRenderObject

Make all your Graphics.DrawMeshNow calls from within there and it will work as expected.

Here’s a workaround (Unity 5.6.1f1) that allows you to use regular Graphics.DrawMesh instead of DrawMeshNow. It relies on checking when the “frame” (you don’t really have the frame loop in editor) changed and submitting new draw calls only then.

int lastRenderedFrame;

void Start() {
    SceneView.onSceneGUIDelegate += OnSceneGUI;
}

void OnSceneGUI(SceneView view)
{
    if (Event.current.type == EventType.Layout)
    {
        // Force the view to update.
        EditorUtility.SetDirty(gameObject);

        // Only update if we know that the queue was cleared.
        if (lastRenderedFrame != Time.renderedFrameCount)
        {
            drawMyMeshes(); // Uses Graphics.DrawMesh()
            lastRenderedFrame = Time.renderedFrameCount;
        }
    }
}

This answer is for people who are running into the same problem but are drawing inside the OnToolGUI() method of an EditorTool!


I am using the new EditorTool clas for creating a custom tool that can place blocks in a grid. I am drawing previews of the blocks with Graphics.DrawMesh() but i was running into similar problems as the OP. I have found many threads discussing this problem but none of the solutions worked for me. That is, until I tried the answer of @pvaananen in this thread. I can’t seem to reply directly to his/her comment so I’m posting this here.


So: If you are trying to draw meshes directly inside the OnToolGUI() of an EditorTool, try the answer of @pvaananen! I’ve replaced the EditorUtility.SetDirty(gameObject); part with EditorUtility.SetDirty(this); and that seems to do the trick.