Beispiel #1
0
        private void AddHouseWithTreesModel()
        {
            // Use assimp importer to load house with trees.3DS
            AssimpLoader.LoadAssimpNativeLibrary();

            var assimpWpfImporter   = new AssimpWpfImporter();
            var houseWithTreesModel = (Model3DGroup)assimpWpfImporter.ReadModel3D(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Models\house with trees.3DS"));

            // Add AmbientLight
            var ambientLight = new AmbientLight(Color.FromRgb(80, 80, 80));

            houseWithTreesModel.Children.Add(ambientLight);

            // Show the loaded 3d scene
            var modelVisual3D = houseWithTreesModel.CreateModelVisual3D();

            _masterDXViewportView.Viewport3D.Children.Add(modelVisual3D);

            // Save the man01 model for animation (when clicked on "Change scene" button)
            _animatedPersonModel = assimpWpfImporter.NamedObjects["man01"] as Model3DGroup;


            // Add base green plate and the house models to a CustomRenderingQueue.
            // This is done with setting value of CustomRenderingQueue (custom DXAttribute) on some parts of the 3D scene.
            // This will add the specified models to the custom rendering queue (created in the OnMasterDXSceneCreated method).
            var modelNames = new string[] { "Box01", "Box02", "roof01" };

            foreach (var modelName in modelNames)
            {
                var model3D = assimpWpfImporter.NamedObjects[modelName] as Model3D;

                // Note that CustomRenderingQueue can be set to an instance of RenderingQueue or to a name (as string) of the RenderingQueue
                model3D.SetDXAttribute(DXAttributeType.CustomRenderingQueue, "CustomRenderingQueue");
            }
        }
        private void LoadModel(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)


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

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


            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());
        }
Beispiel #3
0
        private Model3D LoadDuckModel()
        {
            string fileName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Models\duck.dae");

            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)

            // Move the model so that it has its bottom center position at (0,0,0) and
            // scale it so that its height is set to DuckSize (0.3m) - multiply xSize and zSize by 10 so that only y size will limit the final size (we preserve the aspect ratio)
            Ab3d.Utilities.ModelUtils.PositionAndScaleModel3D(readModel3D, new Point3D(0, 0, 0), PositionTypes.Bottom, new Size3D(DuckSize * 10, DuckSize, DuckSize * 10), preserveAspectRatio: true, preserveCurrentTransformation: false);

            return(readModel3D);
        }
        private Model3D LoadModel3D()
        {
            AssimpLoader.LoadAssimpNativeLibrary();

            string fileName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Models\robotarm-upper-part.3ds");

            var assimpWpfImporter = new AssimpWpfImporter();
            var robotModel3D      = assimpWpfImporter.ReadModel3D(fileName);

            // To get object names execute the following in Visual Studio Immediate window:
            // robotModel3D.DumpHierarchy();

            var hand2Group = assimpWpfImporter.NamedObjects["Hand2__Group"] as Model3DGroup;

            _originalModelAxisAngleRotation3D = new AxisAngleRotation3D(new Vector3D(0, 0, -1), 0);
            var rotateTransform3D = new RotateTransform3D(_originalModelAxisAngleRotation3D);

            Ab3d.Utilities.TransformationsHelper.AddTransformation(hand2Group, rotateTransform3D);


            // By default all objects cast shadow, so to prevent that we need to set the IsCastingShadow to false.
            // This can be done in two ways:
            //
            // 1) Set DXAttributeType.IsCastingShadow on GeometryModel3D
            //    (only GeometryModel3D support that attribute;
            //     this need to be set before the WpfGeometryModel3DNode object is created from GeometryModel3D)
            //
            // 2) After the WpfGeometryModel3DNode is created from GeometryModel3D,
            // then we can set the IsCastingShadow on that SceneNode object - see commented code below.
            //
            // When we change the value of IsCastingShadow, we also need to enable checking this property with
            // setting the PlanarShadowRenderingProvider.IsCheckingIsCastingShadow property to true.
            // By default this property is set to false so improve performance
            // (prevent using FilterObjectsFunction in RenderObjectsRenderingStep that render shadow).

            _teapotGeometryModel3D = assimpWpfImporter.NamedObjects["Teapot"] as GeometryModel3D;
            _teapotGeometryModel3D.SetDXAttribute(DXAttributeType.IsCastingShadow, false);

            // If we want to change the value of IsCastingShadow when the object is already shown,
            // we need to change that in the WpfGeometryModel3DNode - for example:
            //var teapotSceneNode = MainDXViewportView.GetSceneNodeForWpfObject(_teapotGeometryModel3D) as WpfGeometryModel3DNode; // we can get the teapotSceneNode in MainDXViewportView.DXSceneInitialized
            //if (teapotSceneNode != null)
            //    teapotSceneNode.IsCastingShadow = false;

            return(robotModel3D);
        }
        private void LoadExportedScene(string fileName)
        {
            // Now read the exported file and show in the right Viewport3D

            Model3D readModel3D;

            try
            {
                // With uncommenting the following few lines we would use Ab3d.ReaderObj from Ab3d.PowerToys to read obj files instead of Assimp
                //if (fileName.EndsWith(".obj", ignoreCase: true, culture: CultureInfo.InvariantCulture))
                //{
                //    var readerObj = new Ab3d.ReaderObj();
                //    readModel3D = readerObj.ReadModel3D(fileName);
                //}
                //else
                //{
                var assimpWpfImporter = new AssimpWpfImporter();
                readModel3D = assimpWpfImporter.ReadModel3D(fileName);
                //}
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error reading file:\r\n" + ex.Message);
                return;
            }


            var modelVisual3D = new ModelVisual3D();

            modelVisual3D.Content = readModel3D;

            MainViewport2.Children.Clear();
            MainViewport2.Children.Add(modelVisual3D);

            // Set Camera2 from Camera1
            Camera2.TargetPosition = Camera1.TargetPosition;
            Camera2.Heading        = Camera1.Heading;
            Camera2.Attitude       = Camera1.Attitude;
            Camera2.Distance       = Camera1.Distance;

            Camera2.Refresh(); // This will regenerate light that was cleared with MainViewport2.Children.Clear()

            ExportedSceneTitleTextBlock.Text = "Scene imported from " + fileName;
        }
        private void LoadFileWithSkinnedAnimation(string fileName)
        {
            InfoTextBox.Text = "";

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


            Model3D readModel3D;

            try
            {
                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)
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Error reading file:\r\n{0}\r\n\r\n{1}", fileName, ex.Message));
                return;
            }

            _lastLoadedModel3D = readModel3D;

            ModelSilhueteVisual3D.Children.Clear();
            ModelSilhueteVisual3D.Children.Add(readModel3D.CreateModelVisual3D());

            // Set camera to show the whole model
            Camera1.TargetPosition = readModel3D.Bounds.GetCenterPosition();
            Camera1.Distance       = readModel3D.Bounds.GetDiagonalLength() * 1.2;


            BoneMarkersVisual3D.Children.Clear();

            // Stop current animation if it was running
            if (_assimpAnimationController != null)
            {
                _assimpAnimationController.StopAnimation();
                _assimpAnimationController = null;
            }


            _assimpScene = assimpWpfImporter.ImportedAssimpScene;

            if (_assimpScene.AnimationCount == 0)
            {
                // No animation in the file
                AnimationSelectionComboBox.IsEnabled     = false;
                AnimationSelectionComboBox.ItemsSource   = new string[] { "(no animation defined)" };
                AnimationSelectionComboBox.SelectedIndex = 0;

                ShowBonesCheckBox.IsEnabled = false;
                ShowBonesCheckBox.IsChecked = false;

                AnimationSlider.IsEnabled = false;

                StartStopAnimationButton.IsEnabled = false;

                UpdateAnimationUI(0);

                return;
            }

            AnimationSlider.IsEnabled          = true;
            StartStopAnimationButton.IsEnabled = true;


            try
            {
                // Create AssimpAnimationController - it will play the keyframe and skeletal animation
                _assimpAnimationController             = new AssimpAnimationController(assimpWpfImporter);
                _assimpAnimationController.AutoReverse = false;
                _assimpAnimationController.AutoRepeat  = true;

                _assimpAnimationController.AfterFrameUpdated += OnAssimpAnimationControllerOnAfterFrameUpdated;


                SetupAnimationUI();
                UpdateBoneMarkersAndUI();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error starting animation:\r\n" + ex.Message);
                return;
            }

            // Setup animation names
            var animationNames = _assimpScene.Animations.Select(a => a.Name).ToList();

            AnimationSelectionComboBox.IsEnabled   = true;
            AnimationSelectionComboBox.ItemsSource = animationNames;

            int startupIndex = animationNames.IndexOf("Run"); // Start with "Run" animation if it exists

            if (startupIndex < 0)
            {
                startupIndex = 0;
            }

            AnimationSelectionComboBox.SelectedIndex = startupIndex; // This will call ChangeAnimationName method


            if (_assimpAnimationController.HasSkeletalAnimation)
            {
                ShowBonesCheckBox.IsEnabled = true;
                ShowBonesCheckBox.ToolTip   = null;

                // In case ShowBonesCheckBox is checked then set model opacity to 0.8
                UpdateModelOpacity();
            }
            else
            {
                // No skeletal animation (probably only keyframe animation)
                ShowBonesCheckBox.IsEnabled = false;
                ShowBonesCheckBox.IsChecked = false;

                ShowBonesCheckBox.ToolTip = "This files does not define any skeletal animation.";
            }

            //StartAnimation();
        }
        private void LoadModel(string fileName)
        {
            bool isNewFile = false;

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

            string fileExtension = System.IO.Path.GetExtension(fileName);

            if (!assimpWpfImporter.IsImportFormatSupported(fileExtension))
            {
                MessageBox.Show("Assimp does not support importing files file extension: " + fileExtension);
                return;
            }

            try
            {
                Mouse.OverrideCursor = Cursors.Wait;

                // Before readin the file we can set the default material (used when no other materila is defined - here we set the default value again)
                assimpWpfImporter.DefaultMaterial = new DiffuseMaterial(Brushes.Silver);

                // After assimp importer reads the file, it can execute many post processing steps to transform the geometry.
                // See the possible enum values to see what post processes are available.
                // Here we just set the AssimpPostProcessSteps to its default value - execute the Triangulate step to convert all polygons to triangles that are needed for WPF 3D.
                // Note that if ReadPolygonIndices is set to true in the next line, then the assimpWpfImporter will not use assimp's triangulation because it needs original polygon data.
                assimpWpfImporter.AssimpPostProcessSteps = PostProcessSteps.Triangulate;

                // When ReadPolygonIndices is true, assimpWpfImporter will read PolygonIndices collection that can be used to show polygons instead of triangles.
                assimpWpfImporter.ReadPolygonIndices = ReadPolygonIndicesCheckBox.IsChecked ?? false;

                // Read model from file
                Model3D readModel3D;

                try
                {
                    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)

                    // To read 3D model from stream, use the following code:
                    //var extension = System.IO.Path.GetExtension(fileName); // extension is needed as a format hint so assimp will know which importer to use
                    //using (var fileStream = System.IO.File.OpenRead(fileName))
                    //{
                    //    readModel3D = assimpWpfImporter.ReadModel3D(fileStream, extension, resolveResourceFunc: null); // when reading models with texture, you will need to define the resolveResourceFunc
                    //}

                    isNewFile = (_fileName != fileName);
                    _fileName = fileName;
                }
                catch (Exception ex)
                {
                    readModel3D = null;
                    MessageBox.Show("Error importing file:\r\n" + ex.Message);
                }

                // After the model is read and if the object names are defined in the file,
                // you can get the model names by assimpWpfImporter.ObjectNames
                // or get object by name with assimpWpfImporter.NamedObjects

                // To get the  object model of the assimp importer, you can observe the assimpWpfImporter.ImportedAssimpScene

                // Show the model
                ShowModel(readModel3D, updateCamera: isNewFile); // If we just reloaded the previous file, we preserve the current camera TargetPosition and Distance
            }
            finally
            {
                // Dispose unmanaged resources
                assimpWpfImporter.Dispose();

                Mouse.OverrideCursor = null;
            }
        }
Beispiel #8
0
        public void LoadModel(string fileName)
        {
            bool isNewFile = false;

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

            string fileExtension = System.IO.Path.GetExtension(fileName);

            if (!assimpWpfImporter.IsImportFormatSupported(fileExtension))
            {
                MessageBox.Show("Assimp does not support importing files file extension: " + fileExtension);
                return;
            }

            try
            {
                Mouse.OverrideCursor = Cursors.Wait;

                // Before readin the file we can set the default material (used when no other materila is defined - here we set the default value again)
                assimpWpfImporter.DefaultMaterial = new DiffuseMaterial(Brushes.Silver);

                // After assimp importer reads the file, it can execute many post processing steps to transform the geometry.
                // See the possible enum values to see what post processes are available.
                // Here we just set the AssimpPostProcessSteps to its default value - execute the Triangulate step to convert all polygons to triangles that are needed for WPF 3D.
                // Note that if ReadPolygonIndices is set to true in the next line, then the assimpWpfImporter will not use assimp's triangulation because it needs original polygon data.
                assimpWpfImporter.AssimpPostProcessSteps = PostProcessSteps.Triangulate;

                // When ReadPolygonIndices is true, assimpWpfImporter will read PolygonIndices collection that can be used to show polygons instead of triangles.
                assimpWpfImporter.ReadPolygonIndices = ReadPolygonIndicesCheckBox.IsChecked ?? false;

                // Read model from file
                Model3D readModel3D;

                try
                {
                    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)

                    isNewFile = (_fileName != fileName);
                    _fileName = fileName;
                }
                catch (Exception ex)
                {
                    readModel3D = null;
                    MessageBox.Show("Error importing file:\r\n" + ex.Message);
                }

                // After the model is read and if the object names are defined in the file,
                // you can get the model names by assimpWpfImporter.ObjectNames
                // or get object by name with assimpWpfImporter.NamedObjects

                // To get the  object model of the assimp importer, you can observe the assimpWpfImporter.ImportedAssimpScene

                // Show the model
                if (readModel3D != null)
                {
                    ShowModel(readModel3D, updateCamera: isNewFile); // If we just reloaded the previous file, we preserve the current camera TargetPosition and Distance
                }

                // Force garbage collection to clear the previously loaded objects from memory.
                // Note that sometimes when a lot of objects are created in large objects heap,
                // it may take two garbage collections to release the memory
                // (e.g. - after reading one large file, you will need to read two smaller files to clean the memory taken by the large file).
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
            }
            finally
            {
                // Dispose unmanaged resources
                assimpWpfImporter.Dispose();

                Mouse.OverrideCursor = null;
            }
        }
        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());
        }
Beispiel #10
0
        public BasicWpf3dObjectsTutorial()
        {
            InitializeComponent();

            var mesh2 = new MeshGeometry3D()
            {
                Positions = new Point3DCollection(new[]
                {
                    new Point3D(5, 0, 5),
                    new Point3D(100, 0, 5),
                    new Point3D(100, 0, 50),
                    new Point3D(5, 0, 50)
                }),

                TriangleIndices = new Int32Collection(new[]
                {
                    0, 2, 1,
                    3, 2, 0
                })
            };

            var geometryModel2 = new GeometryModel3D()
            {
                Geometry     = mesh2,
                Material     = new DiffuseMaterial(Brushes.LightGreen),
                BackMaterial = new DiffuseMaterial(Brushes.Red),
            };

            var modelVisual2 = new ModelVisual3D()
            {
                Content = geometryModel2
            };

            //// Using CreateModelVisual3D extensiton method from Ab3d.PowerToys
            //var modelVisual2 = geometryModel2.CreateModelVisual3D();
            //Viewport2.Children.Add(modelVisual2);

            //// in one line:
            //Viewport2.Children.Add(geometryModel2.CreateModelVisual3D());

            Viewport2.Children.Add(modelVisual2);

            MeshInspector2.MeshGeometry3D = mesh2;

            // #############

            var mesh3 = new MeshGeometry3D()
            {
                Positions       = mesh2.Positions,
                TriangleIndices = new Int32Collection(new[]
                {
                    2, 0, 1, // changed from 0, 2, 1
                    3, 2, 0
                }),
                Normals = new Vector3DCollection(new [] { new Vector3D(0, 1, 0), new Vector3D(0, 1, 0), new Vector3D(0, 1, 0), new Vector3D(0, 1, 0) }) // We define Normals because the automatically generated normals are not correct because the two triangles are oriented differently and this produces zero length normals for the shared positions; note that for mesh2 this was not needed because triangles are correctly oriented and WPF was able to correctly calculate normals.
            };

            _geometryModel3 = new GeometryModel3D()
            {
                Geometry     = mesh3,
                Material     = new DiffuseMaterial(Brushes.LightGreen),
                BackMaterial = new DiffuseMaterial(Brushes.Red),
            };

            var modelVisual3 = new ModelVisual3D()
            {
                Content = _geometryModel3
            };

            Viewport3.Children.Add(modelVisual3);

            MeshInspector3.MeshGeometry3D = mesh3;

            // #############

            var modelVisual4 = new ModelVisual3D()
            {
                Content = _geometryModel3
            };

            Viewport4.Children.Add(modelVisual4);

            MeshInspector4.MeshGeometry3D = mesh3;

            // #############

            var mesh5 = new MeshGeometry3D()
            {
                Positions          = mesh2.Positions,
                TriangleIndices    = mesh2.TriangleIndices,
                TextureCoordinates = new PointCollection(new[]
                {
                    new Point(0, 0),
                    new Point(1, 0),
                    new Point(1, 1),
                    new Point(0, 1),
                })
            };

            var textureImage = new BitmapImage(new Uri("pack://*****:*****@"c:\images\texture.png")); // Read image from file
            //var textureImage2 = new BitmapImage(new Uri("pack://*****:*****@"Resources\robotarm-upper-part.3ds");

            var assimpWpfImporter = new AssimpWpfImporter();
            var robotModel3D      = assimpWpfImporter.ReadModel3D(fileName);

            string dumpString = Ab3d.Utilities.Dumper.GetObjectHierarchyString(robotModel3D);

            RobotArmSampleTextBox.Text = dumpString;


            //var transform3DGroup = new Transform3DGroup();
            //transform3DGroup.Children.Add(new ScaleTransform3D(2, 3, 2));
            //transform3DGroup.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 45)));
            //transform3DGroup.Children.Add(new TranslateTransform3D(100, 0, 0));

            //geometryModel2.Transform = transform3DGroup;
        }
Beispiel #11
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();
        }
Beispiel #12
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());
        }