Dafydd Owen-Newns
Dafydd Owen-Newns

Reputation: 23

Adding GeometryModel3D to Model3DGroup

I am trying to create a wpf app that shows an animated 3D model. I have working code to create the model in BuildWave (I know this works as I have used it before). However, when I moved the code to a background worker in the class of the wpf window, I get the error "The calling thread cannot access this object because a different thread owns it."

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = BuildWave();
    }

    private void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
    {
        GeometryModel3D model = (GeometryModel3D)e.Result;
        mGeometry = model.Clone();    //mGeometry is a private member of the window
        if (group.Children.Contains(mGeometry))   //error is on this line
        {
            group.Children.Remove(mGeometry);    //group is a Model3DGroup added in xaml
        }
        group.Children.Add(mGeometry);
        System.Diagnostics.Debug.WriteLine("Added geometry to group");
    }

I have searched for solutions to this and found a post that covers a problem with the same error (The calling thread cannot access this object because a different thread owns it), which suggests using Dispatcher.Invoke(). However, when I try that:

    private void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
    {
        GeometryModel3D model = (GeometryModel3D)e.Result;
        mGeometry = model.Clone();

        group.Dispatcher.Invoke(() =>
        {
            if (group.Children.Contains(mGeometry))
            {
                group.Children.Remove(mGeometry);
            }
            group.Children.Add(mGeometry);    //error is now thrown on this line
        });

        System.Diagnostics.Debug.WriteLine("Added geometry to group");
    }

This throws the error "Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.", and again there is a post covering a similar problem (Cannot use a DependencyObject that belongs to a different thread than its parent Freezable) which suggests freezing the model which I have done in BuildWave:

        GeometryModel3D model = new GeometryModel3D(WaveMesh, new DiffuseMaterial(Brushes.YellowGreen));
        model.Transform = new Transform3DGroup();

        model.Freeze();
        return model;

What should I do to fix this?

thank you in advance.

Upvotes: 2

Views: 2155

Answers (2)

Sergey Orlov
Sergey Orlov

Reputation: 509

Call in UI thread something like this:

public static Model3DCollection GetModel3DCollectionByTemplates(List<GeometryTemplate> geometryTemplates) {
        var modelCollection = new Model3DCollection();
        foreach (var geometryTemplate in geometryTemplates) {
            var positions = geometryTemplate.Positions;
            var indicies = geometryTemplate.Indicies;
            var normals = geometryTemplate.Normals;
            var meshGeometry3D = new MeshGeometry3D {
                Positions = new Point3DCollection(positions),
                TriangleIndices = new Int32Collection(indicies),
                Normals = new Vector3DCollection(normals)
            };
            var geometry = new GeometryModel3D(meshGeometry3D,
                new DiffuseMaterial(new SolidColorBrush(Colors.Aquamarine)));
            modelCollection.Add(geometry);
        }
        // =======================
        return modelCollection;
    }

But within Task (TPL) returns List of GeometryTemplate. And simple Data wrapper class:

public class GeometryTemplate {
    public List<Point3D> Positions { get; private set; }
    public List<Int32> Indicies { get; private set; }
    public List<Vector3D> Normals { get; private set; }

    public GeometryTemplate(MeshGeometry3D meshGeometry3D) {
        SetPositions(meshGeometry3D.Positions);
        SetIndicies(meshGeometry3D.TriangleIndices);
        SetNormals(meshGeometry3D.Normals);
    }

    private void SetNormals(Vector3DCollection normals) {
        Normals = new List<Vector3D>(normals);
    }

    private void SetIndicies(Int32Collection triangleIndices) {
        Indicies = new List<Int32>(triangleIndices);
    }

    private void SetPositions(Point3DCollection positions) {
        Positions = new List<Point3D>(positions);
    }
}

And final point:

_model3DGroupContainer.Children = model3DCollection;

where _model3DGroupContainer is Model3DGroup

Upvotes: 0

Rekshino
Rekshino

Reputation: 7325

For it works with no problem, prepare in your bgworker all the data(lists of points, lists of a triangle vertices) for your geometry model and do not use the gui objects such as Point3DCollection or Int32Collection or geometries. Then, when you ready with it, create all your geometries etc. in your lambda method(you can gladly extract this creation to a method for more readability and call it in lambda), which you call on a dispatcher. This should work.

Upvotes: 1

Related Questions