//private void GenerateHeightMapObject(float[,] heightData, Color4[] positionColorsArray) private void GenerateHeightMapSceneNodes(MeshBase heightMapMesh, Ab3d.DirectX.Material dxMaterial) { var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(heightMapMesh, dxMaterial); meshObjectNode.Name = "HeightMeshObjectNode"; _disposables.Add(meshObjectNode); var sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); RootContentVisual3D.Children.Add(sceneNodeVisual3D); // If you also want to render back faces of the height map you need to create another MeshObjectNode and set its IsBackFaceMaterial to true. // You can reuse the mesh. But this still requires almost twice the GPU power. var backDiffuseMaterial = new DiffuseMaterial(Brushes.Gray); var backDXMaterial = new Ab3d.DirectX.Materials.WpfMaterial(backDiffuseMaterial); meshObjectNode = new Ab3d.DirectX.MeshObjectNode(heightMapMesh, backDXMaterial); meshObjectNode.IsBackFaceMaterial = true; meshObjectNode.Name = "HeightBackMeshObjectNode"; _disposables.Add(meshObjectNode); sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); RootContentVisual3D.Children.Add(sceneNodeVisual3D); }
public static SceneNode CreateSceneNodes(MeshGeometry3D mesh, Point3D center, Size3D size, float modelScaleFactor, int xCount, int yCount, int zCount) { var rootSceneNode = new SceneNode(); var dxMeshGeometry3D = new DXMeshGeometry3D(mesh); float xStep = (float)(size.X / xCount); float yStep = (float)(size.Y / yCount); float zStep = (float)(size.Z / zCount); for (int z = 0; z < zCount; z++) { float zPos = (float)(center.Z - (size.Z / 2.0) + (z * zStep)); float zPercent = (float)z / (float)zCount; for (int y = 0; y < yCount; y++) { float yPos = (float)(center.Y - (size.Y / 2.0) + (y * yStep)); float yPercent = (float)y / (float)yCount; for (int x = 0; x < xCount; x++) { float xPos = (float)(center.X - (size.X / 2.0) + (x * xStep)); var matrix = new SharpDX.Matrix(modelScaleFactor, 0, 0, 0, 0, modelScaleFactor, 0, 0, 0, 0, modelScaleFactor, 0, xPos, yPos, zPos, 1); var standardMaterial = new StandardMaterial() { DiffuseColor = new Color3((float)x / (float)xCount, yPercent, zPercent) }; var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(dxMeshGeometry3D, standardMaterial); meshObjectNode.Transform = new Transformation(matrix); rootSceneNode.AddChild(meshObjectNode); } } } return(rootSceneNode); }
// 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(); } }
private void CreateScene() { // IMPORTANT: // Before the Form is closed, we need to dispose all the DXEngine objects that we created (all that implement IDisposable). // This means that all materials, Mesh objects and SceneNodes need to be disposed. // To make this easier, we can use the DisposeList collection that will hold IDisposable objects. _disposables = new DisposeList(); // // 1) // // The easiest way to add 3D models to DXEngine's scene is to add WPF's Visual3D objects to Viewport3D.Children collection: var pyramidVisual3D = new Ab3d.Visuals.PyramidVisual3D() { BottomCenterPosition = new Point3D(-100, 0, 0), Size = new Size3D(80, 50, 80), Material = new DiffuseMaterial(Brushes.Blue) }; pyramidVisual3D.SetName("PyramidVisual3D"); MainViewport.Children.Add(pyramidVisual3D); // We could also start from PyramidMesh3D and then create GeometryModel3D and ModelVisual3D //var pyramidMeshGeometry3D = new Ab3d.Meshes.PyramidMesh3D(new Point3D(100, 0, 0), new Size3D(80, 50, 80)).Geometry; //if (pyramidMeshGeometry3D.Normals.Count == 0) // pyramidMeshGeometry3D.Normals = Ab3d.Utilities.MeshUtils.CalculateNormals(pyramidMeshGeometry3D); //var geometryModel3D = new GeometryModel3D(pyramidMeshGeometry3D, diffuseMaterial); //var modelVisual3D = new ModelVisual3D() //{ // Content = geometryModel3D //}; //MainViewport.Children.Add(modelVisual3D); // DXEngine internally converts WPF objects into SceneNodes. // You can get the string that describes the SceneNodes with opening Visual Studio Immediate Window and execting the following: // MainDXViewportView.DXScene.DumpSceneNodes(); // // Usually this is the best was to define the 3D scene. // // But if you have very complex objects with a lot of positions, it might be good to create the SceneNodes manually. // This allows faster initialization because WPF 3D objects are not created. // Also all the memory used by WPF 3D objects can be freed. // // Because WPF uses double type for Point3D and Vector3D types instead of float as in DirectX and DXEngine, // the memory size required for a 3D objects in WPF is almost twice the size of what is required in DXEngine. // // For example if your object has 100.000 positions, the the memory requirements are the following: // // In WPF: // Positions: 100.000 * 3 (x,y,z) * 8 (8 bytes for one double value) = 2.400.000 bytes // Normals: 100.000 * 3 (x,y,z) * 8 (8 bytes for one double value) = 2.400.000 bytes // Texture coordinates: 100.000 * 2 (u,y) * 8 (8 bytes for one double value) = 1.600.000 bytes // Triangle indices: 100.000 * 4 (4 bytes for one Int32) = 400.000 bytes (the actual number of triangle indices may be different - depends on how many positions are shared between triangles) // TOTAL: = 6.800.000 bytes = 6.7 MB // // In DXEngine: // Positions: 100.000 * 3 (x,y,z) * 4 (4 bytes for one float value) = 1.200.000 bytes // Normals: 100.000 * 3 (x,y,z) * 4 (4 bytes for one float value) = 1.200.000 bytes // Texture coordinates: 100.000 * 2 (u,y) * 4 (4 bytes for one float value) = 800.000 bytes // Triangle indices: 100.000 * 4 (4 bytes for one Int32) = 400.000 bytes // TOTAL: = 3.600.000 bytes = 3.5 MB // // Usually both objects need to be initialized (takes CPU time) and are stored in memory. // // // When the DXEngine's SceneNodes are manually created, the WPF objects can be cleared from memory // or event the SceneNodes can be created without the intermediate WPF objects. // // One the SceneNode is created it can be added to the scene with using SceneNodeVisual3D. // This is a Visual3D and can be added to the Viewport3D.Children collection. // The object also provides a way to add Transformation to the SceneNode. // // A disadvantage of creating SceneNodes is that such objects cannot be shown when WPF 3D rendering is used (for example in case when DXEngine falls back to WPF 3D rendering because of problems with DirectX initialization). // Another disadvantage is that it is more complicated to create and modify SceneNodes. // // Usually, when memory usage is not problematic, it is better to use standard WPF 3D objects. // // 2) // // Create MeshObjectNode from GeometryMesh with providing arrays (IList<T>) for positions, normals, textureCoordinates and triangleIndices: Vector3[] positions; Vector3[] normals; Vector2[] textureCoordinates; int[] triangleIndices; // Get Pyramid mesh data GetObjectDataArrays(out positions, out normals, out textureCoordinates, out triangleIndices); // The easiest way to create DXEngine's material is to use Ab3d.DirectX.Materials.WpfMaterial that takes a WPF material and converts it into DXEngine's material var diffuseMaterial = new DiffuseMaterial(Brushes.Green); var dxMaterial = new Ab3d.DirectX.Materials.WpfMaterial(diffuseMaterial); _disposables.Add(dxMaterial); // Create SceneNode // First create GeometryMesh object from the mesh arrays var geometryMesh = new Ab3d.DirectX.GeometryMesh(positions, normals, textureCoordinates, triangleIndices, "PyramidMesh3D"); _disposables.Add(geometryMesh); // NOTE: // We could also create GeometryMesh from WPF's MeshGeometry with help from DXMeshGeometry3D: //var wpfPyramidMesh = new Meshes.PyramidMesh3D(bottomCenterPosition: new System.Windows.Media.Media3D.Point3D(0, 0, 0), // size: new System.Windows.Media.Media3D.Size3D(30, 20, 10)); //var geometryMesh = new Ab3d.DirectX.Models.DXMeshGeometry3D(wpfPyramidMesh.Geometry, "PyramidMesh"); // Use GeometryMesh to create MeshObjectNode (SceneNode from GeometryMesh object) var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(geometryMesh, dxMaterial); meshObjectNode.Name = "Green-MeshObjectNode-from-GeometryMesh"; _disposables.Add(meshObjectNode); // Use SceneNodeVisual3D to show SceneNode in DXViewportView var sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); //sceneNodeVisual3D.Transform = new TranslateTransform3D(0, 0, 0); MainViewport.Children.Add(sceneNodeVisual3D); // // 3) // // Create MeshObjectNode from SimpleMesh<T> with providing VertexBufferArray and IndexBufferArray: // This option provides faster initialization, because the VertexBufferArray is already generated and it can be directly used to create DirectX vertex buffer. // In the previous sample the VertexBufferArray was generated in the GeometryMesh from positions, normals, textureCoordinates arrays. // // If you can store your 3D models in disk (or some other location) in a form of VertexBuffer and IndexBuffer, // then this is the fastes way to initialize 3D objects. // // 3a) // // The standard way to create a SimpleMesh is to use the PositionNormalTexture or some other struct that defines the data for one array: PositionNormalTexture[] vertexBuffer; int[] indexBuffer; GetVertexAndIndexBuffer(out vertexBuffer, out indexBuffer); var simpleMesh = new SimpleMesh <PositionNormalTexture>(vertexBuffer, indexBuffer, inputLayoutType: InputLayoutType.Position | InputLayoutType.Normal | InputLayoutType.TextureCoordinate, name: "SimpleMesh-from-PositionNormalTexture-array"); _disposables.Add(simpleMesh); diffuseMaterial = new DiffuseMaterial(Brushes.Red); dxMaterial = new Ab3d.DirectX.Materials.WpfMaterial(diffuseMaterial); _disposables.Add(dxMaterial); _redPyramidObjectNode = new Ab3d.DirectX.MeshObjectNode(simpleMesh, dxMaterial); _redPyramidObjectNode.Name = "Red-MeshObjectNode-from-SimpleMesh"; _disposables.Add(_redPyramidObjectNode); sceneNodeVisual3D = new SceneNodeVisual3D(_redPyramidObjectNode); sceneNodeVisual3D.Transform = new TranslateTransform3D(100, 0, 0); MainViewport.Children.Add(sceneNodeVisual3D); // // 3b) // // It is also possible to create SimpleMesh with a base type - for example float (for example if we read data from file). // In this case we need to set the ArrayStride property. // // A drawback of using a non-standard vertex buffer (Vector3, PositionNormalTexture, PositionNormal or PositionTexture) // is that such mesh does not support hit testing. // In this sample this is demonstrated with camera rotation around mouse hit object - it is not possible to rotate around SimpleMesh<float>. float[] floatVertexBuffer; GetFloatVertexAndIndexBuffer(out floatVertexBuffer, out indexBuffer); var floatSimpleMesh = new SimpleMesh <float>(floatVertexBuffer, indexBuffer, inputLayoutType: InputLayoutType.Position | InputLayoutType.Normal | InputLayoutType.TextureCoordinate, name: "SimpleMesh-from-float-array"); _disposables.Add(floatSimpleMesh); // IMPORTANT: // When we do not use PositionNormalTexture or PositionNormal, the DXEngine cannot calculate Bounds of the SimpleMesh for us. // In this case we need to calculate and specify Bounds manually: // Defined bounds for the following mesh: new Ab3d.Meshes.PyramidMesh3D(new Point3D(0, 0, 0), new Size3D(80, 50, 80)) floatSimpleMesh.Bounds = new Bounds(new BoundingBox(minimum: new Vector3(-40, -25, -40), maximum: new Vector3(40, 25, 40))); // Because we created SimpleMesh with a base type (float), // we need to specify how many array elements define one Vertex. // This is 8 in our case: 3 (position x,y,z) + 3 (normal x,y,z) + 2 (texture coordinate u,v) = 8 floatSimpleMesh.ArrayStride = 8; diffuseMaterial = new DiffuseMaterial(Brushes.Orange); dxMaterial = new Ab3d.DirectX.Materials.WpfMaterial(diffuseMaterial); _disposables.Add(dxMaterial); _orangePyramidObjectNode = new Ab3d.DirectX.MeshObjectNode(floatSimpleMesh, dxMaterial); _orangePyramidObjectNode.Name = "Orange-MeshObjectNode-from-FloatSimpleMesh"; _disposables.Add(_orangePyramidObjectNode); sceneNodeVisual3D = new SceneNodeVisual3D(_orangePyramidObjectNode); sceneNodeVisual3D.Transform = new TranslateTransform3D(200, 0, 0); MainViewport.Children.Add(sceneNodeVisual3D); // // 3c) // // Instead of float array elements, it is also possible to use byte array to create SimpleMesh. // // As before, a drawback of using a non-standard vertex buffer (Vector3, PositionNormalTexture, PositionNormal or PositionTexture) // is that such mesh does not support hit testing. // In this sample this is demonstrated with camera rotation around mouse hit object - it is not possible to rotate around SimpleMesh<float>. byte[] byteVertexBuffer; GetByteVertexAndIndexBuffer(out byteVertexBuffer, out indexBuffer); var byteSimpleMesh = new SimpleMesh <byte>(byteVertexBuffer, indexBuffer, inputLayoutType: InputLayoutType.Position | InputLayoutType.Normal | InputLayoutType.TextureCoordinate, name: "SimpleMesh-from-byte-array"); _disposables.Add(byteSimpleMesh); // IMPORTANT: // When we do not use PositionNormalTexture or PositionNormal, the DXEngine cannot calculate Bounds of the SimpleMesh for us. // In this case we need to calculate and specify Bounds manually: // Defined bounds for the following mesh: new Ab3d.Meshes.PyramidMesh3D(new Point3D(0, 0, 0), new Size3D(80, 50, 80)) byteSimpleMesh.Bounds = new Bounds(new BoundingBox(minimum: new Vector3(-40, -25, -40), maximum: new Vector3(40, 25, 40))); // Because we created SimpleMesh with a base type (byte), // we need to specify how many array elements define one Vertex. // This is 32 in our case: 8 (8x float value) * 4 (4 bytes for one float) = 32 byteSimpleMesh.ArrayStride = 32; diffuseMaterial = new DiffuseMaterial(Brushes.Yellow); dxMaterial = new Ab3d.DirectX.Materials.WpfMaterial(diffuseMaterial); _disposables.Add(dxMaterial); meshObjectNode = new Ab3d.DirectX.MeshObjectNode(byteSimpleMesh, dxMaterial); meshObjectNode.Name = "Yellow-MeshObjectNode-from-ByteSimpleMesh"; _disposables.Add(meshObjectNode); sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); sceneNodeVisual3D.Transform = new TranslateTransform3D(300, 0, 0); MainViewport.Children.Add(sceneNodeVisual3D); // // 4) // // When a frozen Model3DGroup is added to the DXViewportView, it is converted into the WpfOptimizedModel3DGroupNode (derived from SceneNode). // In this case both WPF and DXEngine's 3D objects data are stored in memory. // // To release the WPF 3D objects data, it is possible to create the WpfOptimizedModel3DGroupNode manually and // then clear the used WPF 3D objects. // This can be done with setting the AutomaticallyClearWpfObjectsAfterInitialization property on WpfOptimizedModel3DGroupNode to true, // or by calling the ClearWpfObjects method on WpfOptimizedModel3DGroupNode. string dragonModelFileName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources\\Models\\dragon_vrip_res3.obj"); var readerObj = new Ab3d.ReaderObj(); Model3D readModel3D = readerObj.ReadModel3D(dragonModelFileName); double scale = 100 / readModel3D.Bounds.SizeX; // Scale the model to 100 SizeX readModel3D.Transform = new ScaleTransform3D(scale, scale, scale); var model3DGroup = readModel3D as Model3DGroup; if (model3DGroup == null) { model3DGroup = new Model3DGroup(); model3DGroup.Children.Add(readModel3D); } model3DGroup.Freeze(); var wpfOptimizedModel3DGroupNode = new Ab3d.DirectX.Models.WpfOptimizedModel3DGroupNode(model3DGroup, name: "Frozen Model3DGroup"); wpfOptimizedModel3DGroupNode.AutomaticallyClearWpfObjectsAfterInitialization = true; // This will clear the WPF 3D models that are referenced by WpfOptimizedModel3DGroupNode when the DirectX objects are created _disposables.Add(wpfOptimizedModel3DGroupNode); sceneNodeVisual3D = new SceneNodeVisual3D(wpfOptimizedModel3DGroupNode); sceneNodeVisual3D.Transform = new TranslateTransform3D(-100, -20, -100); MainViewport.Children.Add(sceneNodeVisual3D); // // 5) // // The following code shows how to load texture with using TextureLoader if (MainDXViewportView.DXScene != null) { var planeGeometry3D = new Ab3d.Meshes.PlaneMesh3D(new Point3D(0, 0, 0), new Vector3D(0, 1, 0), new Vector3D(1, 0, 0), new Size(80, 80), 1, 1).Geometry; var dxMeshGeometry3D = new DXMeshGeometry3D(planeGeometry3D); _disposables.Add(dxMeshGeometry3D); // Load texture file into ShaderResourceView (in our case we load dds file; but we could also load png file) string textureFileName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources/ab4d-logo-220x220.dds"); // The easiest way to load image file and in the same time create a material with the loaded texture is to use the CreateStandardTextureMaterial method. var standardMaterial = Ab3d.DirectX.TextureLoader.CreateStandardTextureMaterial(MainDXViewportView.DXScene.DXDevice, textureFileName); // We need to manually dispose the created StandardMaterial and ShaderResourceView _disposables.Add(standardMaterial); _disposables.Add(standardMaterial.DiffuseTextures[0]); // If we want more control over the material creation process, we can use the following code: //// To load a texture from file, you can use the TextureLoader.LoadShaderResourceView (this supports loading standard image files and also loading dds files). //// This method returns a ShaderResourceView and it can also set a textureInfo parameter that defines some of the properties of the loaded texture (bitmap size, dpi, format, hasTransparency). //TextureInfo textureInfo; //var loadedShaderResourceView = Ab3d.DirectX.TextureLoader.LoadShaderResourceView(MainDXViewportView.DXScene.Device, // textureFileName, // out textureInfo); //_disposables.Add(loadedShaderResourceView); //// Get recommended BlendState based on HasTransparency and HasPreMultipliedAlpha values. //// Possible values are: CommonStates.Opaque, CommonStates.PremultipliedAlphaBlend or CommonStates.NonPremultipliedAlphaBlend. //var recommendedBlendState = MainDXViewportView.DXScene.DXDevice.CommonStates.GetRecommendedBlendState(textureInfo.HasTransparency, textureInfo.HasPremultipliedAlpha); //// Now we can create a DXEngine's StandardMaterial //var standardMaterial = new StandardMaterial() //{ // // Set ShaderResourceView into array of diffuse textures // DiffuseTextures = new ShaderResourceView[] {loadedShaderResourceView}, // TextureBlendState = recommendedBlendState, // HasTransparency = textureInfo.HasTransparency, // // When showing texture, the DiffuseColor represents a color mask - each color from texture is multiplied with DiffuseColor (White preserves the original color) // DiffuseColor = Colors.White.ToColor3() //}; //_disposables.Add(standardMaterial); meshObjectNode = new Ab3d.DirectX.MeshObjectNode(dxMeshGeometry3D, standardMaterial); meshObjectNode.Name = "MeshObjectNode-from-PlaneMesh3D"; _disposables.Add(meshObjectNode); sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); sceneNodeVisual3D.Transform = new TranslateTransform3D(0, 0, 100); MainViewport.Children.Add(sceneNodeVisual3D); } // Add PointLight var pointLight = new PointLight(Colors.White, new Point3D(100, 500, 0)); MainViewport.Children.Add(pointLight.CreateModelVisual3D()); Camera1.ShowCameraLight = ShowCameraLightType.Never; }
private void GenerateHeightMapObject(float[,] heightData, Color4[] positionColorsArray) { PositionNormalTexture[] vertexBuffer; int[] indexBuffer; CreateHeightVertexAndIndexBuffer(heightData, centerPosition: new Vector3(0, 0, 0), size: new Vector3(1000, 20, 200), vertexBuffer: out vertexBuffer, indexBuffer: out indexBuffer); var simpleMesh = new SimpleMesh <PositionNormalTexture>(vertexBuffer, indexBuffer, inputLayoutType: InputLayoutType.Position | InputLayoutType.Normal | InputLayoutType.TextureCoordinate, name: "HeightSimpleMesh"); _disposables.Add(simpleMesh); Ab3d.DirectX.Material dxMaterial; if (positionColorsArray != null) { dxMaterial = new Ab3d.DirectX.Materials.VertexColorMaterial() { PositionColors = positionColorsArray, // The PositionColors property is used to specify colors for each vertex CreateDynamicBuffer = false, // We will not update the colors frequently // To show specular effect set the specular data here: //SpecularPower = 16, //SpecularColor = Color3.White, //HasSpecularColor = true }; } else { // Solid color material: var diffuseMaterial = new DiffuseMaterial(Brushes.Green); // Texture material: //var imageBrush = new ImageBrush(); //imageBrush.ImageSource = new BitmapImage(new Uri("pack://application:,,,/Resources/GrassTexture.jpg")); //var diffuseMaterial = new DiffuseMaterial(imageBrush); dxMaterial = new Ab3d.DirectX.Materials.WpfMaterial(diffuseMaterial); } _disposables.Add(dxMaterial); var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(simpleMesh, dxMaterial); meshObjectNode.Name = "HeightMeshObjectNode"; _disposables.Add(meshObjectNode); var sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); MainViewport.Children.Add(sceneNodeVisual3D); // If you also want to render back faces of the height map you need to create another MeshObjectNode and set its IsBackFaceMaterial to true. // You can reuse the mesh. But this still requires almost twice the GPU power. var backDiffuseMaterial = new DiffuseMaterial(Brushes.Gray); var backDXMaterial = new Ab3d.DirectX.Materials.WpfMaterial(backDiffuseMaterial); meshObjectNode = new Ab3d.DirectX.MeshObjectNode(simpleMesh, backDXMaterial); meshObjectNode.IsBackFaceMaterial = true; meshObjectNode.Name = "HeightBackMeshObjectNode"; _disposables.Add(meshObjectNode); sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); MainViewport.Children.Add(sceneNodeVisual3D); }
public static SceneNode CreateBoxSceneNodes(MeshGeometry3D mesh, Point3D center, Size3D size, float modelScaleFactor, bool useSingleColor, int xCount, int yCount, int zCount) { var rootSceneNode = new SceneNode(); var dxMeshGeometry3D = new DXMeshGeometry3D(mesh); float xStep = (float)(size.X / xCount); float yStep = (float)(size.Y / yCount); float zStep = (float)(size.Z / zCount); var singleColorStandardMaterial = new StandardMaterial() { DiffuseColor = Colors.Orange.ToColor3() }; for (int z = 0; z < zCount; z++) { float zPos = (float)(center.Z - (size.Z / 2.0) + (z * zStep)); float zPercent = (float)z / (float)zCount; for (int y = 0; y < yCount; y++) { float yPos = (float)(center.Y - (size.Y / 2.0) + (y * yStep)); float yPercent = (float)y / (float)yCount; for (int x = 0; x < xCount; x++) { float xPos = (float)(center.X - (size.X / 2.0) + (x * xStep)); var matrix = new SharpDX.Matrix(modelScaleFactor, 0, 0, 0, 0, modelScaleFactor, 0, 0, 0, 0, modelScaleFactor, 0, xPos, yPos, zPos, 1); StandardMaterial usedMaterial; if (useSingleColor) { // Using single color does not improve performance because for each rendered box we need to // update constant buffer with different transformation matrix. // However, the performance would be improved by almost 30% if all the boxes would have the // same color and transformation. But then we would need to provide many different meshes // and this would greatly decrease initialization time and also slightly decrease performance. // See CreateLineSceneNodes for sample with lines when single color improves performance. usedMaterial = singleColorStandardMaterial; } else { usedMaterial = new StandardMaterial() { DiffuseColor = new Color3((float)x / (float)xCount, yPercent, zPercent) }; } // NOTE: // This sample shows many 3D boxes that are defined by MeshObjectNode objects. // A much more optimal way to show that many 3D boxes is to use object instancing with InstancedMeshGeometryVisual3D, // but in this sample we need to simulate rendering of a very complex scene with many different SceneNodes objects (each with its own MeshGeometry3D). var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(dxMeshGeometry3D, usedMaterial); meshObjectNode.Transform = new Transformation(matrix); rootSceneNode.AddChild(meshObjectNode); } } } return(rootSceneNode); }
private void CreateTestModels() { _rootModelVisual3D = new ModelVisual3D(); MainViewport3D.Children.Add(_rootModelVisual3D); // SphereVisual3D var sphereVisual3D = new Ab3d.Visuals.SphereVisual3D() { CenterPosition = new Point3D(-50, 0, -50), Radius = 30, Material = new DiffuseMaterial(Brushes.Silver) }; sphereVisual3D.SetName("SphereVisual3D"); _rootModelVisual3D.Children.Add(sphereVisual3D); var readerObj = new ReaderObj(); var teapotModel = readerObj.ReadModel3D(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Models\teapot-hires.obj")); Ab3d.Utilities.ModelUtils.CenterAndScaleModel3D(teapotModel, centerPosition: new Point3D(50, 0, -50), finalSize: new Size3D(80, 80, 80), preserveAspectRatio: true); var modelVisual3D = new ModelVisual3D() { Content = teapotModel }; teapotModel.SetName("teapot Model3D"); modelVisual3D.SetName("teapot ModelVisual3D"); _rootModelVisual3D.Children.Add(modelVisual3D); // InstancedMeshGeometryVisual3D var boxMesh3D = new Ab3d.Meshes.BoxMesh3D(new Point3D(0, 0, 0), new Size3D(6, 6, 6), 1, 1, 1); InstanceData[] instancedData = DXEnginePerformance.InstancedMeshGeometry3DTest.CreateInstancesData(center: new Point3D(-50, 0, 50), size: new Size3D(80, 50, 80), modelScaleFactor: 1, xCount: 5, yCount: 1, zCount: 5, useTransparency: false); var instancedMeshGeometryVisual3D = new InstancedMeshGeometryVisual3D(boxMesh3D.Geometry); instancedMeshGeometryVisual3D.InstancesData = instancedData; instancedMeshGeometryVisual3D.SetName("InstancedMeshGeometryVisual3D"); _rootModelVisual3D.Children.Add(instancedMeshGeometryVisual3D); // MeshObjectNode and SceneNodeVisual3D var meshGeometry3D = new Ab3d.Meshes.PyramidMesh3D(new Point3D(50, -20, 50), new Size3D(80, 40, 80)).Geometry; var dxMeshGeometry3D = new Ab3d.DirectX.Models.DXMeshGeometry3D(meshGeometry3D); var standardMaterial = new StandardMaterial() { DiffuseColor = Colors.Gold.ToColor3() }; var meshObjectNode = new Ab3d.DirectX.MeshObjectNode(dxMeshGeometry3D, standardMaterial); _disposables.Add(dxMeshGeometry3D); _disposables.Add(meshObjectNode); var sceneNodeVisual3D = new SceneNodeVisual3D(meshObjectNode); sceneNodeVisual3D.SetName("SceneNodeVisual3D"); _rootModelVisual3D.Children.Add(sceneNodeVisual3D); }