Reputation: 4676
I can paint Model3DGroup in the color i want by using
Material material = new DiffuseMaterial(new SolidColorBrush(Colors.White));
GeometryModel3D model = new GeometryModel3D(mesh, material);
(following this tutorial)
to set the color of GeometryModel3D i include in the Model3DGroup.
I have an application where I have to place a map on a terrain in 3D (terrain is completely flat), the map is not an image, and I have detailed data of the points of the shapes I want to draw, and I also have DrawingVisual objects of all the shapes that I want to project on the 3D surface. In 2D mode, I draw them on a custom canvas (derived from Canvas) where I add them using
myCanvas.addVisualChild(item);
myCanvas.addLogicvalChild(item);
My question is how to "paint" or draw shapes and lines etc on 3D items? These shapes do not cover the terrain fully. I have tried using Viewport2DVisual3D class and tried placing a canvas on a 3D surface (a simple canvas with a button) using the following code:
Canvas testCanvas = new Canvas();
testCanvas.Background = Brushes.Green;
Button b1 = new Button();
b1.Content = "Hello there";
Canvas.SetTop(b1, 50);
Canvas.SetLeft(b1, 50);
Canvas.SetZIndex(b1, 2);
testCanvas.Children.Add(b1);
testVisual3d.Visual = testCanvas; // testVisual3d is a Viewport2DVisual3D declared in xaml
But the problem is that I am not able to figure out how the Canvas or any Visual "fills" the Viewport2DVisual3D class because:
Also i cannot use Viewport2DVisual3D everywhere, as I also have to create models of buildings etc. when I am projecting the map to a 3D terrain, so I'll have to paint areas of a building model (which will be a Model3DGroup) differently to give a realistic effects, but if i manage to project the map on a Viewport2DVisual3D, it'll solve lots of problems as i'll be able to directly project all the shapes including grid on the Viewport2DVisual3D object or terrain.
I am using .NET 4.0 and C#.
Please help.
Update
Using this code solves the initial problem of canvas size and space:
Canvas testCanvas = new Canvas();
testCanvas.Background = Brushes.Green;
Button b1 = new Button();
b1.Width = 120;
b1.Height = 25;
testCanvas.Width = 200;
testCanvas.Height = 200;
b1.Content = "Hello there";
Canvas.SetTop(b1, 50);
Canvas.SetLeft(b1, 50);
Canvas.SetZIndex(b1, 2);
testCanvas.Children.Add(b1);
testVisual3d.Visual = testCanvas;
The size of Viewport2DVisual3D is
Positions="0,0,0 0,0,30 30,0,30 30,0,0"
And the canvas resizes to fit the boundaries of the Viewport2DVisual3D, but will it work with a class derived from Canvas in which shapes have directly been added using Canvas.AddVisualChild and Canvas.AddLogicalChild, i am yet to try that.
And also the original question of painting on the Model3DGroup remains, how to do it?
Upvotes: 1
Views: 4157
Reputation: 31
So here's some code that I've used (with some modifications, adapt as necessary). This is in VB, porting to C# should be easy. Note that this is a copy-paste from some existing project, so some items may not be as clear as they could be... sorry for being lazy...
Private Sub RefreshRHDisplay()
'global/input data is RHTris which is a List(of Point3D())
'updates the data on the display with the available data
Dim v, f, i As Integer
'clear geometry
Group3D_RH.Children.Clear() 'is a Model3DGroup object
'define lights
Dim _L As Model3DGroup = GetLighting() 'defines the lighting
'mesh items
Dim mesh As New MeshGeometry3D
'group of items
Dim geomGroup As New Model3DGroup
'materials
Dim mat As MaterialGroup 'the foreground/front material
Dim bmat As MaterialGroup 'the background/back material
Dim MatGroup As New MaterialGroup
Dim ColBrush As Brush = New SolidColorBrush(_bodyCol) : ColBrush.Opacity = 1.0F
Dim LightBrush As Brush = New SolidColorBrush(Colors.White) : LightBrush.Opacity = 0.3
Dim _diffuseMaterial As New DiffuseMaterial(ColBrush)
Dim _specularMaterial As New SpecularMaterial(LightBrush, 300.0F)
MatGroup.Children.Add(_diffuseMaterial)
MatGroup.Children.Add(_specularMaterial)
mat = MatGroup
MatGroup = New MaterialGroup
Dim bColBrush As Brush = New SolidColorBrush(Colors.DarkGray) : bColBrush.Opacity = 1.0F
_diffuseMaterial = New DiffuseMaterial(bColBrush)
MatGroup.Children.Add(_diffuseMaterial)
MatGroup.Children.Add(_specularMaterial)
bmat = MatGroup
Dim vPts(0) As Point
'distinguish between direct (uniform color) and colorize mode
If chkColorize.IsChecked Then 'COLORIZE MODE
'Note: the gradient stop ends at 0.9, this is purely so that I can assign a special color after that.
Dim gsp As New GradientStopCollection({New GradientStop(Colors.Red, 0.0),
New GradientStop(Colors.Orange, 0.15),
New GradientStop(Colors.Yellow, 0.3),
New GradientStop(Colors.Green, 0.45),
New GradientStop(Colors.Blue, 0.6),
New GradientStop(Colors.Indigo, 0.75),
New GradientStop(Colors.Violet, 0.9)
})
Dim lBrsh As New LinearGradientBrush(gsp, New Point(0, 1), New Point(0.9, 1))
'define random locations for the textures
ReDim vPts(RHTris.Count - 1)
Dim rnd As New Random(CInt(Now.Ticks Mod Integer.MaxValue))
For v = 0 To RHTris.Count - 1
vPts(v) = New Point(rnd.NextDouble, 1)
Next
'replace the default material with the linear gradient
mat.Children.Clear()
mat.Children.Add(New DiffuseMaterial(lBrsh))
End If
'add all vertices
For v = 0 To RHTris.Count - 1
mesh.Positions.Add(RHTris.Item(v)(0))
mesh.Positions.Add(RHTris.Item(v)(1))
mesh.Positions.Add(RHTris.Item(v)(2))
Next
'add all triangles
v = 0
For f = 0 To RHTris.Count - 1
'If .isVisible Then 'true by def of _visList
Dim v0, v1, n As Vector3D
v0 = Point3D.Subtract(RHTris.Item(f)(1), RHTris.Item(f)(0))
v1 = Point3D.Subtract(RHTris.Item(f)(2), RHTris.Item(f)(0))
n = Vector3D.CrossProduct(v0, v1)
mesh.Normals.Add(n)
mesh.Normals.Add(n)
mesh.Normals.Add(n)
mesh.TriangleIndices.Add(v)
mesh.TriangleIndices.Add(v + 1)
mesh.TriangleIndices.Add(v + 2)
v += 3
Next
If chkColorize.IsChecked Then 'define the texture coordinates that define the color through the linear gradient brush
For v = 0 To RHTris.Count - 1
For i = 0 To 2
mesh.TextureCoordinates.Add(vPts(v)) 'add same three points for each location
Next
Next
End If
'body mesh has been defined, create body and add to visual
Dim geo As New GeometryModel3D
geo.Geometry = mesh
geo.Material = mat
geo.BackMaterial = bmat
'combine to group
geomGroup.Children.Add(geo)
'add to scene
Group3D_RH.Children.Add(geomGroup)
'add lights back in
Group3D_RH.Children.Add(_L)
End Sub
Some basic comments are in order: the data is in the RHTris collection which is a variable of type List(of Point3D()) which contains three locations. The color assigned to each is either a default material or a random color (if chkColorize is checked). The Sub also applies the data to the Group3D_RH which is a Model3DGroup object. The approach uses either a solid material or a (constant) texture that has a color with a gradient (going through all the colors that are then available). The assignment of color is then to define a point (2D point) to the texture coordinates. If each point of the triangle has the same point assigned then the triangle ends up having a uniform color (based on its coordinates along the color gradient)... Hope this at least illustrates the approach...
Upvotes: 1
Reputation: 31
This may be a bit late, but I was having issues with a very similar problem. Though the above works fine for smaller meshes, once you get into the hundreds and thousands (or more) of triangles, it becomes slow. The alternative I have implemented uses a uniform material for all meshes/collections but uses a lineargradientbrush to assign a gradient color to the material (stops and colors set as needed for what you are trying to achieve). Then by using texture coordinates, the color of a given triangle can be set (all three texture coordinates would be the same point if you want to have a solid color). This approach works far better and is more efficient for large meshes (10k and up) with only extra time required to set up the brush and assigning texture coordinates. The same/similar approach can also be used for changing the color of a triangle through hit-testing or similar whereby only the triangle's vertices (index) are provided and the source mesh (thus the index of the texture coordinates that need updating are provided)
Upvotes: 2
Reputation: 4676
After a bit of experimenting, i have the answer, simple way is to set the color of each triangle while defining the mesh. In WPF, what is needed is to set the Material color when combining the triangle (MeshGeometry3D) and material while defining a new GeometryModel3D.
(from this tutorial)
Material material = new DiffuseMaterial(
new SolidColorBrush(Colors.DarkKhaki)); //Set color here
GeometryModel3D model = new GeometryModel3D(
mesh, material);
I am a beginner of 3D and I have not yet explored the concept of materials.
If someone knows another way, please post here.
Upvotes: 0