3

enter image description here In the short clip above you will see I have a fairly minimal set-up in Unity. There is a counter that is incremented every frame (in Update), along side that is this call 'UnityEditor.SceneView.RepaintAll()'. You can see a counter for that being outputted to the Console box.

Once I snap into the Game view and click on a button in the bottom left hand corner (the highlighted button press), it calls a script which imports a mesh at run time into the scene and renders it. You will notice the counter and repaint calls freeze during this, albeit brief, but very noticeable period of time. In actuality, everything in the scene freezes during this load time.

I have been googling how to refresh Unity's scene so that I can, in essence, load meshes at run time while maintaining responsiveness for the player. So far, nothing.

What is the best practice here to periodically refresh/update the scene so that the player isn't afflicted by load time game halts?

EDIT: RepaintAll() call does not work for me. Should have added that previously. This method does not work as I had hoped it would, in fact I am not exactly sure what its purpose is if not to refresh the scene

Mesh import code:

    const int MAX_FACETS_PER_MESH = 65535 / 3;

    class Facet
    {
        public Vector3 normal;
        public Vector3 a, b, c;

        public override string ToString()
        {
            return string.Format("{0:F2}: {1:F2}, {2:F2}, {3:F2}", normal, a, b, c);
        }
    }

    private static Mesh[] ImportBinary(string path)
    {
        Facet[] facets;

        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
            {
                // read header
                byte[] header = br.ReadBytes(80);
                uint facetCount = br.ReadUInt32();
                facets = new Facet[facetCount];

                for(uint i = 0; i < facetCount; i++)
                {
                    facets[i] = new Facet();

                    facets[i].normal.x = br.ReadSingle();
                    facets[i].normal.y = br.ReadSingle();
                    facets[i].normal.z = br.ReadSingle();

                    facets[i].a.x = br.ReadSingle();
                    facets[i].a.y = br.ReadSingle();
                    facets[i].a.z = br.ReadSingle();

                    facets[i].b.x = br.ReadSingle();
                    facets[i].b.y = br.ReadSingle();
                    facets[i].b.z = br.ReadSingle();

                    facets[i].c.x = br.ReadSingle();
                    facets[i].c.y = br.ReadSingle();
                    facets[i].c.z = br.ReadSingle();

                    // padding
                    br.ReadUInt16();
                }
            }
        }

        return CreateMeshWithFacets(facets);
    }

    private static Mesh[] CreateMeshWithFacets(IList<Facet> facets)
    {
        int fl = facets.Count, f = 0, mvc = MAX_FACETS_PER_MESH * 3;
        Mesh[] meshes = new Mesh[fl / MAX_FACETS_PER_MESH + 1];

        for(int i = 0; i < meshes.Length; i++)
        {
            int len = System.Math.Min(mvc, (fl - f) * 3);
            Vector3[] v = new Vector3[len];
            Vector3[] n = new Vector3[len];
            int[] t = new int[len];

            for(int it = 0; it < len; it += 3)
            {
                v[it  ] = facets[f].a;
                v[it+1] = facets[f].b;
                v[it+2] = facets[f].c;

                n[it  ] = facets[f].normal;
                n[it+1] = facets[f].normal;
                n[it+2] = facets[f].normal;

                t[it  ] = it;
                t[it+1] = it+1;
                t[it+2] = it+2;

                f++;
            }

            meshes[i] = new Mesh();
            meshes[i].vertices = v;
            meshes[i].normals = n;
            meshes[i].triangles = t;
        }

        return meshes;
    }
jtth
  • 876
  • 1
  • 12
  • 40
  • See [this](https://stackoverflow.com/a/46488319/3785314) post for how to do this with ThreadPool and the `UnityThread` class I wrote – Programmer Oct 11 '17 at 18:35
  • @Programmer "[One] can start a separate thread ... as long as their are no calls to any Unity functions executed in that thread", quoting 'lockstock' from your link. I imagine that the import code (see above) using Mesh constructors means it cannot be run in a new thread. – jtth Dec 15 '17 at 18:39
  • @lockstock Is this the case? – jtth Dec 15 '17 at 18:44
  • Yes, you can't use Unity's API in another Thread. There are few exceptions. One of them is the `Vector3` class. You can use it in another class. I don't think you can with the Mesh class but give it a try. If it shows an error then you can't. The part of your code that freezing is the part you are reading file with the FileStream. Do that in another thread. – Programmer Dec 15 '17 at 19:37
  • @Programmer Not entirely sure that's possible. Do you mean to separate the two `using` statements, `FileStream` and `BinaryReader` and use the BR apart from the FS? Fail to see how this would improve overall performance. The FS thread would need to finish entirely before sending its data to the BR no? Please clarify – jtth Dec 15 '17 at 19:54
  • You failed to reply to my comment on time and you have an accepted answer. My reply would have to be in a form of a code.Don't make that unction return a Mesh. Make it return void. Wrap the first `using` statement in a new Thread. When it is done, call the `CreateMeshWithFacets` in the main function with the help of the API I linked in my first comment. From there, you can make a function that returns Mesh[] as param. If you have a problem or still need help with this question, you will need to create new question with what you are doing and what problems you have and I will leave an answer. – Programmer Dec 15 '17 at 20:14

1 Answers1

5

You're trying to perform File IO synchronously, which puts a lock on whatever thread it's being run on. In this case, the main thread.

You can see the effects of this lock in all three areas of your clip:

  • Scene view doesn't update
  • Game view doesn't update
  • Console stops logging

This is because Update() is not running at all.

In order to fix this you need to load the file asynchronously.

  • Very well explained, thank you Draco. So is the fix here as easy as making these functions asynchronous ? – jtth Oct 11 '17 at 16:50
  • @jtth You'll have to do a little research on it. I haven't done any System.FileIO things asynchronously, only unity things (e.g. Resources). I did a cursory search, but it only turned up the things I already knew about. – Draco18s no longer trusts SE Oct 11 '17 at 16:51
  • Short of using the (extremely simple yet brilliantly outstanding) TPL Tasks, which I think are not available in Unity's .Net 3.5 runtime, you'll have to use a separate thread to do such heavy calculations. However, keep in mind that threads are not available in WSA apps. – Arshia001 Oct 12 '17 at 06:53