private void AddInstancedSpirals(int xCount, int yCount, int spiralLength) { double circleRadius = 10; int spiralCircles = spiralLength / 20; // One circle in the spiral is created from 20 lines var spiralPositions = CreateSpiralPositions(startPosition: new Point3D(0, 0, 0), circleXDirection: new Vector3D(1, 0, 0), circleYDirection: new Vector3D(0, 1, 0), oneSpiralCircleDirection: new Vector3D(0, 0, -10), circleRadius: circleRadius, segmentsPerCircle: 20, circles: spiralCircles); var spiralPositionCollection = new Point3DCollection(spiralPositions); double xStart = -xCount * circleRadius * 3 / 2; double yStart = -yCount * circleRadius * 3 / 2; MainViewport.BeginInit(); MainViewport.Children.Clear(); var materialGroup = new MaterialGroup(); materialGroup.Children.Add(new DiffuseMaterial(Brushes.Black)); materialGroup.Children.Add(new EmissiveMaterial(Brushes.White)); for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { // PERFORMANCE NOTICE: // The AddSpiralVisual3D creates one PolyLineVisual3D for each sphere. // When creating many PolyLineVisual3D objects, the performance would be significantly improved if instead of many PolyLineVisual3D objects, // all the spheres would be rendered with only one MultiPolyLineVisual3D. // This would allow rendering all the 3D lines with only one draw call. var instancedMeshGeometryVisual3D = AddInstancedTubePath(spiralPositionCollection, Color4.White); instancedMeshGeometryVisual3D.Transform = new TranslateTransform3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0); //var tubePathVisual3D = new TubePathVisual3D() //{ // PathPositions = spiralPositionCollection, // Radius = 2, // Segments = 8, // GenerateTextureCoordinates = false, //}; //tubePathVisual3D.Material = materialGroup; //tubePathVisual3D.Transform = new TranslateTransform3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0); //MainViewport.Children.Add(tubePathVisual3D); } } MainViewport.EndInit(); }
// This method uses standard Ab3d.PowerToys TubePathVisual3D to create tube paths private void AddSpirals_TubePathVisual3D(int xCount, int yCount, int spiralLength) { float circleRadius = 10; int spiralCircles = spiralLength / 20; // One circle in the spiral is created from 20 lines double xStart = -xCount * circleRadius * 1.5; double yStart = -yCount * circleRadius * 1.5; MainViewport.BeginInit(); MainViewport.Children.Clear(); var materialGroup = new MaterialGroup(); materialGroup.Children.Add(new DiffuseMaterial(Brushes.Black)); materialGroup.Children.Add(new EmissiveMaterial(Brushes.White)); for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { var spiralPositions = CreateSpiralPositions(startPosition: new Point3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0), circleXDirection: new Vector3D(1, 0, 0), circleYDirection: new Vector3D(0, 1, 0), oneSpiralCircleDirection: new Vector3D(0, 0, -10), circleRadius: circleRadius, segmentsPerCircle: 20, circles: spiralCircles); var tubePathVisual3D = new TubePathVisual3D() { PathPositions = new Point3DCollection(spiralPositions), Radius = 2, Segments = 8, GenerateTextureCoordinates = false, }; tubePathVisual3D.Material = materialGroup; //tubePathVisual3D.Transform = new TranslateTransform3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0); MainViewport.Children.Add(tubePathVisual3D); } } MainViewport.EndInit(); }
private void AddSpirals(int xCount, int yCount, int spiralLength) { double circleRadius = 10; int spiralCircles = spiralLength / 20; // One circle in the spiral is created from 20 lines var spiralPositions = CreateSpiralPositions(startPosition: new Point3D(0, 0, 0), circleXDirection: new Vector3D(1, 0, 0), circleYDirection: new Vector3D(0, 1, 0), oneSpiralCircleDirection: new Vector3D(0, 0, -10), circleRadius: circleRadius, segmentsPerCircle: 20, circles: spiralCircles); var spiralPositionCollection = new Point3DCollection(spiralPositions); double xStart = -xCount * circleRadius * 3 / 2; double yStart = -yCount * circleRadius * 3 / 2; MainViewport.BeginInit(); MainViewport.Children.Clear(); for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { // PERFORMANCE NOTICE: // The AddSpiralVisual3D creates one PolyLineVisual3D for each sphere. // When creating many PolyLineVisual3D objects, the performance would be significantly improved if instead of many PolyLineVisual3D objects, // all the spheres would be rendered with only one MultiPolyLineVisual3D. // This would allow rendering all the 3D lines with only one draw call. AddSpiralVisual3D(spiralPositionCollection, new TranslateTransform3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0)); } } MainViewport.EndInit(); }
// This method uses low level DXEngine objects to create tube paths. private void AddSpirals_MeshObjectNode(int xCount, int yCount, int spiralLength, bool useMultiThreading) { float circleRadius = 10; int spiralCircles = spiralLength / 20; // One circle in the spiral is created from 20 lines var dxMaterial = new Ab3d.DirectX.Materials.StandardMaterial() { DiffuseColor = Color3.Black, EmissiveColor = Color3.White, Effect = _solidColorEffect }; _disposables.Add(dxMaterial); float xStart = -xCount * circleRadius * 1.5f; float yStart = -yCount * circleRadius * 1.5f; if (useMultiThreading) { // On i7 6700 with 4 cores with hyper-threading the multi-threaded code path is almost 100% faster then single threaded solution. var initializedMeshes = new MeshBase[xCount, yCount]; var dxDevice = MainDXViewportView.DXScene.DXDevice; Parallel.For(0, xCount * yCount, xy => { int x = (int)xy / yCount; int y = (int)xy % yCount; var spiralPositions = CreateSpiralPositions(startPosition: new Vector3(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0), circleXDirection: new Vector3(1, 0, 0), circleYDirection: new Vector3(0, 1, 0), oneSpiralCircleDirection: new Vector3(0, 0, -10), circleRadius: circleRadius, segmentsPerCircle: 20, circles: spiralCircles); MeshBase tubePathMesh = CreateTubePathMesh(spiralPositions, radius: 1.0f, segmentsCount: 8, isTubeClosed: true, tubeColor: Color4.White); // Create DirectX resources in the background thread (this creates buffers on the GPU and send data there from the main memory) tubePathMesh.InitializeResources(dxDevice); // Save the mesh initializedMeshes[x, y] = tubePathMesh; }); // Now most of the work was done in multi-threaded way. // So we only need to create the MeshObjectNode and add that to the Scene. This needs to be done on the UI thread. MainViewport.BeginInit(); MainViewport.Children.Clear(); for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { var tubePathMesh = initializedMeshes[x, y]; var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(tubePathMesh, dxMaterial); var tubePathVisual3D = new SceneNodeVisual3D(meshObjectNode); // IMPORTANT: // // In this performance demo we create new spiral positions and new tubePathMesh for each spiral. // But because the spirals are the same, we could create only one spiral positions and one tubePathMesh // and then use that tubePathMesh to create multiple MeshObjectNode and SceneNodeVisual3D objects // each of them with its Transform property set - as shown in the line below. // // Sharing one mesh would provide much better performance and lower memory usage, // but for this demo we want to simulate cration of huge tube paths in the background thread. // //tubePathVisual3D.Transform = new TranslateTransform3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0); _disposables.Add(tubePathMesh); // We did not add that in the background thread (we would need locking for that) so we need to do that now _disposables.Add(meshObjectNode); MainViewport.Children.Add(tubePathVisual3D); } } MainViewport.EndInit(); } else { // No multi-threading MainViewport.BeginInit(); MainViewport.Children.Clear(); for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { var spiralPositions2 = CreateSpiralPositions(startPosition: new Point3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0), circleXDirection: new Vector3D(1, 0, 0), circleYDirection: new Vector3D(0, 1, 0), oneSpiralCircleDirection: new Vector3D(0, 0, -10), circleRadius: circleRadius, segmentsPerCircle: 20, circles: spiralCircles); var spiralPositions = spiralPositions2.Select(p => p.ToVector3()).ToArray(); //var spiralPositions = CreateSpiralPositions(startPosition: new Vector3(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0), // circleXDirection: new Vector3(1, 0, 0), // circleYDirection: new Vector3(0, 1, 0), // oneSpiralCircleDirection: new Vector3(0, 0, -10), // circleRadius: circleRadius, // segmentsPerCircle: 20, // circles: spiralCircles); var tubePathMesh = CreateTubePathMesh(spiralPositions, radius: 2, segmentsCount: 8, isTubeClosed: true, tubeColor: Color4.White); var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(tubePathMesh, dxMaterial); var tubePathVisual3D = new SceneNodeVisual3D(meshObjectNode); //tubePathVisual3D.Transform = new TranslateTransform3D(x * circleRadius * 3 + xStart, y * circleRadius * 3 + yStart, 0); _disposables.Add(meshObjectNode); MainViewport.Children.Add(tubePathVisual3D); } } MainViewport.EndInit(); } }