I am trying to get some animations working where I smoothly change the material of various shapes so that they are transparent/different colors.
The issue is:
If I look into the shape from above I can see the inner corners of it (say if it's a cube I can see the inner surface of it), but anything outside/beyond the perimeter of the shape is occluded.
So far I am doing the following, which works great besides that problem:
Color c = new Color()
{
A = 16,
R = Colors.Transparent.R,
G = Colors.Transparent.G,
B = Colors.Transparent.B
};
(model as GeometryModel3D).Material = new DiffuseMaterial(new SolidColorBrush(c));
(model as GeometryModel3D).BackMaterial = new DiffuseMaterial(new SolidColorBrush(c));
If I drop the alpha of the color all the way to 0, the shape becomes opaque, seemingly because the shape is invisible but it's still occluding whatever is behind it.
The information I've found so far suggested that using emissive or specular materials would work because it didn't get written to the Z-buffer, but either diffuse materials don't work the same or I implemented that wrong.
Edit:
After coming across this question: StackOverflow, and seeing the comment under the first answer, I'm assuming being able to make objects truly transparent must be more involved than I first thought. That person seems to have had the same trouble as me.
Sounds like your surfaces might be oriented the wrong way. If so, you can fix it by reversing the order of the vertices for each element of the cube.
The standard rasterization pipeline typically draws "one-sided" primitives -- i.e., it will only draw triangles it thinks are facing the camera. That way, for instance, it doesn't even have to try to draw the back-facing sides of your cube.
One more solution that helped me is using Helix toolkit and this example helped a lot
http://helix-toolkit.github.io/demos/wpf/transparency
I imported the models from the stl files and added them as list inside of the
HelixToolkit:SortingVisual3D as Model3DGroup (from Microsoft)
an example of XAML:
<HelixToolkit:SortingVisual3D IsSorting="True" x:Name="sortingVisual1" Method="BoundingBoxCorners" SortingFrequency="2">
<ModelVisual3D Content="{Binding Model3DGroup1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<ModelVisual3D Content="{Binding Model3DGroup2, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<ModelVisual3D Content="{Binding Model3DGroup3, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<ModelVisual3D Content="{Binding Model3DGroup4, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</HelixToolkit:SortingVisual3D>
Every group is one separate .stl file
My problem was that when uploaded stl files, the outer one wouldn't let the light through for the inner models no matter the opacity
this is the function for the bound model in VM:
private readonly HelixToolkit.Wpf.ModelImporter _importer = new HelixToolkit.Wpf.ModelImporter();
var model = _importer.Load(stlPath);
var m0 = (MeshGeometry3D)((GeometryModel3D)model.Children[0]).Geometry;
var insideMaterial = MaterialHelper.CreateMaterial(outsideColor, 0.6);
return new GeometryModel3D { Geometry = m0, Material = insideMaterial };
Related
Hello! I am trying to implement a simple way to display the deformed shape of a beam. I found HelixToolkit that offers perfect tools, but I can't find the way to display different tiles of the same mesh with a different colour, or gradient. I found this: https://github.com/helix-toolkit/helix-toolkit/issues/885 that is the adding of the VertColorMaterial property, but it looks like it is for SharpDX library, but I started with HelixToolkit wpf (don't understand if in HelixToolkit is also available).
I can't even find a way to do it with SharpDX: it looks that there is almost no doc in internet.
Additionaly, SharpDX stopped its developement.
So:
do you know any example?
do you suggest me another library, which is fast/offers the ability of navigate the model, and it is compatible/use the wpf framework?
I also would like the ability to refine and subdivide a mesh.
Any kind of advice would be useful, I am new to the world of computer 3d graphic.
Thanks
EDIT 1:
I followed JonasH hint applying a texture, but it apply the texture for each tile. (See image).
I can only dinstict by out materian and in materia (set in the picture as Hue and the arrow Texture).
I need to apply one color for each polygon to give to the mesh a "FEM" style. Do you know how is it possibile with HelixToolkit?
You might consider using Kitware VTK instead of HelixToolkit. It’s extremely powerful library for scientific data visualization, well documented, perfect for finite element pre and post processing. You can take a look on my app, unfortunately it has not been documented yet, but just as an example:
https://github.com/galuszkm/STAN
I assume you have a color per vertex you want to use. I would recommend using wpf or helixToolkit wpf since they are quite easy to use. But as far as I'm aware they do not support vertex coloring.
A workaround would be to use a texture. I would assume you want to visualize some scalar property as a color. You would first need to create your MeshGeometry and assign the TextureCoordinates, simply assign the value you want to visualize to one of the texture coordinates in the 0-1 range. You would also need to create a gradient texture, either a gradientBrush or create an image. You would then assign the brush like:
var brush = new ImageBrush()
{
ImageSource = new BitmapImage(new Uri("gradient.png", UriKind.Relative))
};
var material = new DiffuseMaterial(brush);
GeometryModel3D model = new GeometryModel3D(mesh, material);
I have an OxyPlot graph, and I want to transform one of the LineSeries in this graph. I'd like to apply both rotation and translation transformations, but I can't get any kind of transformation to actually take place (I'm not including the model that holds the MyItems property, but it's MVVM and working correctly).
<oxy:Plot>
<oxy:Plot.Axes>
<oxy:LinearAxis Position="Bottom" Title="X"/>
<oxy:LinearAxis Position="Left" Title="Y"/>
</oxy:Plot.Axes>
<oxy:Plot.Series>
<oxy:LineSeries ItemsSource={Binding Path=MyItems}/>
</oxy:Plot.Series>
</oxy:Plot>
So far, I've tried playing with the LayoutTransform property in the LineSeries, like so (the X and Y values are just arbitrary for testing if I can get it to move at all):
<oxy:LineSeries ItemsSource={Binding Path=MyItems}>
<oxy:LineSeries.LayoutTransform>
<TranslateTransform X="1" Y="2"/>
</oxy:LineSeries.LayoutTransform>
</oxy:LineSeries>
However, nothing happens - the series displays exactly the same way with and without the LayoutTransform property. I've read through the documentation, and I can't find any other properties that even appear to reference transformation, and I can't find any guide on how to actually use the LayoutTransform property. Does anyone know how to achieve this, short of manually recalculating each and every point in MyItems in the code-behind?
I'm willing to create a transformation object in the code-behind, but I would much rather work with the XAML directly because I'll be using binding.
Note: The points in MyItems all have both X and Y coordinates within 0 and 1, so the problem isn't that the move is too slight for me to see.
Quick question about the Rectangle constructor in System.Windows.Shapes. I'm trying to create a Rectangle object using the following code.
Rectangle rect1 = new Rectangle(5,5,5,5);
However, I get an error saying that the Rectangle constructor doesn't take 4 arguments. After some extensive research, the documentation gives a couple explicit constructors, such as these two.
Rectangle(int32,int32, int32, int32);
and
Rectangle(Point, Size);
I have tried using both, and it accepts neither, giving the error that the Rectangle class doesn't accept either 2 or 4 arguments. However, when I don't pass it any arguments, it works fine. The issue is that there is no way to access the location of the Rectangle, so I can only change the width and height after creating the object, not the location.
Disclaimer:
This is indeed part of a homework assignment, but a very, very small piece of a larger whole, and as I have spent a lot of time on this one simple problem(so I assume), therefore, I have no qualms about asking here.
After some back and forth in comments, this is what you actually want: A UI element in a Canvas, depicting a rectangular shape.
var rect = new System.Windows.Shapes.Rectangle();
// Capital C Canvas: Static methods of System.Windows.Canvas, setting what are
// called "attached properties" in WPF. This is very weird if you're new to WPF, sorry.
Canvas.SetLeft(rect, 5);
Canvas.SetTop(rect, 5);
rect.Height = 5;
rect.Width = 5;
// canvas, lowercase c, is your instance of System.Windows.Canvas
canvas.Children.Add(rect);
With WPF, I prefer to work in XAML rather than C# for creating UI elements:
<Canvas Width="300" Height="300">
<Ellipse
Stroke="DeepSkyBlue"
Width="30"
Height="30"
Canvas.Left="40"
Canvas.Top="40"
/>
<Rectangle
Stroke="Black"
StrokeThickness="1"
Canvas.Left="20"
Canvas.Top="20"
Width="100"
Height="100"
/>
</Canvas>
Other possible answers, none applicable in this case
If you're in winforms, use System.Drawing.Rectangle instead. That's the one you were reading the documentation for (kudos for actually reading documentation -- few do).
You may possibly have to add to your project a reference to System.Drawing.dll (in References in the project tree in VS).
In WPF, you can use that class, or System.Windows.Rect. That one uses double precision floating point values instead of integers. Pick whichever is appropriate to the task at hand. UI stuff in WPF is all floating point, so Rect may be best.
The one in Shapes is something else.
If System.Windows.Shapes is in your using lines at the top, delete that if you can.
But if you need to keep that, this always works in cases of ambiguity:
var rect = new System.Drawing.Rectangle(5,5,5,5);
var r2 = new System.Windows.Rect(5,5,5,5);
In .Net there are two Rectangle classes:
System.Drawing.Rectangle(Int32,Int32,Int32,Int32);
System.Drawing.Rectangle(Point, Size);
and
System.Windows.Shapes.Rectangle();
Change the namespace as below. Add a reference to System.Drawing.dll if required.
//using System.Windows.Shapes;
using System.Drawing;
I'm trying to draw a graph-paper like grid as the background of a Canvas. This grid is different from most explanations of how to do this that I've found because the canvas can be scaled to implement zooming. What I want to do is have a series of scales of grid lines, i.e. at every 10^n units. Then, the grid lines should fade out as they become close together due to zooming. In other words if n is large, the lines associated with that grid should be darker/heavier weight than those for a smaller n.
This was easy to do in WinForms, I implemented it by overriding OnPaint and defining the color of the line to be a function of the distance to the next grid line. Lines far apart were given a heavier weight than lines close together.
I have not figured out how to do this in WPF. I can sort of get this behavior by creating a line that has a StrokeThickness according to the spacing of the grid lines, but this only works for a small range of StrokeThickness and scaling values. It would work if it were possible to define a line as having a very heavy weight, but still a small StrokeThickness.
Even doing this via implementing a custom control with OnRender is difficult because I have not found a reliable way to get the scale of the control while rendering it (the ScaleTransform is part of one of the parent controls, not the immediate parent).
Any thoughts on how to accomplish this goal would be much appreciated!
I solved this by NOT adding the grid to the canvas but by stacking the canvas on top of another control that contains the grid:
<Grid>
<Canvas x:Name="GridLayer"/>
<Canvas x:Name="DrawingLayer" />
</Grid>
When zooming events occur I simply redraw the GridLayer.
This allowed me to only draw the lines that are needed, to draw them exactly how I want them and, in my case very important because I had potentially a gazillion grid lines, I did not need to draw the lines any longer/taller than needed. This way I conserved a lot of CPU time.
Another thing to note is that I implemented my own zoom code. I did not use a RenderTransform or a ViewBox because I wanted the line to stay at the same width. All I did was keep track of the coordinates of the top left corner to support panning and the zoomlevel. As soon as one of these changes I redraw the canvases. I wrote two functions: one transforms a coordinate on the Canvas to a graph coordinate and the other one does the reverse. The first method allows me to translate cursor coordinates to graph coordinates and the second one will turn the coordinates of the graph into points that can be used to draw on the canvas.
Untested code and making a lot of assumptions about the orientation of axis:
Point Graph2Canvas(Point graphPoint)
{
var canvasPoint = new Point(graphPoint);
canvasPoint.X *= zoomLevel;
canvasPoint.Y *= zoomLevel;
canvasPoint.X -= topLeft.X;
canvasPoint.Y -= topLeft.Y;
return canvasPoint;
}
This can be optimized and the truth is I created more functions that do the same thing for collections of points.
Extra:
I ended up with a far more complex setup that looked a bit like this:
<Grid>
<Canvas x:Name="BackgroundLayer"/>
<Canvas x:Name="GridLayer"/>
<Canvas x:Name="AxisLayer"/>
<Canvas x:Name="DrawingLayer" />
<Canvas x:Name="SelectionBoxLayer"/>
<Canvas x:Name="CursorLayer"/>
</Grid>
I have a Canvas with 2 "dots" drawn on it. See this (simplified) code:
<Canvas>
<Ellipse />
<Ellipse />
<Canvas.RenderTransform>
<RotateTransform x:Name="rotateEllipse" />
</Canvas.RenderTransform>
</Canvas>
As you can see, I want to rotate the canvas using the given RotateTransform.
Next, I want to put a TextBlock near to each Ellipse (a label). However, I don't want to include this TextBlock into the Canvas because it will then rotate also. I want the text to remain horizontal.
Any idea how to solve this in an elegant way?
Something like this, should work for you
<TextBlock RenderTransform="{Binding RelativeSource={RelativeSource AncestorType=Canvas},
Path=RenderTransform.Inverse}"/>
Assign to text box transformation matrix an inverse of the transformation matrix of the Canvas.
Good question! And I'm going to guess, so please take this answer with a pinch of salt.
I believe you are trying to place text annotations next to ellipses on a rotated canvas, but these annotations need to remain horizontal. Two things you could try:
Firstly, given the XY point that you know of each ellipse from Canvas.GetTop/GetLeft, you could find its new rotated XY location by applying the RotateTransform to the ellipse location, using the formula U = M*V, where U is the output point, V is the input point (XY location of ellipse) and M is the Rotation Matrix.
Secondly, you could place a second canvas over the first (assuming they are both in a grid, the second canvas is at higher Z-index and is the same size as the underlying canvas). Call it an annotation layer. Your annotations (text labels) can appear at the new transformed locations and unrotated using this approach.
You'd have to do this in code of course, not Xaml, although you might find a binding friendly approach by creating a value converter on the TextBlock that bound to the source RotateTransform/Ellipse and did this operation for you.
Another approach would be to take the .Inverse of the RotateTransform and apply that to the textblocks, however you may still need to translate to get to the new location. Either way I think it demands some experimentation.
Best regards,