public BooleanOperationsSample()
        {
            InitializeComponent();

            var boxMesh    = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(100, 100, 100), 1, 1, 1).Geometry;
            var sphereMesh = new Ab3d.Meshes.SphereMesh3D(new Point3D(0, 0, 0), 65, 16).Geometry;


            // Note that we set processOnlyIntersectingTriangles to false because boxMesh and sphereMesh are almost the same size and
            // therefore all triangles need to be processes. With false value we skip checking which triangles are intersecting.
            // If the sphereMesh would be much smaller than it is recommended to set that to true (this is also the default value so we can skip that).
            // See the next (AdvancedBooleanOperations) for more info about that.

            // Subtract
            var subtractedMesh = Ab3d.Utilities.MeshBooleanOperations.Subtract(boxMesh, sphereMesh, processOnlyIntersectingTriangles: false);

            ShowMesh(subtractedMesh, -150);

            // Intersect
            var intersectedMesh = Ab3d.Utilities.MeshBooleanOperations.Intersect(boxMesh, sphereMesh, processOnlyIntersectingTriangles: false);

            ShowMesh(intersectedMesh, 0);

            //Union
            var unionMesh = Ab3d.Utilities.MeshBooleanOperations.Union(boxMesh, sphereMesh, processOnlyIntersectingTriangles: false);

            ShowMesh(unionMesh, 150);


            // SEE ALSO:
            // AdvancedBooleanOperations.xaml.cs
            // UseCases/ModelingWithBooleanOperations.xaml.cs
            // Utilities/TextureCoordinatesGeneratorSample (to generate texture coordinates that are lost with boolean operations)
        }
Esempio n. 2
0
        private void CreateInstances()
        {
            MeshGeometry3D meshGeometry3D;
            double         modelScaleFactor;

            if (MeshTypeComboBox.SelectedIndex == 2) // Bunnies
            {
                // Load standard Standfor Bunny model (res3) with 11533 position
                meshGeometry3D = LoadMeshFromObjFile("bun_zipper_res3.obj");

                var bounds = meshGeometry3D.Bounds;

                double diagonalSize = Math.Sqrt(bounds.SizeX * bounds.SizeX + bounds.SizeY * bounds.SizeY + bounds.SizeZ * bounds.SizeZ);
                modelScaleFactor = 20 / diagonalSize;     // Scale model so that its diagonal is 20 units big
            }
            else if (MeshTypeComboBox.SelectedIndex == 1) // Spheres
            {
                // Sphere with 382
                meshGeometry3D   = new Ab3d.Meshes.SphereMesh3D(centerPosition: new Point3D(0, 0, 0), radius: 5, segments: 20, generateTextureCoordinates: false).Geometry;
                modelScaleFactor = 1;
            }
            else
            {
                // Box with 24 positions (for each corner we need 3 positions to create sharp edges - we need 3 normal vectors for each edge)
                meshGeometry3D   = new Ab3d.Meshes.BoxMesh3D(centerPosition: new Point3D(0, 0, 0), size: new Size3D(10, 10, 10), xSegments: 1, ySegments: 1, zSegments: 1).Geometry;
                modelScaleFactor = 1;
            }

            int selectedInstancesYCount = GetSelectedInstancesYCount();


            bool  useTransparency = UseTransparencyCheckBox.IsChecked ?? false;
            float alphaColor      = useTransparency ? 0.3f : 1.0f; // When we use transparency, we set alpha color to 0.3 (we also need to set UseAlphaBlend to true - see below)

            // The following method prepare InstanceData array with data for each instance (WorldMatrix and Color)
            InstanceData[] instancedData = CreateInstancesData(new Point3D(0, 200, 0), new Size3D(400, 400, 400), (float)modelScaleFactor, 20, selectedInstancesYCount, 20, useTransparency);


            // Create InstancedGeometryVisual3D with selected meshGeometry and InstancesData
            var instancedMeshGeometryVisual3D = new InstancedMeshGeometryVisual3D(meshGeometry3D);

            instancedMeshGeometryVisual3D.InstancesData = instancedData;

            // When we use transparency, we also need to set UseAlphaBlend to true
            instancedMeshGeometryVisual3D.UseAlphaBlend = useTransparency;

            // If we would only change the InstancedData we would need to call Update method (but here this is not needed because we have set the data for the first time)
            //_instancedGeometryVisual3D.Update();


            ObjectsPlaceholder.Children.Clear();
            ObjectsPlaceholder.Children.Add(instancedMeshGeometryVisual3D);


            // Update statistics:
            //PositionsPerMeshTextBlock.Text = string.Format("Positions per mesh: {0:#,##0}", meshGeometry3D.Positions.Count);
            TotalTextBlock.Text = string.Format("Total positions: {0:#,##0} * {1:#,##0} = {2:#,##0}", meshGeometry3D.Positions.Count, 20 * 20 * selectedInstancesYCount, meshGeometry3D.Positions.Count * selectedInstancesYCount * 20 * 20);
        }
        public BooleanOperationsSample()
        {
            InitializeComponent();

            var boxMesh    = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(100, 100, 100), 1, 1, 1).Geometry;
            var sphereMesh = new Ab3d.Meshes.SphereMesh3D(new Point3D(0, 0, 0), 65, 16).Geometry;


            // Subtract
            var subtractedMesh = Ab3d.Utilities.MeshBooleanOperations.Subtract(boxMesh, sphereMesh);

            ShowMesh(subtractedMesh, -150);

            // Intersect
            var intersectedMesh = Ab3d.Utilities.MeshBooleanOperations.Intersect(boxMesh, sphereMesh);

            ShowMesh(intersectedMesh, 0);

            //Union
            var unionMesh = Ab3d.Utilities.MeshBooleanOperations.Union(boxMesh, sphereMesh);

            ShowMesh(unionMesh, 150);
        }
        public WireframePolygonLines()
        {
            InitializeComponent();

            // This sample shows how to show polygon lines for the 3D models created in Ab3d.PowerToys library.
            //
            // To enable creation of polygon indices (position indexes that define polygon lines),
            // you can set the CreatePolygonIndices property on the Mesh3D objects that is used to created 3D object.
            //
            // For example:
            // var boxMesh3D = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(10, 10, 10), 1, 1, 1);
            // boxMesh3D.CreatePolygonIndices = true;
            // var boxMeshGeometry = boxMesh3D.Geometry;
            //
            // The CreatePolygonIndices property is actually nullable Boolean.
            // When its value is not set (is null), then a global static Ab3d.Utilities.MeshUtils.CreatePolygonIndicesByDefault value is used.
            //
            // This way it is easy to enable creation of polygon indices for all objects.
            // This is also done in this sample:

            Ab3d.Utilities.MeshUtils.CreatePolygonIndicesByDefault = true;

            // Create sample objects
            Model3DGroup sceneModel3DGroup = CreateScene();

            // We will show the polygon lines with using WireframeVisual3D.
            // To show polygon lines instead of triangle wireframe, we need to set ShowPolygonLines to true.
            WireframeVisual1.ShowPolygonLines = true;

            // Now assign Model3DGroup that contains the sample 3D objects
            WireframeVisual1.OriginalModel = sceneModel3DGroup;


            // IMPORTANT:
            // WireframeVisual3D is great for showing polygon lines for mostly static objects and for not very complex scenes.
            // But if you have a complex scene where the objects change often, using the WireframeVisual3D can become slow.
            // In this case it is recommended to show polygon lines manually with using MultiLineVisual3D or some similar object (see code below).

            // The following code shows how to show polygon lines manually (without WireframeVisual3D object)
            // Those lines will be shown as white to distinguish them from the lines created by WireframeVisual3D.

            // First create box model with polygon.
            var boxMesh3D = new Ab3d.Meshes.BoxMesh3D(centerPosition: new Point3D(-15, 4, 90), size: new Size3D(40, 8, 20), xSegments: 1, ySegments: 1, zSegments: 1);

            boxMesh3D.CreatePolygonIndices = true;

            var boxMeshGeometry3D = boxMesh3D.Geometry;


            // You can get polygon indices with GetPolygonIndices extension method on meshGeometry3D
            Int32Collection polygonIndices = boxMeshGeometry3D.GetPolygonIndices();

            // But instead of using polygon indices we need polygonpositions. This can be get with GetPolygonPositions
            Point3DCollection polygonPositions = Ab3d.Utilities.MeshUtils.GetPolygonPositions(boxMeshGeometry3D);

            // Now use the positions to create a MultiLineVisual3D object
            var multiLineVisual3D = new Ab3d.Visuals.MultiLineVisual3D()
            {
                Positions     = polygonPositions,
                LineColor     = Colors.White,
                LineThickness = 2
            };

            MainViewport.Children.Add(multiLineVisual3D);


            // We will also create a solid model from the box
            var material        = this.FindResource("ObjectsMaterial") as Material;
            var geometryModel3D = new GeometryModel3D(boxMeshGeometry3D, material);

            var modelVisual3D = new ModelVisual3D();

            modelVisual3D.Content = geometryModel3D;

            MainViewport.Children.Add(modelVisual3D);
        }
        public AdvancedBooleanOperations()
        {
            InitializeComponent();

            var boxMesh = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(200, 10, 200), 10, 1, 10).Geometry;

            // NOTE:
            // When doing the boolean operations it is very good to keep the meshes as simple as possible.
            // Here we use quite complex meshes because segments parameter is set to 30. This better demonstrates the use of processOnlyIntersectingTriangles parameter.
            // For a real world scenarios I would recommend to use lower segment values.
            var cylinder1Mesh = new Ab3d.Meshes.CylinderMesh3D(new Point3D(-50, -50, 90), radius: 8, height: 100, segments: 30, isSmooth: true).Geometry;
            var cylinder2Mesh = new Ab3d.Meshes.CylinderMesh3D(new Point3D(-20, -50, 90), radius: 8, height: 100, segments: 30, isSmooth: true).Geometry;

            // It is recommended to combine the cylinder meshes and then do one subtraction.
            var combinedMesh = Ab3d.Utilities.MeshUtils.CombineMeshes(cylinder1Mesh, cylinder2Mesh);


            // Process mesh subtraction only on triangles that intersect the combinedMesh.
            // Because in our case the combinedMesh is much smaller then boxMesh, this produces significantly less triangles.
            //
            // But when you know that most of the triangles in the meshes would intersect, then
            // it is worth setting processOnlyIntersectingTriangles to false to skip getting intersecting triangles.

            var subtractedMesh1 = Ab3d.Utilities.MeshBooleanOperations.Subtract(boxMesh, combinedMesh, processOnlyIntersectingTriangles: true);

            ShowMesh(subtractedMesh1, -120);

            var subtractedMesh2 = Ab3d.Utilities.MeshBooleanOperations.Subtract(boxMesh, combinedMesh, processOnlyIntersectingTriangles: false);

            ShowMesh(subtractedMesh2, 120);


            TextBlockVisual1.Text += string.Format("\r\nFinal triangles count: {0}", subtractedMesh1.TriangleIndices.Count / 3);
            TextBlockVisual2.Text += string.Format("\r\nFinal triangles count: {0}", subtractedMesh2.TriangleIndices.Count / 3);


            // Deep dive:
            // When processOnlyIntersectingTriangles is set to true, then behind the scenes the
            // MeshUtils.GetIntersectingTriangles, MeshUtils.SplitMeshByIndexesOfTriangles and MeshUtils.CombineMeshes methods
            // are used to split the original mesh into two meshes.
            //
            // If you want to do that manually, here is the code from Subtract method:
            // (instead of SubtractInt method you can call Subtract method with processOnlyIntersectingTriangles set to false)
            //
            //public static MeshGeometry3D Subtract(MeshGeometry3D mesh1, MeshGeometry3D mesh2, bool processOnlyIntersectingTriangles = true)
            //{
            //    if (mesh1 == null)
            //        return null;

            //    if (mesh2 == null)
            //        return mesh1;


            //    MeshGeometry3D resultMesh;

            //    if (processOnlyIntersectingTriangles)
            //    {
            //        int originalTrianglesCount = mesh1.TriangleIndices.Count / 3;

            //        var intersectingTriangles = MeshUtils.GetIntersectingTriangles(mesh2.Bounds, mesh1, meshTransform: null);

            //        if (intersectingTriangles == null || intersectingTriangles.Count == 0)
            //            return mesh1; // No intersection - preserve the mesh1


            //        if (intersectingTriangles.Count > originalTrianglesCount * 0.8) // When more than 80 percent of triangles is inside the mesh2, then just use the original mesh1
            //        {
            //            resultMesh = SubtractInt(mesh1, mesh2);
            //        }
            //        else
            //        {
            //            MeshGeometry3D splitMesh1, splitMesh2;
            //            MeshUtils.SplitMeshByIndexesOfTriangles(mesh1, intersectingTriangles, false, out splitMesh1, out splitMesh2);

            //            var processedMesh = SubtractInt(splitMesh1, mesh2);

            //            if (processedMesh == null)
            //            {
            //                resultMesh = splitMesh2;
            //            }
            //            else
            //            {
            //                // Combine triangles that were not processed (not intersecting with mesh2) with the result of subtraction
            //                resultMesh = Ab3d.Utilities.MeshUtils.CombineMeshes(splitMesh2, processedMesh);
            //            }
            //        }
            //    }
            //    else
            //    {
            //        if (!mesh2.Bounds.IntersectsWith(mesh1.Bounds))
            //        {
            //            // In case there is no intersection, then just return the original mesh1
            //            resultMesh = mesh1;
            //        }
            //        else
            //        {
            //            // Process whole meshes
            //            resultMesh = SubtractInt(mesh1, mesh2);
            //        }
            //    }

            //    return resultMesh;
            //}
        }