Search Unity

Reading meshes at runtime that are not enabled for read/write!!

Discussion in 'Scripting' started by andyz, Aug 12, 2020.

Thread Status:
Not open for further replies.
  1. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,276
    I have a problem that I want to read meshes to export parts of a scene at runtime.
    But many models are imported at runtime from assetbundles & so when I go to read them (mesh.triangles) I get:
    "Not allowed to access triangles/indices on mesh 'xxx' (isReadable is false; Read/Write must be enabled in import settings)"

    Is there any way around that to get the mesh data as I can not guarantee all models are exported with isReadable true?
     
    Last edited: Aug 12, 2020
  2. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    Select the imported meshes and enable read/write in the inspector under import settings.
     
  3. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,276
    You miss some points as I can not 'select the imported meshes' - this is runtime and loaded assetbundles may not have meshes with the enable read/write on. If that means there is no way to read the mesh data then so be it...
     
  4. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    Are you not creating the asset bundles?
     
  5. Na2CuC4O8

    Na2CuC4O8

    Joined:
    Aug 6, 2020
    Posts:
    2
    Armegalo and Bunny83 like this.
  6. nat-soragge

    nat-soragge

    Joined:
    May 15, 2013
    Posts:
    8
    lucbloom likes this.
  7. Langdex

    Langdex

    Joined:
    Aug 15, 2019
    Posts:
    1
    You can use SerializedObject to make readable at UnityEditor:

    Code (CSharp):
    1.  
    2. public void ProcessExportObject(Transform root)
    3. {
    4.     if (root == null)
    5.     {
    6.         return;
    7.     }
    8.  
    9.     for (int i = root.childCount - 1; i >= 0; i--)
    10.     {
    11.         var child = root.GetChild(i);
    12.         if (!child.gameObject.activeSelf)
    13.         {
    14.             GameObject.DestroyImmediate(child.gameObject);
    15.         }
    16.         else
    17.         {
    18.             var smr = child.GetComponent<SkinnedMeshRenderer>();
    19.             if (smr != null)
    20.             {
    21.                 var sharedMesh = smr.sharedMesh;
    22.                 if (!sharedMesh.isReadable)
    23.                 {
    24.                     sharedMesh.UploadMeshData(false);
    25.                     var so = new SerializedObject(sharedMesh);
    26.                     so.Update();
    27.                     var sp = so.FindProperty("m_IsReadable");
    28.                     sp.boolValue = true;
    29.                     so.ApplyModifiedProperties();
    30.                     smr.sharedMesh = sharedMesh;
    31.                     // GameObject.DestroyImmediate(child.gameObject);
    32.                 }
    33.             }
    34.         }
    35.     }
    36.  
    37.     foreach (Transform t in root)
    38.     {
    39.         ProcessExportObject(t);
    40.     }
    41. }
     
  8. poprev-3d

    poprev-3d

    Joined:
    Apr 12, 2019
    Posts:
    72
    I would recommend using the low-level mesh api with GraphicsBuffer!

    It must be noted than when a Mesh is not readable, it only lives in the GPU memory and NOT in the CPU memory. Therefore, to make it "readable", one must pull back the data from the GPU.

    In my example, I make a copy of the mesh using the low level Mesh api with buffer, it's pretty efficient and straightforward. It works at runtime.

    Code (CSharp):
    1.  
    2.         public static Mesh MakeReadableMeshCopy(Mesh nonReadableMesh)
    3.         {
    4.             Mesh meshCopy = new Mesh();
    5.             meshCopy.indexFormat = nonReadableMesh.indexFormat;
    6.  
    7.             // Handle vertices
    8.             GraphicsBuffer verticesBuffer = nonReadableMesh.GetVertexBuffer(0);
    9.             int totalSize = verticesBuffer.stride * verticesBuffer.count;
    10.             byte[] data = new byte[totalSize];
    11.             verticesBuffer.GetData(data);
    12.             meshCopy.SetVertexBufferParams(nonReadableMesh.vertexCount, nonReadableMesh.GetVertexAttributes());
    13.             meshCopy.SetVertexBufferData(data, 0, 0, totalSize);
    14.             verticesBuffer.Release();
    15.  
    16.             // Handle triangles
    17.             meshCopy.subMeshCount = nonReadableMesh.subMeshCount;
    18.             GraphicsBuffer indexesBuffer = nonReadableMesh.GetIndexBuffer();
    19.             int tot = indexesBuffer.stride * indexesBuffer.count;
    20.             byte[] indexesData = new byte[tot];
    21.             indexesBuffer.GetData(indexesData);
    22.             meshCopy.SetIndexBufferParams(indexesBuffer.count, nonReadableMesh.indexFormat);
    23.             meshCopy.SetIndexBufferData(indexesData, 0, 0, tot);
    24.             indexesBuffer.Release();
    25.  
    26.             // Restore submesh structure
    27.             uint currentIndexOffset = 0;
    28.             for (int i = 0; i < meshCopy.subMeshCount; i++)
    29.             {
    30.                 uint subMeshIndexCount = nonReadableMesh.GetIndexCount(i);
    31.                 meshCopy.SetSubMesh(i, new SubMeshDescriptor((int)currentIndexOffset, (int)subMeshIndexCount));
    32.                 currentIndexOffset += subMeshIndexCount;
    33.             }
    34.  
    35.             // Recalculate normals and bounds
    36.             meshCopy.RecalculateNormals();
    37.             meshCopy.RecalculateBounds();
    38.  
    39.             return meshCopy;
    40.         }
    41.  
    42.  
     
    Last edited: Mar 21, 2023
  9. bryanlarock

    bryanlarock

    Joined:
    May 30, 2022
    Posts:
    5
    @poprev-3d:

    Thanks so much for the above! This is exactly what I need. Unfortunately this does not work for Unity 2020.3 as the GetVertexBuffer() and GetIndexBuffer() methods had not yet been implemented for Mesh. It seems these methods were implemented in the very next major version of Unity.

    Do you have any suggestions as to how to get access to non-readable mesh data in 2020.3? I'm making a mod for a game that uses this version of Unity, so I'm unfortunately stuck with this version.

    Thank you!
     
  10. FrankvHoof

    FrankvHoof

    Joined:
    Nov 3, 2014
    Posts:
    258
    A bit of a non-standard approach:
    What about writing the data to a texture in a shader, then pushing that texture to RAM using AsyncGPUReadback?
     
    bryanlarock likes this.
  11. bryanlarock

    bryanlarock

    Joined:
    May 30, 2022
    Posts:
    5
    This sounds interesting! I'm afraid I'm still pretty new in this space, so I don't quite know how I'd follow your suggestion. I'll do some research today and see what I can figure out, though! Thank you!!
     
  12. StarBornMoonBeam

    StarBornMoonBeam

    Joined:
    Mar 26, 2023
    Posts:
    209
    You can read a mesh as a text file, though it is encrypted, if you can get behind the encryption you have all of the data needed to make a mesh without having to get the write access for it.

    using the wavefront object format a mesh can be read as a text file and created at instance. Though some duplicate data is part missing if it repeated in order to reduce the size of the wavefront obj file. Notably the normals.

    That is generally the only approach I will take. Get all lines of the file and make a copy. After all the original mesh file should remain to be unwritable! Otherwise you might erase your models.
     
  13. bryanlarock

    bryanlarock

    Joined:
    May 30, 2022
    Posts:
    5
    Thanks, @SunbeamCoffee! Since I'm making a mod, I don't have immediate access to the original asset files. I'm able to instantiate prefabs that I need from asset bundles, though. Do you happen to know if there's any way that I could export the prefab meshes to text files? If I could do that, indeed I think I would have access to everything I need. Unfortunately, the only functionality I can find in Unity to export assets is contained within the UnityEditor namespace. At runtime, I have no access to anything in this namespace.

    Thanks again!
     
  14. StarBornMoonBeam

    StarBornMoonBeam

    Joined:
    Mar 26, 2023
    Posts:
    209
    https://learn.microsoft.com/en-us/dotnet/api/system.io.file.create?view=net-8.0
    ^ the science of file creation

    As for what to put into the file, familiarise yourself with what a mesh is and how it’s made

    https://docs.unity3d.com/ScriptReference/Mesh.html

    I am no Unity asset exporter so I couldn’t tell you anything on those lines. Like I say all I would do is create a file using the file system and throw all my mesh data into it. And then read that file back.

    if you had never done any of those things you’re in for a small learning curve as there are no shortcuts. You’ll have to learn how to write the file, write the file efficiently, and how to read it back. You’ll also have to learn about how to make a mesh out of points norms and triangles. So I guess it is fairly advanced.
    this type of thing is not unique to Unity, and really has little to do with Unity except that you’re working with that program. Unity asset database is all Unity all the time, and whatever Unity abilities have been made available to you in ed or at runtime is where it ends. Though the shader hack strategy mentioned FrankVHoof sparked my interest in replying here. Though you should likely branch to your own thread if you needed any more help or direction.
     
  15. bryanlarock

    bryanlarock

    Joined:
    May 30, 2022
    Posts:
    5
    Thanks, @SunbeamCoffee. I really appreciate all the detailed input. Unfortunately, the basic problem is that Unity does not give me access to the internal Mesh data that I need. I can't manually write data to a file because I don't have access to the data. It's a bit of a catch 22.

    As I understand it, when Unity loads meshes for a game, there is an option as to whether the mesh data should be "readable". If this is true, the mesh data goes into both CPU RAM and GPU RAM. If the option is false ("non-readable"), the data goes into GPU RAM and is no longer available for inspection or manipulation by Unity code. This option is set on the mesh and is baked into the prefab. Once a mesh is non-readable, there is no way to undo this. The meshes that I need are unfortunately all non-readable.

    There are ways to get the data out of GPU RAM (as @poprev-3d) describes above, but the particular method explained does not work for Unity 2020.3.

    I'll keep looking into the shader texture idea, though. Thank you again so much for the input!
     
  16. poprev-3d

    poprev-3d

    Joined:
    Apr 12, 2019
    Posts:
    72
    @bryanlarock what about using AsyncGPUReadback requests ? Isn't available in Unity 2020.3 ?
     
  17. bryanlarock

    bryanlarock

    Joined:
    May 30, 2022
    Posts:
    5
    Yes!! Thank you, @poprev-3d! I did see this functionality, but I wasn't sure if this might be able to get what I need. Sorry for my delayed response. I put this part of my mod on the backburner, but I'll get back to trying to get the mesh data soon. I know almost nothing about how AsyncGPUReadback works. I'd greatly appreciate any intro pointers you might be willing to share. I need to dig in on my own, though, so no worries if not!

    Thank you again!
     
  18. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Closing.
    These type of discussions are not allowed here. Modifying / extracting assets from a game / project you do not have rights to is not something for these forums. Contact the game publisher for any additional information you need on this topic.
     
    Kurt-Dekker likes this.
Thread Status:
Not open for further replies.