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

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?

Select the imported meshes and enable read/write in the inspector under import settings.

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…

Are you not creating the asset bundles?

For anyone still looking for the answer:

yourObject.GetComponent<MeshFilter>().mesh.UploadMeshData(false);

Check more on UnityAPI:

2 Likes

This seems to only work to set isReadable to false, not true

1 Like

You can use SerializedObject to make readable at UnityEditor:

public void ProcessExportObject(Transform root)
{
    if (root == null)
    {
        return;
    }

    for (int i = root.childCount - 1; i >= 0; i--)
    {
        var child = root.GetChild(i);
        if (!child.gameObject.activeSelf)
        {
            GameObject.DestroyImmediate(child.gameObject);
        }
        else
        {
            var smr = child.GetComponent<SkinnedMeshRenderer>();
            if (smr != null)
            {
                var sharedMesh = smr.sharedMesh;
                if (!sharedMesh.isReadable)
                {
                    sharedMesh.UploadMeshData(false);
                    var so = new SerializedObject(sharedMesh);
                    so.Update();
                    var sp = so.FindProperty("m_IsReadable");
                    sp.boolValue = true;
                    so.ApplyModifiedProperties();
                    smr.sharedMesh = sharedMesh;
                    // GameObject.DestroyImmediate(child.gameObject);
                }
            }
        }
    }

    foreach (Transform t in root)
    {
        ProcessExportObject(t);
    }
}

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.

        public static Mesh MakeReadableMeshCopy(Mesh nonReadableMesh)
        {
            Mesh meshCopy = new Mesh();
            meshCopy.indexFormat = nonReadableMesh.indexFormat;

            // Handle vertices
            GraphicsBuffer verticesBuffer = nonReadableMesh.GetVertexBuffer(0);
            int totalSize = verticesBuffer.stride * verticesBuffer.count;
            byte[] data = new byte[totalSize];
            verticesBuffer.GetData(data);
            meshCopy.SetVertexBufferParams(nonReadableMesh.vertexCount, nonReadableMesh.GetVertexAttributes());
            meshCopy.SetVertexBufferData(data, 0, 0, totalSize);
            verticesBuffer.Release();

            // Handle triangles
            meshCopy.subMeshCount = nonReadableMesh.subMeshCount;
            GraphicsBuffer indexesBuffer = nonReadableMesh.GetIndexBuffer();
            int tot = indexesBuffer.stride * indexesBuffer.count;
            byte[] indexesData = new byte[tot];
            indexesBuffer.GetData(indexesData);
            meshCopy.SetIndexBufferParams(indexesBuffer.count, nonReadableMesh.indexFormat);
            meshCopy.SetIndexBufferData(indexesData, 0, 0, tot);
            indexesBuffer.Release();

            // Restore submesh structure
            uint currentIndexOffset = 0;
            for (int i = 0; i < meshCopy.subMeshCount; i++)
            {
                uint subMeshIndexCount = nonReadableMesh.GetIndexCount(i);
                meshCopy.SetSubMesh(i, new SubMeshDescriptor((int)currentIndexOffset, (int)subMeshIndexCount));
                currentIndexOffset += subMeshIndexCount;
            }

            // Recalculate normals and bounds
            meshCopy.RecalculateNormals();
            meshCopy.RecalculateBounds();

            return meshCopy;
        }
9 Likes

@pohype :

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!

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?

1 Like

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!!

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.

Thanks, @StarBornMoonBeam ! 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!

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.

Thanks, @StarBornMoonBeam . 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 @pohype ) 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!

@bryanlarock what about using AsyncGPUReadback requests ? Isn’t available in Unity 2020.3 ?

Yes!! Thank you, @pohype ! 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!

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.

1 Like