예제 #1
0
        private void LoadFile(string fileName, string customTexturesFolder)
        {
            if (!System.IO.File.Exists(fileName))
            {
                MessageBox.Show($"File does not exist:\r\n{fileName}");
                return;
            }

            Mouse.OverrideCursor = Cursors.Wait;

            ModelPlaceholder.Content = null;
            ModelPlaceholder.Children.Clear();
            //LightsModelPlaceholder.Children.Clear();

            InfoTextBlock.Text = "";
            Log("Start opening file: " + fileName);


            try
            {
                //// ReaderObj that comes with Ab3d.PowerToys cannot read normal map textures
                //var readerObj = new Ab3d.ReaderObj();
                //readerObj.BitmapCacheOption = BitmapCacheOption.None; // Do not load textures by ReaderObj
                //Model3D readModel3D = readerObj.ReadModel3D(fileName);

                //UpdateMaterials(readModel3D);


                // Therefore we need to use Assimp importer
                var assimpWpfImporter = new AssimpWpfImporter();

                // Let assimp calculate the tangents that are needed for normal mapping
                assimpWpfImporter.AssimpPostProcessSteps |= PostProcessSteps.CalculateTangentSpace;

                //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)

                var assimpScene = assimpWpfImporter.ReadFileToAssimpScene(fileName);

                if (assimpScene == null)
                {
                    Log("Cannot load 3D model probably because file format is not recognized!");
                    return;
                }

                var assimpWpfConverter = new AssimpWpfConverter();
                var readModel3D        = assimpWpfConverter.ConvertAssimpModel(assimpScene, System.IO.Path.GetDirectoryName(fileName));


                _loadedFileName = fileName;

                // Convert standard WPF materials into PhysicallyBasedMaterial objects
                UpdateMaterials(fileName, customTexturesFolder, assimpScene, assimpWpfConverter, useStrictFileNameMatch: true, supportDDSTextures: true);


                ModelPlaceholder.Content = readModel3D;

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

                Log(string.Format("\r\nFile '{0}' loaded\r\nCenter position: {1:0};    Size: {2:0}",
                                  System.IO.Path.GetFileName(fileName),
                                  readModel3D.Bounds.GetCenterPosition(),
                                  readModel3D.Bounds.Size));


                Camera1.ShowCameraLight = ShowCameraLightType.Always;
                Camera1.Refresh();

                // Manually call Refresh to update the scene while we are still showing Wait cursor
                MainDXViewportView.Refresh();

                this.Title = "Ab3d.DXEngine PBR Rendering - " + System.IO.Path.GetFileName(fileName);
            }
            finally
            {
                Mouse.OverrideCursor = null;
            }
        }
예제 #2
0
        private void CreateRootModel()
        {
            switch (ObjectComboBox.SelectedIndex)
            {
            case 0:
                Ab3d.Meshes.BoxMesh3D box = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(130, 60, 100), 1, 1, 1);
                _rootMesh = box.Geometry;
                break;

            case 1:
                Ab3d.Meshes.BoxMesh3D box2 = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(130, 60, 100), 4, 4, 4);
                _rootMesh = box2.Geometry;
                break;

            case 2:
                Ab3d.Meshes.SphereMesh3D sphere = new Ab3d.Meshes.SphereMesh3D(new Point3D(0, 0, 0), 80, 10);
                _rootMesh = sphere.Geometry;
                break;

            case 3:
                Ab3d.Meshes.SphereMesh3D sphere2 = new Ab3d.Meshes.SphereMesh3D(new Point3D(0, 0, 0), 80, 5);
                _rootMesh = sphere2.Geometry;
                break;

            case 4:
                Ab3d.Meshes.CylinderMesh3D cylinder = new Ab3d.Meshes.CylinderMesh3D(new Point3D(0, -50, 0), 60, 100, 12, true);
                _rootMesh = cylinder.Geometry;
                break;

            case 5:
                Ab3d.Meshes.ConeMesh3D cone = new Ab3d.Meshes.ConeMesh3D(new Point3D(0, -50, 0), 30, 60, 100, 12, true);
                _rootMesh = cone.Geometry;
                break;

            case 6:
                Ab3d.Meshes.ConeMesh3D cone2 = new Ab3d.Meshes.ConeMesh3D(new Point3D(0, -50, 0), 30, 60, 100, 6, false);
                _rootMesh = cone2.Geometry;
                break;

            case 7:
                var readerObj   = new Ab3d.ReaderObj();
                var teapotModel = readerObj.ReadModel3D(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\ObjFiles\Teapot.obj")) as GeometryModel3D;

                if (teapotModel == null)
                {
                    return;
                }

                // Get the teapot MeshGeometry3D
                _rootMesh = (MeshGeometry3D)teapotModel.Geometry;

                break;

            default:
                _rootMesh = null;
                break;
            }


            var geometryModel3D = new GeometryModel3D(_rootMesh, new DiffuseMaterial(Brushes.Silver));

            MainViewport.Children.Clear();
            MainViewport.Children.Add(geometryModel3D.CreateModelVisual3D());

            MeshInspector.MeshGeometry3D = _rootMesh;

            Camera1.Refresh(); // Recreate the camera's light
        }
        private void CreateSceneObjects()
        {
            // NOTE:
            // It is recommended to use meter as basic unit for all 3D objects used by VR.

            var rootVisual3D = new ModelVisual3D();

            var wireGridVisual3D = new WireGridVisual3D()
            {
                CenterPosition   = new Point3D(0, 0, 0),
                Size             = new Size(3, 3), // 3 x 3 meters
                WidthCellsCount  = 10,
                HeightCellsCount = 10,
                LineColor        = Colors.Gray,
                LineThickness    = 2
            };

            rootVisual3D.Children.Add(wireGridVisual3D);


            double centerX      = 0;
            double centerZ      = 0;
            double circleRadius = 1;

            var boxMaterial    = new DiffuseMaterial(Brushes.Gray);
            var sphereMaterial = new MaterialGroup();

            sphereMaterial.Children.Add(new DiffuseMaterial(Brushes.Silver));
            sphereMaterial.Children.Add(new SpecularMaterial(Brushes.White, 16));

            for (int a = 0; a < 360; a += 36)
            {
                double rad = SharpDX.MathUtil.DegreesToRadians(a);
                double x   = Math.Sin(rad) * circleRadius + centerX;
                double z   = Math.Cos(rad) * circleRadius + centerZ;

                var boxVisual3D = new BoxVisual3D()
                {
                    CenterPosition = new Point3D(x, 0.3, z),
                    Size           = new Size3D(0.2, 0.6, 0.2),
                    Material       = boxMaterial
                };

                var sphereVisual3D = new SphereVisual3D()
                {
                    CenterPosition = new Point3D(x, 0.75, z),
                    Radius         = 0.15,
                    Material       = sphereMaterial
                };

                rootVisual3D.Children.Add(boxVisual3D);
                rootVisual3D.Children.Add(sphereVisual3D);
            }

            MainViewport.Children.Clear();
            MainViewport.Children.Add(rootVisual3D);

            Camera1.Heading  = 25;
            Camera1.Attitude = -20;
            Camera1.Distance = 4; // 3 meters

            Camera1.Refresh();
        }
        private void UpdateClosestLine()
        {
            if (_lineSelectorData == null || _lineSelectorData.Count == 0)
            {
                return;
            }

            if (_isCameraChanged)
            {
                // Each time camera is changed, we need to call CalculateScreenSpacePositions method.
                // This will update the 2D screen positions of the 3D lines.

                // IMPORTANT:
                // Before calling CalculateScreenSpacePositions it is highly recommended to call Refresh method on the camera.
                Camera1.Refresh();

                if (MultiThreadedCheckBox.IsChecked ?? false)
                {
                    // This code demonstrates how to use call CalculateScreenSpacePositions from multiple threads.
                    // This significantly improves performance when many 3D lines are used (thousands)
                    // but is not needed when using only a few lines (as in this demo).
                    //
                    // When calling CalculateScreenSpacePositions we need to prepare all the data
                    // from WPF properties before calling the method because those properties
                    // are not accessible from the other thread.
                    // We need worldToViewportMatrix and
                    // the _lineSelectorData[i].Camera and _lineSelectorData[i].UsedLineThickness need to be set
                    // (in this sample they are set in the LineSelectorData constructor).

                    var  worldToViewportMatrix        = new Matrix3D();
                    bool isWorldToViewportMatrixValid = Camera1.GetWorldToViewportMatrix(ref worldToViewportMatrix, forceMatrixRefresh: false);

                    if (isWorldToViewportMatrixValid)
                    {
                        Parallel.For(0, _lineSelectorData.Count,
                                     i => _lineSelectorData[i].CalculateScreenSpacePositions(ref worldToViewportMatrix, transform: null));
                    }
                }
                else
                {
                    for (var i = 0; i < _lineSelectorData.Count; i++)
                    {
                        _lineSelectorData[i].CalculateScreenSpacePositions(Camera1);
                    }
                }

                _isCameraChanged = false;
            }


            // Now we can call the GetClosestDistance method.
            // This method calculates the closest distance from the _lastMousePosition to the line that was used to create the LineSelectorData.
            // GetClosestDistance also sets the LastDistance, LastLinePositionIndex properties on the LineSelectorData.

            if (MultiThreadedCheckBox.IsChecked ?? false)
            {
                Parallel.For(0, _lineSelectorData.Count,
                             i => _lineSelectorData[i].GetClosestDistance(_lastMousePosition));
            }
            else
            {
                for (var i = 0; i < _lineSelectorData.Count; i++)
                {
                    _lineSelectorData[i].GetClosestDistance(_lastMousePosition);
                }
            }


            // Get the closest line
            IEnumerable <LineSelectorData> usedLineSelectors;

            // If we limit the distance of the specified position to the line, then we can filter all the line with Where
            if (_maxSelectionDistance >= 0)
            {
                usedLineSelectors = _lineSelectorData.Where(l => l.LastDistance <= _maxSelectionDistance).ToList();
            }
            else
            {
                usedLineSelectors = _lineSelectorData;
            }


            List <LineSelectorData> orderedLineSelectors;

            if (OrderByDistanceCheckBox.IsChecked ?? false)
            {
                // Order by camera distance
                orderedLineSelectors = usedLineSelectors.OrderBy(l => l.LastDistanceFromCamera).ToList();
            }
            else
            {
                // Order by distance to the specified position
                orderedLineSelectors = usedLineSelectors.OrderBy(l => l.LastDistance).ToList();
            }

            // Get the closest LineSelectorData
            LineSelectorData closestLineSelector;

            if (orderedLineSelectors.Count > 0)
            {
                closestLineSelector = orderedLineSelectors[0];
            }
            else
            {
                closestLineSelector = null;
            }


            // It is possible to get the positions of the line segment that is closest to the mouse position
            //var closestPolyLine = (PolyLineVisual3D)closestLineSelector.LineVisual;
            //Point3D firstSegmentPosition = closestPolyLine.Positions[closestLineSelector.LastLinePositionIndex];
            //Point3D secondSegmentPosition = closestPolyLine.Positions[closestLineSelector.LastLinePositionIndex + 1];

            // To get the actual position on the line that is closest to the mouse position, use the LastClosestPositionOnLine
            //closestLineSelector.LastClosestPositionOnLine;


            // The closest position on the line is shown with a SphereVisual3D
            if (_closestPositionSphereVisual3D == null)
            {
                _closestPositionSphereVisual3D = new SphereVisual3D()
                {
                    Radius   = 2,
                    Material = new DiffuseMaterial(Brushes.Red)
                };

                MainViewport.Children.Add(_closestPositionSphereVisual3D);
            }

            if (closestLineSelector == null)
            {
                ClosestDistanceValue.Text  = "";
                LineSegmentIndexValue.Text = "";
                _closestPositionSphereVisual3D.IsVisible = false;
            }
            else
            {
                ClosestDistanceValue.Text  = string.Format("{0:0.0}", closestLineSelector.LastDistance);
                LineSegmentIndexValue.Text = closestLineSelector.LastLinePositionIndex.ToString();

                _closestPositionSphereVisual3D.CenterPosition = closestLineSelector.LastClosestPositionOnLine;
                _closestPositionSphereVisual3D.IsVisible      = true;
            }


            // Show the closest line as red
            if (!ReferenceEquals(_lastSelectedLineSelector, closestLineSelector))
            {
                if (_lastSelectedLineSelector != null)
                {
                    _lastSelectedLineSelector.LineVisual3D.LineColor = _savedLineColor;
                }

                if (closestLineSelector != null)
                {
                    _savedLineColor = closestLineSelector.LineVisual3D.LineColor;
                    closestLineSelector.LineVisual3D.LineColor = Colors.Red;
                }

                _lastSelectedLineSelector = closestLineSelector;
            }
        }
        private void CreateLandscape(int gridSize, float noise, int randomSeed = 0) // is randomSeed is 0 or less, then random seed is used
        {
            _lastUsedGridSize = gridSize;

            // Generate height data with using DiamondSquare algorithm
            float[,] floatHeightData = DiamondSquareGrid(gridSize, seed: randomSeed, rMin: 0, rMax: 1, noise: noise);

            // To show height map we will use HeightMapMesh3D from Ab3d.PowerToys library
            // This class requires data in array of doubles.
            // NOTE: In the future version of DXEngine there will be an optimized version of HeightMesh that will take floats

            _heightData = new double[gridSize, gridSize];

            for (int rowIndex = 0; rowIndex < gridSize; rowIndex++)
            {
                for (int columnIndex = 0; columnIndex < gridSize; columnIndex++)
                {
                    _heightData[rowIndex, columnIndex] = (double)floatHeightData[rowIndex, columnIndex];
                }
            }


            // We can use HeightMapMesh3D object from Ab3d.PowerToys library to create a MeshGeometry3D object from 2D array
            // We create Mesh with size 1 x 1 x 1
            // This will allow us to scale it later to any size with ScaleTransform3D
            _heightMapMesh3D = new Meshes.HeightMapMesh3D(new Point3D(0, 0, 0), new Size3D(1, 1, 1), _heightData).Geometry;


            var vertexColorDiffuseMaterial = new DiffuseMaterial();

            int positionsCount = _heightMapMesh3D.Positions.Count;


            // Create vertexColorsArray
            var vertexColorsArray = new Color4[positionsCount];

            // Fill the vertexColorsArray with color values based on the data from _heightData
            CalculateVertexColors(_heightData, vertexColorsArray);

            // To show per-vertex color, we need to use a special VertexColorMaterial
            // Reuse VertexColorMaterial if possible.

            // Dispose old VertexColorMaterial and create a new one
            if (_vertexColorMaterial != null)
            {
                _vertexColorMaterial.Dispose();
            }

            _vertexColorMaterial = new VertexColorMaterial()
            {
                PositionColors = vertexColorsArray
            };

            // This material needs to be set to the WPF vertexColorDiffuseMaterial as UsedDXMaterial.
            // This means that when DXEngine will need to show WPF vertexColorDiffuseMaterial, it will use the vertexColorMaterial.
            vertexColorDiffuseMaterial.SetUsedDXMaterial(_vertexColorMaterial);


            // Now create GeometryModel3D and ModelVisual3D
            var vertexColorGeometryModel3D = new GeometryModel3D(_heightMapMesh3D, vertexColorDiffuseMaterial);

            vertexColorGeometryModel3D.Transform = new ScaleTransform3D(200, 200, 200);

            // To make the landscape colors visible from front and also back side, uncomment the line below (and comment the GeometryModel3D generation a few lines below):
            //vertexColorGeometryModel3D.BackMaterial = vertexColorDiffuseMaterial;

            var vertexColorModelVisual3D = new ModelVisual3D()
            {
                Content = vertexColorGeometryModel3D
            };

            MainViewport.Children.Add(vertexColorModelVisual3D);


            // Add a new GeometryModel3D with the same _heightMapMesh3D
            // But this time render only back faces with gray material
            var backGeometryModel3D = new GeometryModel3D
            {
                Geometry     = vertexColorGeometryModel3D.Geometry,  // Same geometry as vertexColorGeometryModel3D
                Material     = null,                                 // Do not render front-faced triangles
                BackMaterial = new DiffuseMaterial(Brushes.DimGray), // Render only back faces
                Transform    = vertexColorGeometryModel3D.Transform  // Same scale transform as vertexColorGeometryModel3D
            };

            var backModelVisual3D = new ModelVisual3D()
            {
                Content = backGeometryModel3D
            };

            MainViewport.Children.Add(backModelVisual3D);


            // Refresh the camera in case we have removed the camera's light with MainViewport.Children.Clear();
            Camera1.Refresh();
        }
        private void RecreateTargetModel()
        {
            MainViewport.Children.Clear();

            Visual3D targetModelVisual3D;

            // Setup VisualBrush as the material for the model
            var visualBrush = new VisualBrush(DrawingCanvas);
            var material    = new DiffuseMaterial(visualBrush);


            if (HeightMapModelTypeRadioButton.IsChecked ?? false)
            {
                var heightMapVisual3D = new Ab3d.Visuals.HeightMapVisual3D()
                {
                    Size                = new Size3D(100, 50, 100),
                    IsWireframeShown    = false,
                    IsSolidSurfaceShown = true,
                    Material            = material,
                    BackMaterial        = new DiffuseMaterial(Brushes.DimGray)
                };

                // Create height data from bitmap
                string      heightMapFileName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources\\HeightMaps\\simpleHeightMap.png");
                BitmapImage heightImage       = new BitmapImage(new Uri(heightMapFileName, UriKind.RelativeOrAbsolute));

                double[,] heightData = Ab3d.PowerToys.Samples.Objects3D.HeightMapSample.OpenHeightMapDataFile(heightImage, invertData: false);

                if (heightData != null)
                {
                    heightMapVisual3D.HeightData = heightData;
                }

                targetModelVisual3D = heightMapVisual3D;
            }
            else if (BoxModelTypeRadioButton.IsChecked ?? false)
            {
                var boxVisual3D = new Ab3d.Visuals.BoxVisual3D()
                {
                    Size     = new Size3D(100, 50, 100),
                    Material = material
                };

                targetModelVisual3D = boxVisual3D;
            }
            else //if (SphereModelTypeRadioButton.IsChecked ?? false)
            {
                var sphereVisual3D = new Ab3d.Visuals.SphereVisual3D()
                {
                    Radius   = 40,
                    Material = material
                };

                targetModelVisual3D = sphereVisual3D;
            }

            MainViewport.Children.Add(targetModelVisual3D);

            // With clearing all childen we have also removed the camera's light.
            // Refresh the camera now to recreate the light
            Camera1.Refresh();

            SetupMouseEventHandlers(targetModelVisual3D);
        }
        public InstanceWorldMatrixSamples()
        {
            InitializeComponent();

            AddSampleTransformation(
                SharpDX.Matrix.Identity,
                @"The original MeshGeometry3D is not changed when the World transformation is set to the Identity matrix (all matrix fields are zero, except diagonal fields that are 1)");

            AddSampleTransformation(
                SharpDX.Matrix.Translation(-1.5f, 0.4f, 1f),
                @"To change the position of one MeshGeometry3D instance, we set the M41 (for x offset), M42 (for y offset) and M43 (for z offset) fields in the World matrix.

The following offset is used here:
x offset: -1.5
y offset: 0.4
z offset: 1");

            AddSampleTransformation(
                SharpDX.Matrix.Scaling(0.5f, 1.0f, 3.0f),
                @"To scale one MeshGeometry3D instance, we set the 3 diagonal matrix values: M11 (for x scale), M22 (for y scale) and M33 (for z scale).

The following scale factors are used here:
x scale: 0.5
y scale: 1.0
z scale: 3.0");

            AddSampleTransformation(
                SharpDX.Matrix.RotationAxis(new SharpDX.Vector3(0, 1, 0), MathUtil.DegreesToRadians(33)),
                @"There are many ways to rotate an instance. One way is to use WPF's RotateTransform3D and AxisAngleRotation3D to specify axis of rotation and rotation angle. Then we need to convert the rotation matrix from the WPF matrix to the SharpDX matrix.

The following rotation is used for this instance:
Matrix.RotationAxis(new SharpDX.Vector3(0, 1, 0), MathUtil.DegreesToRadians(33))

This is the same as the following WPF rotation would be used:
RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 33))");

            // We can also use WPF's rotation objects:
            //var rotateTransform3D = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 33));
            //AddSampleTransformation(
            //    rotateTransform3D.Value.ToMatrix(),
            //    @"There are many ways to rotate an instance. One way is to use WPF's RotateTransform3D and AxisAngleRotation3D to specify axis of rotation and rotation angle. Then we need to convert the rotation matrix from the WPF matrix to the SharpDX matrix.\n\nThe following rotation is used for this instance:\nRotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 33))");


            AddSampleTransformation(
                SharpDX.Matrix.Scaling(0.5f, 1.0f, 3.0f) *
                SharpDX.Matrix.Translation(-1.5f, 0.4f, 1f),
                @"We can combine transformations with multiplying them.

This sample combines scale and translation with:
finalTransformation = scale(0.5, 1.0, 3.0) * translation(-1.5, 0.4, 1)");

            AddSampleTransformation(
                SharpDX.Matrix.Scaling(0.5f, 1.0f, 3.0f) *
                SharpDX.Matrix.RotationAxis(new SharpDX.Vector3(0, 1, 0), MathUtil.DegreesToRadians(33)) *
                SharpDX.Matrix.Translation(-1.5f, 0.40f, 1f),
                @"We can also add rotation (as the second step in transformations):
finalTransformation = scale * rotation * translation");

            AddSampleTransformation(
                SharpDX.Matrix.Translation(-1.5f, 0.40f, 1f) *
                SharpDX.Matrix.RotationAxis(new SharpDX.Vector3(0, 1, 0), MathUtil.DegreesToRadians(33)) *
                SharpDX.Matrix.Scaling(0.5f, 1.0f, 3.0f),
                @"IMPORTANT
\!Order in which the transformations are applied (are multiplied) is very important!\!
The proper order is:
1) scale
2) rotate
3) traslate

This example shows the result where the transformantions are not application in the correct order: 
finalTransformation = translation * rotation * scale");


            AddSampleTransformation(
                CalculateMatrixFromPositionsAndSize(startPosition: new Vector3(-1, 0, 0), endPosition: new Vector3(0, 0, -1), size: new Size(0.5, 0.5)),
                @"Using \!CalculateMatrixFromPositionsAndSize\! method to create a Matrix that will transform the box mesh so that it starts at the specified start position, end at the specified end position and have the specified width and height.

\bstartPosition:\b new Vector3(-1, 0, 0)
\bendPosition:\b new Vector3(0, 0, -1)
\bsize:\b new Size(0.5, 0.5)");

            AddSampleTransformation(
                CalculateMatrixFromPositionsAndSize(startPosition: new Vector3(-1, 0, -1), endPosition: new Vector3(1, 0, 0), size: new Size(1.0, 0.1)),
                @"Another sample of using CalculateMatrixFromPositionsAndSize. Note that even if the MeshGeometry3D box has size of 1 x 1 x 1, we can easily convert it into a plate with scaling its height - to 0.1 in this case.

\!startPosition:\! new Vector3(-1, 0, -1)
\!endPosition:\! new Vector3(1, 0, 0)
\!size:\! new Size(1.0, 0.1)");


            GenerateSampleObjects();


            this.Focusable       = true;                                                           // by default Page is not focusable and therefore does not receive keyDown event
            this.PreviewKeyDown += new KeyEventHandler(InstanceWorldMatrixSamples_PreviewKeyDown); // Use PreviewKeyDown to get arrow keys also (KeyDown event does not get them)
            //this.Focus();


            this.Loaded += delegate(object sender, RoutedEventArgs args)
            {
                Camera1.Refresh();

                SetupIntroText();
            };
        }
        void ShadingHelperSample_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateModel();

            Camera1.Refresh(); // This will measure the models on the scene and reposition the scene camera
        }