Search Unity

SetBlendShapeWeight() for mesh is delayed??

Discussion in 'Editor & General Support' started by Airmouse, Feb 26, 2020.

  1. Airmouse

    Airmouse

    Joined:
    Jan 12, 2019
    Posts:
    107
    I am trying to automate creating profile pictures of my characters, but I need to set facial blendshapes first. After that I want to render a camera and save it's RenderTexture as a .png file.

    So I have written this code that runs in a CustomEditor. When a "Create Profile" button is pressed it sets the faceshapes, renders the camera and then saves the texture to a file.

    But when I run it, I see the new facial expression was set properly in the viewport, but the saved images does not show the blendshaps have changed - shows the previous shapes.

    I have to press the "Create Profile" button twice to get the expected result.

    Code (csharp):
    1. //sudo code
    2.  
    3. //Set the facial blendshapes for the character
    4. for (int i = 0; i < builder.blendShapeWeights.Length; ++i)
    5. {
    6.     var shapeWeight = builder.blendShapeWeights[i];
    7.     linkerArmature.faceRenderer.SetBlendShapeWeight(i, shapeWeight * 100);
    8. }
    9.  
    10. //Render the character profile picture
    11. RenderTexture.active = m_camera.targetTexture;
    12. m_camera.Render();
    13.  
    14. Texture2D image = new Texture2D(m_camera.targetTexture.width, m_camera.targetTexture.height);
    15. image.ReadPixels(new Rect(0, 0, m_camera.targetTexture.width, m_camera.targetTexture.height), 0, 0);
    16. image.Apply();
    17.  
    18. //Now save the image
    19. var Bytes = image.EncodeToPNG();
    20. DestroyImmediate(image);
    21.  
    22. var destDir = Path.GetDirectoryName (destPath);
    23. if (!Directory.Exists (destDir)) {
    24.    Directory.CreateDirectory (destDir);
    25. }
    26. File.WriteAllBytes(destPath, Bytes);
    27.  
    28. AssetDatabase.Refresh();
    It is beyond me. Why Unity is not setting the blendshapes instantly???

    Why would setting all blendshapes then calling m_camera.Render() not render the intended values until after the editor redraws once?

    How can I work around this limitation?
     
    Last edited: Feb 26, 2020
  2. Airmouse

    Airmouse

    Joined:
    Jan 12, 2019
    Posts:
    107
    I had to hack Unity to make it work. My code is now very bad looking, but it was the only way to force Unity to do what I need.

    I now connect to OnEditorUpdate callback and 'wait' a few updates so Unity editor can catch up with it's self. Then disconnect OnEditorUpdate immediately after rendering,

    Now after one entire update Unity finally applies the blendshapes. Only then can rendering the mesh finally produces the expected result.!!!

    Here's my super hacky code that I had to create just to modify blendshapes and render to a image file:
    Code (csharp):
    1.  
    2. private int updateCount = 0; //keep track of how many extra editor updates before allowing rendering of image (hack)
    3. protected virtual void OnEditorUpdate()
    4. {
    5.     if (updateCount > 0) //After editor updates a few times it is okay to render and blendshapes are finally applied.
    6.     {
    7.        //Render the character profile picture
    8.        RenderTexture.active = m_camera.targetTexture;
    9.        m_camera.Render();
    10.  
    11.        Texture2D image = new Texture2D(m_camera.targetTexture.width, m_camera.targetTexture.height);
    12.        image.ReadPixels(new Rect(0, 0, m_camera.targetTexture.width, m_camera.targetTexture.height), 0, 0);
    13.        image.Apply();
    14.  
    15.        //Now save the image
    16.        var Bytes = image.EncodeToPNG();
    17.        DestroyImmediate(image);
    18.  
    19.        var destDir = Path.GetDirectoryName (destPath);
    20.        if (!Directory.Exists (destDir)) {
    21.           Directory.CreateDirectory (destDir);
    22.        }
    23.        File.WriteAllBytes(destPath, Bytes);
    24.  
    25.        AssetDatabase.Refresh();
    26.        return;
    27.     }
    28.     updateCount++; //let the editor update - EXTREMLEY HACKY - But works...
    29.  
    30.     Debug.Log("EDITOR UPDATE " + updateCount);
    31. }
    32.  
    33. public override void OnInspectorGUI()
    34. {
    35.     ...
    36.     if(GUILayout.Button("Create Profile"))
    37.     {
    38.        //Set the facial blendshapes for the character
    39.        for (int i = 0; i < builder.blendShapeWeights.Length; ++i)
    40.        {
    41.            var shapeWeight = builder.blendShapeWeights[i];
    42.            skinnedMesh.SetBlendShapeWeight(i, shapeWeight * 100);
    43.        }
    44.        EditorApplication.update += OnEditorUpdate; //now wait one update for Unity editor to finally catch up and apply blendshapes - EXTREMLEY HACKY - But works...
    45.     }
    46. }
    47.  
    Note: it connects the OnEditorUpdate just after changing blendshape weights, then waits one whole update, then renders the camera texture, finally it disconnects the OnEditorUpdate and resets updateCount back to zero for next run.

    It's so ugly!!! Unity why???

    How do I fix this proper? :)
     
    Last edited: Feb 26, 2020