private void CreateEdgeLines(Color lineColor)
        {
            foreach (var wireframeVisual3D in RootModelVisual.Children.OfType <WireframeVisual3D>())
            {
                var edgeLinePositions = new Point3DCollection();
                EdgeLinesFactory.AddEdgeLinePositions(wireframeVisual3D.OriginalModel, edgeStartAngleInDegrees: 30, linePositions: edgeLinePositions);

                var multiLineVisual3D = new MultiLineVisual3D()
                {
                    Positions     = edgeLinePositions,
                    LineColor     = lineColor,
                    LineThickness = 2,
                    Transform     = wireframeVisual3D.Transform
                };

                EdgeLinesModelVisual.Children.Add(multiLineVisual3D);
            }
        }
        private void LoadModelWithEdgeLines(string fileName)
        {
            MainViewport.Children.Clear();

            // Create an instance of AssimpWpfImporter
            var assimpWpfImporter = new AssimpWpfImporter();
            var readModel3D       = assimpWpfImporter.ReadModel3D(fileName, texturesPath: null); // we can also define a textures path if the textures are located in some other directory (this is parameter can be skipped, but is defined here so you will know that you can use it)

            MainViewport.Children.Add(readModel3D.CreateModelVisual3D());


            // NOTES:
            // 1)
            // The source code for EdgeLinesFactory class is written below (in a comment at the end of this file).
            // You can change its source and create a new class that would suite your needs.
            //
            // 2)
            // With using AddEdgeLinePositions we will create STATIC lines from the current readModel3D.
            // If the readModel3D would be changed (any child transformation would be changed),
            // then the lines would not be correct any more.
            // See the DynamicEdgeLinesSample to see how to create dynamic edge lines.
            // If your object will not change, then it is better to create static edge lines for performance reasons
            // (having single MultiLineVisual3D for the whole instead of one MultiLineVisual3D for each GeometryModel3D).
            //
            // 3)
            // Multi-threading:
            // If you want to call AddEdgeLinePositions method on background threads, you will need to call Freeze on the readModel3D
            // (this will make the model and meshes immutable and this way they can be read on other threads).
            // You will also need to create Point3DCollection for each thread and after it is filled in AddEdgeLinePositions
            // call Freeze on it so it can be "send" to the main UI thread.
            // Another option (better) is to create a MeshAnalyzer class in each thread and for each MeshGeometry3D call CreateEdgeLines
            // (see source of the EdgeLinesFactory.GenerateEdgeLineIndices method).
            //
            // 4)
            // You can use wpf3d file format to save 3D models with embedded EdgeLineIndices data (and also with PolygonIndices data).
            // The source code for wpf3d file format is available in this project under Wpf3DFile folder (see sample in this folder for more info).
            // This way you can calculate model edges in a separate application and then save that into the wpf3d file
            // so that the application that requires models with EdgeLineIndices data will not need to calculate that.

            var edgeLinePositions = new Point3DCollection();

            EdgeLinesFactory.AddEdgeLinePositions(readModel3D, EdgeStartAngleSlider.Value, edgeLinePositions);

            _multiLineVisual3D = new MultiLineVisual3D()
            {
                Positions     = edgeLinePositions,
                LineColor     = Colors.Black,
                LineThickness = LineThicknessSlider.Value,
            };

            MainViewport.Children.Add(_multiLineVisual3D);


            if (_fileName != fileName) // Reset camera only when the file is loaded for the first time
            {
                _fileName = fileName;

                Camera1.TargetPosition = readModel3D.Bounds.GetCenterPosition();
                Camera1.Distance       = readModel3D.Bounds.GetDiagonalLength() * 1.2;
            }

            // Add ambient light
            var ambientLight = new AmbientLight(Color.FromRgb(100, 100, 100));

            MainViewport.Children.Add(ambientLight.CreateModelVisual3D());
        }
Exemple #3
0
        private void LoadRobotArm()
        {
            string fileName = AppDomain.CurrentDomain.BaseDirectory + @"Resources\robotarm.3ds";

            // Create an instance of AssimpWpfImporter
            var assimpWpfImporter = new AssimpWpfImporter();

            _robotArmModel3D = assimpWpfImporter.ReadModel3D(fileName, texturesPath: null);

            // In VS Immediate window call "readModel3D.DumpHierarchy()" to get hierarchy and names of the objects
            var baseModel3D   = assimpWpfImporter.NamedObjects["Base__Group"] as Model3D;
            var joint2Model3D = assimpWpfImporter.NamedObjects["Joint2__Group"] as Model3D;

            _baseAxisAngleRotation3D   = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);
            _joint2AxisAngleRotation3D = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);

            // Add RotateTransform3D to existing transformations
            var transform3DGroup = new Transform3DGroup();

            transform3DGroup.Children.Add(new RotateTransform3D(_baseAxisAngleRotation3D));
            transform3DGroup.Children.Add(baseModel3D.Transform);

            baseModel3D.Transform = transform3DGroup;

            transform3DGroup = new Transform3DGroup();
            transform3DGroup.Children.Add(new RotateTransform3D(_joint2AxisAngleRotation3D));
            transform3DGroup.Children.Add(joint2Model3D.Transform);

            joint2Model3D.Transform = transform3DGroup;

            ModelRootVisual3D.Children.Add(_robotArmModel3D.CreateModelVisual3D());


            // NOTES:
            // 1)
            // The source code for EdgeLinesFactory class is written in a comment at the end of the StaticEdgeLinesCreationSample.xaml.cs file.
            // You can change its source and create a new class that would suite your needs.
            //
            // 2)
            // Call CreateEdgeLinesForEachGeometryModel3D that will generate MultiLineVisual3D
            // with edge lines for each GeometryModel3D in the _robotArmModel3D hierarchy.
            // The MultiLineVisual3D are added to to the EdgeLinesRootVisual3D.
            // The create MultiLineVisual3D will be set as EdgeLinesFactory.EdgeMultiLineVisual3DProperty to each GeometryModel3D.
            // This way we will be able to update the Transformation in the MultiLineVisual3D after the transformation in the model is changed.
            // This is done in the OnRendering method.
            //
            // If the model is not animated or otherwise transformed, then it is recommended to call AddEdgeLinePositions
            // that will create static edge lines. See StaticEdgeLinesCreationSample for more info.
            //
            // 3)
            // Multi-threading:
            // You cannot call CreateEdgeLinesForEachGeometryModel3D in background treads because MultiLineVisual3D object cannot be used on other threads.
            // But you can move most of the work (generating edge line positions) to the background threads.
            // To do that you will need to manually collect all MeshGeometry3D objects, call Freeze method on them and then in multiple threads
            // create MeshAnalyzer classes and call CreateEdgeLines for each MeshGeometry3D.
            // Because you will not be able to store the generated lines into MeshGeometry3D (it is frozen), you will need to crate a Dictionary
            // where key will be MeshGeometry3D and value will be the List<int> that is created by the CreateEdgeLines method.
            // After all the threads have completed its work, then in the main thread you can create MultiLineVisual3D objects
            // based on the data from the Dictionary that was created in each thread.
            EdgeLinesFactory.CreateEdgeLinesForEachGeometryModel3D(_robotArmModel3D, edgeStartAngleInDegrees: 25, lineThickness: 2, lineColor: Colors.Black, parentModelVisual3D: EdgeLinesRootVisual3D);

            SetupAnimation();
        }
Exemple #4
0
        private void LoadModelWithEdgeLines(string fileName)
        {
            _mainViewport3D.Children.Clear();

            // Create an instance of AssimpWpfImporter
            var assimpWpfImporter = new AssimpWpfImporter();
            var readModel3D       = assimpWpfImporter.ReadModel3D(fileName, texturesPath: null); // we can also define a textures path if the textures are located in some other directory (this is parameter can be skipped, but is defined here so you will know that you can use it)


            if (readModel3D == null)
            {
                MessageBox.Show("Cannot read file");
                return;
            }

            _mainViewport3D.Children.Add(readModel3D.CreateModelVisual3D());


            // NOTE:
            // EdgeLinesFactory from Ab3d.PowerToys library will be used to create 3D lines that define the edges of the 3D models.
            // The edges are created when the angle between two triangles is bigger then the specified edgeStartAngleInDegrees (set by EdgeStartAngleSlider).
            //
            // See Lines3D\StaticEdgeLinesCreationSample and Lines3D\DynamicEdgeLinesSample samples for more info.
            //
            //
            // ADDITIONAL NOTE:
            // It is possible to store edge lines into wpf3d file format (this way it is not needed to generate the edge lines after the file is read).
            // The source code to read and write wpf3d file is available with Ab3d.PowerToys library.



            // With using AddEdgeLinePositions we will create STATIC lines from the current readModel3D.
            // If the readModel3D would be changed (any child transformation would be changed),
            // then the lines would not be correct any more.
            // See the DynamicEdgeLinesSample to see how to create dynamic edge lines.
            // If your object will not change, then it is better to create static edge lines for performance reasons
            // (having single MultiLineVisual3D for the whole instead of one MultiLineVisual3D for each GeometryModel3D).

            var edgeLinePositions = new Point3DCollection();

            EdgeLinesFactory.AddEdgeLinePositions(readModel3D, EdgeStartAngleSlider.Value, edgeLinePositions);


            _edgeLinesVisual3D = new MultiLineVisual3D()
            {
                Positions     = edgeLinePositions,
                LineColor     = Colors.Black,
                LineThickness = LineThicknessSlider.Value,
                IsVisible     = ShowEdgeLinesCheckBox.IsChecked ?? false
            };

            if (LineDepthBiasCheckBox.IsChecked ?? false)
            {
                // Use line depth bias to move the line closer to the camera so the line is rendered on top of solid model and is not partially occluded by it.
                double depthBias = (double)DepthBiasComboBox.SelectedItem;
                _edgeLinesVisual3D.SetDXAttribute(DXAttributeType.LineDepthBias, depthBias);
            }

            _mainViewport3D.Children.Add(_edgeLinesVisual3D);


            if (_fileName != fileName) // Reset camera only when the file is loaded for the first time
            {
                _fileName = fileName;

                // Set both Distance and CameraWidth (this way we can change the CameraType with RadioButtons)
                // Distance is used when CameraType is PerspectiveCamera.
                // CameraWidth is used when CameraType is OrthographicCamera.
                Camera1.Distance    = readModel3D.Bounds.GetDiagonalLength() * 1.3;
                Camera1.CameraWidth = readModel3D.Bounds.SizeX * 2;

                Camera1.TargetPosition = readModel3D.Bounds.GetCenterPosition() + new Vector3D(0, readModel3D.Bounds.SizeY * 0.15, 0); // slightly move the object down so that the object is not shown over the title

                // Find best EdgeLine DepthBias
                double idealDepthBias = Camera1.Distance / 3000;

                double bestDistance = double.MaxValue;
                int    bestIndex    = -1;
                for (int i = 0; i < PossibleEdgeLineDepthBiases.Length; i++)
                {
                    double distance = Math.Abs(idealDepthBias - PossibleEdgeLineDepthBiases[i]);
                    if (distance < bestDistance)
                    {
                        bestDistance = distance;
                        bestIndex    = i;
                    }
                }

                DepthBiasComboBox.SelectedIndex = bestIndex;
            }

            // Add ambient light
            var ambientLight = new AmbientLight(Color.FromRgb(100, 100, 100));

            _mainViewport3D.Children.Add(ambientLight.CreateModelVisual3D());
        }