Question

I have a WPF project (VS2010, .NET4.0) in which I create a rather big ModelVisual3D object (read from custom format STL file, process info, create mesh, etc.) This takes about 3-4 sec. to be created and another 2-3 sec. to do a mainViewport.Children.Add(ModelVisual3D). I do this all in a custom class and call this method:

 class My3DModel
{
...
        public MyModelVisual3D createModelVisual3D(MyTypes tType, int tNumber)
            {
                this.myModelVisual3D = new MyModelVisual3D(tType, tNumber);
                for (int i = 0, j = 0; i < this.Triangles.Length; i++)
                {
                    this.mesh.Positions.Add(this.Triangles[i].Vertex1);
                    this.mesh.Positions.Add(this.Triangles[i].Vertex2);
                    this.mesh.Positions.Add(this.Triangles[i].Vertex3);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.TriangleIndices.Add(j++);
                    this.mesh.TriangleIndices.Add(j++);
                    this.mesh.TriangleIndices.Add(j++);
                }
                this.model3DGroup.Children.Add(new GeometryModel3D(this.mesh, material));
                this.myModelVisual3D.Content = this.model3DGroup;
                return this.myModelVisual3D;
            }
}

The return value is also a custom class I created:

class ToothModelVisual3D : ModelVisual3D
{
    //VARIABLES
    private MyTypes myType;
    private int number;

    //OPERATORS
    public MyTypes MyType
    {get { return myType; } set { myType = value; }}

    public int Number
    {get { return number; } set { number = value;}}

    public ToothModelVisual3D() { }

    public ToothModelVisual3D(MyTypes tType, int tNumber) { MyType = tType; Number = tNumber; }
}

All I want to do is the following once in the beginning of the program:

{
        My3DModel myModel;
        myModel = new My3DModel();
        myModel.readFileBytes("C:\\registered\\" + 1 + ".stl");
        myModel.loadTriangles();
        mainViewport.Children.Add(myModel.createModelVisual3D(MyTypes.Sometype, 1);
}

If I do it on the main thread the UI hangs. If I do it on a worker thread and invoke mainViewport.Children.Add(...) it says it cannot access the resourses created on that worker thread. Help?!

From what I understand I've reached a point where I have two threads and resources belonging to each of them (mainViewport => UIThread & myModel => WorkerThread). Neither thread can access directly the other's resource but creating and using myModel on the UIThread makes it hang... All I want to do is have enough responsiveness from the UI, so the user may minimize the program while waiting for it to load the models, nothing more. How can I do that? Is there a way to do all the CPU heavy work on the UIThread, so no resource conflicts arise and have a worker thread that only handles UI for that time?

PS: I've tried with Thread, BackgroundWorker & Task<TResult> classes. Results were similar if not to say the same.

PPS: The full version will load massive models which will load more than 30-40 sec...

Was it helpful?

Solution 2

Sorry for the late answer, but I actually managed to workaround the problem long time ago the following way:

    delegate void myDelegate();

    private void fileOpenButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            Thread ViewportLoaderThread = new Thread(loadViewportItemsAsync);
            ViewportLoaderThread.IsBackground = true;
            ViewportLoaderThread.Start();
        }
        catch (Exception err) { UtilsProgram.writeErrorLog(err.ToString()); }
    }

    private void loadViewportItemsAsync()
    {
        try
        {
            //TRY to browse for a file
            if (!browseForFile()) return;

            Dispatcher.Invoke(new Action(() => { myStatusBar.Visibility = System.Windows.Visibility.Visible; menuItemHelpDemo.IsEnabled = false; }), null);

            //Load file, unpack, decrypt, load STLs and create ModelGroup3D objects
            UtilsDen.DenModel = new DenLoader(UtilsDen.Filename, UtilsDen.Certificate, UtilsDen.PrivateKey, this);

            //Load the models to viewport async
            myDelegate asyncDel = new myDelegate(sendModelsToViewportAsync);
            this.Dispatcher.BeginInvoke(asyncDel, null);
        }
        catch (Exception err) { MessageBox.Show(UtilsProgram.langDict["msg18"]); UtilsProgram.writeErrorLog(err.ToString()); }
    }

    private void sendModelsToViewportAsync()
    {
        for (int i = 0; i < UtilsDen.DenModel.StlFilesCount; i++)
        {
            //Add the models to MAIN VIEWPORT
            ModelVisual3D modelVisual = new ModelVisual3D();
            GeometryModel3D geometryModel = new GeometryModel3D();
            Model3DGroup modelGroup = new Model3DGroup();

            geometryModel = new GeometryModel3D(UtilsDen.DenModel.StlModels[i].MeshGeometry, UtilsDen.Material);

            modelGroup.Children.Add(geometryModel);
            modelVisual.Content = modelGroup;
            mainViewport.Children.Add(toothModelVisual);
        }
    }

The key was to use the this.Dispatcher.BeginInvoke(asyncDel, null); as it works on the main thread, but does not lag it, because it is executed asynchronously.

OTHER TIPS

I recently came across the same issue when porting an XNA application to WPF. In my case I partially resolved this by using a background thread to load the positions, normals, and indices from file. Then in that same thread, construct a memory stream containing XAML for the Model3DGroup with the GeometryModel3D and MeshGeometry3D.

Then, in the UI thread, once the memory stream is available, load the model...

Model3DGroup model = System.Windows.Markup.XamlReader.Load(memoryStream) as Model3DGroup;

There is still a delay, but as file access is done in a background thread, it is not as severe.

Using a delegate still appears to introduce a lag on the UI, a better solution is create the model in a worker thread and then freeze it. The model can then be cloned by the UI thread without the annoying exception. This works for me with models which take 25 seconds or more to load. The only issue I've found with this is that it doesn't work if the model contains a texture.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top