Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Graphics.DrawMesh in Editor

Discussion in 'General Graphics' started by CDF, Jun 22, 2017.

  1. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Been struggling with this for a while now. I need to draw a mesh using Graphics.DrawMesh, not Graphics.DrawMeshNow as I want lighting information.

    The problem is "Update" is only called when the scene changes. As a result, my mesh disappears under certain situations, like:

    - Losing application focus: e.g: toggling between Unity and Visual Studio.
    - Randomly after script reloads
    - Focusing on an object, sometimes unity clears graphics when doing this but doesn't call Update after.

    I know I can't use: OnRenderObject, DrawGizmos, EditorApplication.update. Because these are called many times before unity actually performs scene rendering. Drawing a mesh in OnWillRenderObject crashes Unity.

    So my question is: How can I tell Unity to render the scene, or clear the graphics in edit mode?

    As an example, imagine a cube that is rendered in-front of the scene camera that needs to react to light. In order to do this, I need an event that updates frequently, has access to Camera.current and only draws before the scene is about to be rendered. The object/script responsible for drawing cannot be added to the camera. The object/script cannot be made dirty when the scene camera moves: i.e don't modify the scene in any way.

    I think this scenario is impossible with Graphics.DrawMesh. But just wanted to check if there is a possible solution. I might be able to try CommandBuffers, but that's a whole other beast, especially if you want Cast/Receive shadows.

    The endless struggle continues...
     
  2. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    943
  3. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Thanks for the response. Been playing around with this more. Turns out you can just set the material to use instancing. However, there's some extra weirdness going on:
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [ExecuteInEditMode]
    5. public class GraphicsTest : MonoBehaviour {
    6.  
    7.     public Mesh mesh;
    8.     public Material material;
    9.  
    10.     private void OnEnable() {
    11.  
    12.         UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUI;
    13.         UnityEditor.SceneView.onSceneGUIDelegate += OnSceneGUI;
    14.     }  
    15.  
    16.     private void OnDisable() {
    17.  
    18.         UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUI;
    19.     }
    20.  
    21.     private void OnSceneGUI(SceneView sceneView) {
    22.  
    23.         Draw(sceneView.camera);
    24.     }
    25.  
    26.     private void Update() {
    27.  
    28.         Draw(Camera.main);
    29.     }
    30.  
    31.     private void Draw(Camera camera) {
    32.  
    33.         if (mesh && material && camera) {
    34.  
    35.             Matrix4x4 matrix = camera.transform.localToWorldMatrix * Matrix4x4.TRS(Vector3.forward * 10, Quaternion.identity, Vector3.one);
    36.  
    37.             //For some reason, this stops Unity from piling up the draw commands?
    38.             material.enableInstancing = true;
    39.  
    40.             //No trail, object visible twice in scene, scene mesh visible in game.
    41.             Graphics.DrawMesh(mesh, matrix, material, gameObject.layer, null);
    42.            
    43.             //Leaves a small trail, object only visible in camera
    44.             //Graphics.DrawMesh(mesh, matrix, material, gameObject.layer, camera);
    45.         }
    46.     }
    47. }
    If I draw the mesh without supplying a Camera to render to, the mesh is drawn twice in the scene and in the game, this is expected because the Docs state passing null here draws the mesh in every camera.

    Ok, so I want to just draw the mesh in the camera supplied. Cool, oh wait... It leaves a small trail of meshes before clearing itself. Setting the material to a transparent type, shows that the mesh is drawn multiple times before being cleared.

    Very strange.

    OnRenderObject seems unusable, supplying the camera to DrawMesh in OnRenderObject results in no mesh being drawn. I suspect this is because rendering has already occured, so the mesh is queued for the next run, but the camera clears everything before it even gets drawn next time.

    Anyone got any ideas as to why the mesh would be drawn multiple times with an instanced material? Or even why instancing works in this case? Why does a non-instanced material leave an infinite trail meshes, but an instanced one leaves only a small trail of meshes. None of this makes any sense.

    I tried looking into Command Buffers, but that was a total fail, trying to draw a mesh with a Command Buffer with lighting seems to be reserved for people with graphics chips in their brains. Such a simple thing made unnecessarily difficult.
     
  4. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Ok, I think I figured it out. By using a flag telling the object to render, I can limit it to render once OnSceneGUI
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [ExecuteInEditMode]
    5. public class GraphicsTest : MonoBehaviour {
    6.  
    7.     public Mesh mesh;
    8.     public Material material;
    9.  
    10.     private bool render;
    11.  
    12.     private void OnEnable() {
    13.  
    14.         UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUI;
    15.         UnityEditor.SceneView.onSceneGUIDelegate += OnSceneGUI;
    16.     }  
    17.  
    18.     private void OnDisable() {
    19.  
    20.         UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUI;
    21.     }
    22.  
    23.     private void OnSceneGUI(SceneView sceneView) {
    24.  
    25.         if (render) {
    26.  
    27.             render = false;
    28.  
    29.             Draw(sceneView.camera, sceneView.camera.transform.localToWorldMatrix * Matrix4x4.TRS(Vector3.forward * 10, Quaternion.identity, Vector3.one));
    30.         }
    31.     }
    32.  
    33.     /*
    34.     This causes object to be rendered twice when playing
    35.  
    36.     private void OnRenderObject() {
    37.        
    38.         render = true;
    39.     }
    40.     */
    41.  
    42.     private void OnDrawGizmos() {
    43.  
    44.         render = true;
    45.     }
    46.  
    47.     private void Update() {
    48.  
    49.         Draw(Camera.main, Matrix4x4.identity);
    50.     }
    51.  
    52.     private void Draw(Camera camera, Matrix4x4 matrix) {
    53.  
    54.         if (mesh && material && camera) {
    55.  
    56.             Graphics.DrawMesh(mesh, matrix, material, gameObject.layer, camera);
    57.         }
    58.     }
    59. }
    60.  
    In the above code, the scene mesh is drawn at the scene camera (albeit a little laggy behind, but that's ok) and is not visible in the game camera. The same occurs for the game camera, mesh is drawn at 0,0,0 in the game view

    :D

    No need for instancing either! Seems that using OnRenderObject was setting the "render" flag to true more than once. Could probably fix that by checking the current camera, but OnDrawGizmos works out-of-the box
     
    CoughE likes this.
  5. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Ok, this is the one, simple and works much better
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [ExecuteInEditMode]
    5. public class GraphicsTest : MonoBehaviour {
    6.  
    7.     public Mesh mesh;
    8.     public Material material;
    9.  
    10.     private void OnEnable() {
    11.  
    12.         Camera.onPreCull -= DrawWithCamera;
    13.         Camera.onPreCull += DrawWithCamera;
    14.     }
    15.  
    16.     private void OnDisable() {
    17.  
    18.         Camera.onPreCull -= DrawWithCamera;
    19.     }
    20.  
    21.     private void DrawWithCamera(Camera camera) {
    22.  
    23.         if (camera) {
    24.  
    25.             Draw(camera, camera.transform.localToWorldMatrix * Matrix4x4.TRS(Vector3.forward * 10, Quaternion.identity, Vector3.one));
    26.         }
    27.     }
    28.  
    29.     private void Draw(Camera camera, Matrix4x4 matrix) {
    30.  
    31.         if (mesh && material) {
    32.  
    33.             Graphics.DrawMesh(mesh, matrix, material, gameObject.layer, camera);
    34.         }
    35.     }
    36. }
    No idea when those Camera events became a thing, but very thankful for them
     
  6. 00jknight

    00jknight

    Joined:
    Mar 28, 2014
    Posts:
    34
    Logged in to say thank you for this. I had a hacky mess with Repaint events, OnSceneGui and Update to render my meshes into Scene and GameView and this works much better.
     
    OxDEADFACE likes this.