private void CreateScene() { var boxMeshGeometry3D = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(BoxSize, BoxSize, BoxSize), 1, 1, 1).Geometry; _oneMeshTriangleIndicesCount = boxMeshGeometry3D.TriangleIndices.Count; PositionNormalTexture[] vertexBuffer; int[] indexBuffer; CreateMultiMeshBuffer(center: new Vector3(0, 0, 0), size: new Vector3(XCount * (BoxSize + BoxesMargin), YCount * (BoxSize + BoxesMargin), ZCount * (BoxSize + BoxesMargin)), xCount: XCount, yCount: YCount, zCount: ZCount, meshGeometry3D: boxMeshGeometry3D, vertexBuffer: out vertexBuffer, indexBuffer: out indexBuffer); _multiMaterialMesh = new SimpleMesh <PositionNormalTexture>(vertexBuffer, indexBuffer, inputLayoutType: InputLayoutType.Position | InputLayoutType.Normal | InputLayoutType.TextureCoordinate); _indexBufferLength = indexBuffer.Length; // i1 is at 1/4 of the height of the box _firstColorIndex = (int)(_indexBufferLength / 4); // i2 is at 3/4 of the height _secondColorIndex = _firstColorIndex * 3; _multiMaterialMesh.SubMeshes = new SubMesh[] { new SubMesh("SubMesh1") { MaterialIndex = 0, StartIndexLocation = 0, IndexCount = _firstColorIndex }, new SubMesh("SubMesh2") { MaterialIndex = 1, StartIndexLocation = _firstColorIndex, IndexCount = _secondColorIndex - _firstColorIndex }, new SubMesh("SubMesh3") { MaterialIndex = 2, StartIndexLocation = _secondColorIndex, IndexCount = _indexBufferLength - _secondColorIndex }, }; _disposables.Add(_multiMaterialMesh); var materials = new Ab3d.DirectX.Material[] { new Ab3d.DirectX.Materials.StandardMaterial() { DiffuseColor = Colors.DimGray.ToColor3() }, new Ab3d.DirectX.Materials.StandardMaterial() { DiffuseColor = Colors.Silver.ToColor3() }, new Ab3d.DirectX.Materials.StandardMaterial() { DiffuseColor = Colors.Gold.ToColor3() }, }; _meshObjectNode = new Ab3d.DirectX.MeshObjectNode(_multiMaterialMesh, materials); _disposables.Add(_meshObjectNode); // Use SceneNodeVisual3D to show SceneNode in DXViewportView var sceneNodeVisual3D = new SceneNodeVisual3D(_meshObjectNode); MainViewport.Children.Add(sceneNodeVisual3D); }
private void UpdateFogUsage() { if (MainDXViewportView.DXScene == null) // probably WPF 3D rendering is used { return; } // First make sure that we have the StandardEffect that is used by default if (_defaultStandardEffect == null) { _defaultStandardEffect = MainDXViewportView.DXScene.DXDevice.EffectsManager.GetStandardEffect(); } // First reset the changes of previously changed SceneNodes if (_changedDXMaterial != null) { _changedDXMaterial.Effect = null; _changedDXMaterial = null; } if (_yellowFogWpfMaterial != null) { // Replace _yellowFogWpfMaterial with default YellowMaterial var yellowMaterial = this.FindResource("YellowMaterial") as DiffuseMaterial; foreach (var baseModelVisual3D in MainViewport.Children.OfType <BaseModelVisual3D>()) { if (ReferenceEquals(baseModelVisual3D.Material, _yellowFogWpfMaterial)) { baseModelVisual3D.Material = yellowMaterial; } } _yellowFogWpfMaterial = null; } // NOTES: // We can use two ways to use custom FogEffect: // // 1) Set FogEffect as StandardEffect on EffectsManager. // StandardEffect is used when DXEngine is rendering standard WPF materials (diffuse, specular, emissive). // This means that if we change the StandardEffect, then all standard objects will be rendered with FogEffect. // Other objects like 3D lines will still be rendered with their special effects. // // 2) Set FogEffect to the Effect property on the WpfMaterial object that is a DXEngine's material object created from WPF's material. // When setting Effect property on DXEngine's material, the material will be rendered with the specified effect (instead of StandardEffect). // Additional notes: // 1) EffectsManager is created when the DXDevice is created. // This means that you cannot access it in the constructor of this class. // If you need to change the StandardEffect (or some other setting on DXDevice) before the first frame is rendered, // you need to use DXSceneDeviceCreated or DXSceneInitialized event handler to do that (as in this case). Effect newStandardEffect; if (StandardEffectRadioButton.IsChecked ?? false) { newStandardEffect = _fogEffect; } else if (RootBoxRadioButton.IsChecked ?? false) { newStandardEffect = _defaultStandardEffect; // Get the DXEngine's material that is created from the WPF's Material used for BaseBoxVisual3D var dxMaterial = Ab3d.DirectX.Materials.WpfMaterial.GetUsedDXMaterial(BaseBoxVisual3D.Material, MainDXViewportView.DXScene.DXDevice); if (dxMaterial != null) { // Now we can specify the exact effect that will be used to render dxMaterial (if this is not specified, then StandardEffect is used) dxMaterial.Effect = _fogEffect; _changedDXMaterial = dxMaterial; } } else if (ReplaceRadioButton.IsChecked ?? false) { newStandardEffect = _defaultStandardEffect; // Create a clone of YellowMaterial var yellowMaterial = this.FindResource("YellowMaterial") as DiffuseMaterial; if (yellowMaterial != null) { _yellowFogWpfMaterial = yellowMaterial.Clone(); // Now create a DXEngine's material that is created from WPF's material: var yellowFogDXEngineMaterial = new Ab3d.DirectX.Materials.WpfMaterial(_yellowFogWpfMaterial); // Specify the exact effect that will be used to render this material (if not specified, then the StandardEffect is used) yellowFogDXEngineMaterial.Effect = _fogEffect; // Now that we have both WPF material and DXEngine's material, // we can specify that whenever the _yellowFogWpfMaterial will be used (and when our DXDevice is used), // we should use the specified yellowFogDXEngineMaterial. // This is done with the static SetUsedDXMaterial method. // // TIP: The same approach can be used to use some other rendering technique to render on material - for example to use ModelColorLineEffect, ThickLineEffect or SolidColorEffect. Ab3d.DirectX.Materials.WpfMaterial.SetUsedDXMaterial(_yellowFogWpfMaterial, yellowFogDXEngineMaterial); // NOTE: // Instead of calling SetUsedDXMaterial, we could also specify the DXDevice in the WpfMaterial constructor. // This would call the SetUsedDXMaterial behind the scenes. But we did it here in the long way to better demonstrate what is going on. // The following code would call SetUsedDXMaterial in the constructor: // var yellowFogDXEngineMaterial = new Ab3d.DirectX.Materials.WpfMaterial(_yellowFogWpfMaterial, MainDXViewportView.DXScene.DXDevice); // After the _yellowFogWpfMaterial is "connected" to the yellowFogDXEngineMaterial, we can assign it insetad of yellow material: foreach (var baseModelVisual3D in MainViewport.Children.OfType <BaseModelVisual3D>()) { if (ReferenceEquals(baseModelVisual3D.Material, yellowMaterial)) { baseModelVisual3D.Material = _yellowFogWpfMaterial; } } } } else { newStandardEffect = _defaultStandardEffect; } MainDXViewportView.DXScene.DXDevice.EffectsManager.SetStandardEffect(newStandardEffect); }
private void CreateScene() { int xCount = 40; int yCount = 1; int zCount = 40; float sphereRadius = 10; float sphereMargin = 10; var sphereMeshGeometry3D = new Ab3d.Meshes.SphereMesh3D(new Point3D(0, 0, 0), sphereRadius, 10).Geometry; _oneMeshTriangleIndicesCount = sphereMeshGeometry3D.TriangleIndices.Count; PositionNormalTexture[] vertexBuffer; int[] indexBuffer; var size = new Vector3(xCount * (sphereRadius + sphereMargin), yCount * (sphereRadius + sphereMargin), zCount * (sphereRadius + sphereMargin)); SubMeshesSample.CreateMultiMeshBuffer(center: new Vector3(0, 0, 0), size: size, xCount: xCount, yCount: yCount, zCount: zCount, meshGeometry3D: sphereMeshGeometry3D, vertexBuffer: out vertexBuffer, indexBuffer: out indexBuffer); _multiMaterialMesh = new SimpleMesh <PositionNormalTexture>(vertexBuffer, indexBuffer, inputLayoutType: InputLayoutType.Position | InputLayoutType.Normal | InputLayoutType.TextureCoordinate); // Create all 3 SubMeshes at the beginning. // Though at first only the first SubMesh will be rendered (the other two have IndexCount set to 0), // this will allow us to simply change the StartIndexLocation and IndexCount of the SubMeshes // to show selected part without adding or removing any SubMesh (this would regenerate the RenderingQueues). // This way the selection is almost a no-op (only changing a few integer values and rendering the scene again). _multiMaterialMesh.SubMeshes = new SubMesh[] { // First sub-mesh will render triangles from the first to the start of selection (or all triangles if there is no selection) new SubMesh("MainSubMesh1") { MaterialIndex = 0, StartIndexLocation = 0, IndexCount = indexBuffer.Length }, // Second sub-mesh will render triangles after the selection (this one follows the first on to preserve the same material) new SubMesh("MainSubMesh2") { MaterialIndex = 0, StartIndexLocation = 0, IndexCount = 0 }, // The third sub-mesh will render selected triangles and will use the second material for that. new SubMesh("SelectionSubMesh") { MaterialIndex = 1, StartIndexLocation = 0, IndexCount = 0 }, }; _disposables.Add(_multiMaterialMesh); // Create OctTree from vertexBuffer. // This will significantly improve hit testing performance (check this with uncommenting the dxScene.GetClosestHitObject call in OnMouseMouse method). _octTree = new OctTree(vertexBuffer, indexBuffer); var materials = new Ab3d.DirectX.Material[] { new Ab3d.DirectX.Materials.StandardMaterial() { DiffuseColor = Colors.Green.ToColor3() }, new Ab3d.DirectX.Materials.StandardMaterial() { DiffuseColor = Colors.Red.ToColor3() } }; _meshObjectNode = new Ab3d.DirectX.MeshObjectNode(_multiMaterialMesh, materials); _disposables.Add(_meshObjectNode); // Use SceneNodeVisual3D to show SceneNode in DXViewportView var sceneNodeVisual3D = new SceneNodeVisual3D(_meshObjectNode); MainViewport.Children.Add(sceneNodeVisual3D); }
//private int _xCount = 10000; //private int _yCount = 2000; // Performance values for the 10000 x 2000 height map on NVIDIA 1080: // DirectXOverlay: render time 0.25 ms (4000 FPS) // DirectXImage - with back face rendering (bottom of the height map): render time around 14 ms (70 FPS) // - without back face rendering: render time around 7 ms (140 FPS) public OptimizedHeightMapGeneration() { InitializeComponent(); TitleTextBlock.Text += string.Format(" ({0}x{1} height values)", XCount, YCount); // First create very simple height map that can be created immediately. // After that we will wait until DXScene is initialized and then start a background worker // that will generate the mesh in the background. When this is done, we will update the 3D scene. _disposables = new DisposeList(); int xCount = XCount / 100; int yCount = YCount / 100; float[,] simpleHeightData; Color4[] simplePositionColorsArray; //GenerateSimpleHeightData(XCount, YCount, out heightData, out positionColorsArray); //GenerateRandomHeightData(XCount, YCount, out heightData, out positionColorsArray); GenerateSinusHeightData(xCount, yCount, null, out simpleHeightData, out simplePositionColorsArray); var simpleHeightMapMesh = GenerateHeightMapMesh(simpleHeightData, dxDevice: null); _simplePositionColorMaterial = GeneratePositionColorMaterial(simplePositionColorsArray, dxDevice: null); _disposables.Add(simpleHeightMapMesh); _disposables.Add(_simplePositionColorMaterial); GenerateHeightMapSceneNodes(simpleHeightMapMesh, _simplePositionColorMaterial); //GenerateHeightMapObject(heightData, positionColorsArray); MainDXViewportView.DXSceneInitialized += delegate(object sender, EventArgs args) { if (MainDXViewportView.DXScene == null) { return; // WPF 3D rendering } MeshBase heightMapMesh = null; _dxMaterial = null; var dxDevice = MainDXViewportView.DXScene.DXDevice; _backgroundWorker = new BackgroundWorker() { WorkerSupportsCancellation = true, WorkerReportsProgress = true }; _backgroundWorker.DoWork += delegate(object o, DoWorkEventArgs eventArgs) { // 1) // Generate height map data in the background. float[,] heightData; Color4[] positionColorsArray; //GenerateSimpleHeightData(XCount, YCount, out heightData, out positionColorsArray); //GenerateRandomHeightData(XCount, YCount, out heightData, out positionColorsArray); GenerateSinusHeightData(XCount, YCount, _backgroundWorker, out heightData, out positionColorsArray); if (_backgroundWorker.CancellationPending) { return; } // 2) // Generate the mesh object and initialize it with dxDevice // This will generate DirectX resources and send them to GPU heightMapMesh = GenerateHeightMapMesh(heightData, dxDevice); if (_backgroundWorker.CancellationPending) { return; } _backgroundWorker.ReportProgress(95); if (_dxMaterial != null) { _dxMaterial.Dispose(); } // 3) // Generate material with position color data and sent that to GPU _dxMaterial = GeneratePositionColorMaterial(positionColorsArray, dxDevice); _backgroundWorker.ReportProgress(100); }; _backgroundWorker.ProgressChanged += delegate(object o, ProgressChangedEventArgs eventArgs) { GenerationProgressBar.Value = eventArgs.ProgressPercentage; }; _backgroundWorker.RunWorkerCompleted += delegate(object o, RunWorkerCompletedEventArgs eventArgs) { // Clean and dispose existing models RootContentVisual3D.Children.Clear(); _disposables.Dispose(); // Create new DisposeList _disposables = new DisposeList(); _disposables.Add(simpleHeightMapMesh); _disposables.Add(_simplePositionColorMaterial); // Generate SceneNode with new heightMapMesh and dxMaterial. // Note that this is a very fast operation if (heightMapMesh != null && _dxMaterial != null) { GenerateHeightMapSceneNodes(heightMapMesh, _dxMaterial); } _backgroundWorker = null; GenerationProgressBar.Visibility = Visibility.Collapsed; MainDXViewportView.Refresh(); }; GenerationProgressBar.Value = 0; GenerationProgressBar.Visibility = Visibility.Visible; _backgroundWorker.RunWorkerAsync(); }; this.Unloaded += delegate(object sender, RoutedEventArgs args) { if (_backgroundWorker != null) { _backgroundWorker.CancelAsync(); } if (_dxMaterial != null) { _dxMaterial.Dispose(); _dxMaterial = null; } if (_simplePositionColorMaterial != null) { _simplePositionColorMaterial.Dispose(); _simplePositionColorMaterial = null; } _disposables.Dispose(); MainDXViewportView.Dispose(); }; }