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