I'm trying to use C# and WPF to render two triangle mesh objects with different colors, and I can't quite figure out how to make it work.
If I set numObjects to 1, it will display a single red triangle as it should.
But, when I set numObjects to 2, the first red triangle is not displayed and only the 2nd green triangle is displayed.
What am I doing wrong here?
Here's my code:
using System.Windows.Media.Media3D;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
// Declare scene objects.
Viewport3D myViewport3D = new Viewport3D();
Model3DGroup myModel3DGroup = new Model3DGroup();
GeometryModel3D myGeometryModel = new GeometryModel3D();
ModelVisual3D myModelVisual3D = new ModelVisual3D();
// Defines the camera used to view the 3D object. In order to view the 3D object,
// the camera must be positioned and pointed such that the object is within view
// of the camera.
PerspectiveCamera myPCamera = new PerspectiveCamera();
public MainWindow()
{
InitializeComponent();
// Specify where in the 3D scene the camera is.
myPCamera.Position = new Point3D(0, 0, 5);
// Specify the direction that the camera is pointing.
myPCamera.LookDirection = new Vector3D(0, 0, -1);
// Define camera's horizontal field of view in degrees.
myPCamera.FieldOfView = 60;
// Asign the camera to the viewport
myViewport3D.Camera = myPCamera;
// Define the lights cast in the scene. Without light, the 3D object cannot
// be seen. Note: to illuminate an object from additional directions, create
// additional lights.
DirectionalLight myDirectionalLight = new DirectionalLight();
myDirectionalLight.Color = Colors.White;
myDirectionalLight.Direction = new Vector3D(-0.61, -0.5, -0.61);
myModel3DGroup.Children.Add(myDirectionalLight);
int numObjects = 2;
for(int i = 0; i < numObjects; i++)
{
BuildObject(i);
}
// Add the group of models to the ModelVisual3d.
myModelVisual3D.Content = myModel3DGroup;
//
myViewport3D.Children.Add(myModelVisual3D);
// Apply the viewport to the page so it will be rendered.
this.Content = myViewport3D;
}
private void BuildObject(int i)
{
// The geometry specifes the shape of the 3D plane. In this sample, a flat sheet
// is created.
MeshGeometry3D myMeshGeometry3D = new MeshGeometry3D();
// Create a collection of normal vectors for the MeshGeometry3D.
Vector3DCollection myNormalCollection = new Vector3DCollection();
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myMeshGeometry3D.Normals = myNormalCollection;
double basex = 0 + i * 1;
// Create a collection of vertex positions for the MeshGeometry3D.
Point3DCollection myPositionCollection = new Point3DCollection();
myPositionCollection.Add(new Point3D(basex + -0.5, -0.5, 0.5));
myPositionCollection.Add(new Point3D(basex + 0.5, -0.5, 0.5));
myPositionCollection.Add(new Point3D(basex + 0.5, 0.5, 0.5));
myMeshGeometry3D.Positions = myPositionCollection;
// Create a collection of texture coordinates for the MeshGeometry3D.
PointCollection myTextureCoordinatesCollection = new PointCollection();
myTextureCoordinatesCollection.Add(new Point(0, 0));
myTextureCoordinatesCollection.Add(new Point(1, 0));
myTextureCoordinatesCollection.Add(new Point(1, 1));
myMeshGeometry3D.TextureCoordinates = myTextureCoordinatesCollection;
// Create a collection of triangle indices for the MeshGeometry3D.
Int32Collection myTriangleIndicesCollection = new Int32Collection();
myTriangleIndicesCollection.Add(0);
myTriangleIndicesCollection.Add(1);
myTriangleIndicesCollection.Add(2);
myMeshGeometry3D.TriangleIndices = myTriangleIndicesCollection;
// Apply the mesh to the geometry model.
myGeometryModel.Geometry = myMeshGeometry3D;
// The material specifies the material applied to the 3D object. In this sample a
// linear gradient covers the surface of the 3D object.
Color color = Color.FromArgb(255, 255, 0, 0);
if(i == 1)
{
color = Color.FromArgb(255, 0, 255, 0);
}
SolidColorBrush solid_brush = new SolidColorBrush(color);
DiffuseMaterial solid_material = new DiffuseMaterial(solid_brush);
myGeometryModel.Material = solid_material;
// Add the geometry model to the model group.
myModel3DGroup.Children.Add(myGeometryModel);
Console.WriteLine(myGeometryModel.ToString());
}
}
}
You instantiate and work only with a single GeometryModel3D object (referenced by the field myGeometryModel). So, the data of the green triangle essentially replaces the red triangle data in myGeometryModel.
To fix the issue, delete the myGeometryModel field, and create a GeometryModel3D object for each triangle within the BuildObject method:
public partial class MainWindow : Window
{
Viewport3D myViewport3D = new Viewport3D();
Model3DGroup myModel3DGroup = new Model3DGroup();
ModelVisual3D myModelVisual3D = new ModelVisual3D();
PerspectiveCamera myPCamera = new PerspectiveCamera();
...
private void BuildObject(int i)
{
var myGeometryModel = new GeometryModel3D();
...
}
}
Related
I have a very simply scene (camera, light set up as usual). I want to apply a simple image material over the model. If I use one of the convenience models like Sphere, the object renders with the material all right:
var model = modelNode.CreateComponent<Sphere>();
model.SetMaterial(Material.FromImage("Textures/small.jpg"));
If I switch to a static model (it's the default cube from Blender), nothing renders (or probably, it renders invisible). With a color material, it works all right, so there is no question about the model itself.
var model = modelNode.CreateComponent<StaticModel>();
model.Model = ResourceCache.GetModel("Models/Cube.mdl", false);
//model.SetMaterial(CoreAssets.Materials.DefaultGrey);
//model.SetMaterial(Material.FromColor(Color.Yellow));
model.SetMaterial(Material.FromImage("Textures/small.jpg"));
For reference, the rest of the scene is:
scene = new Scene();
octree = scene.CreateComponent<Octree>();
var cameraNode = scene.CreateChild();
cameraNode.Position = new Vector3(0, 0, -10);
cameraNode.SetDirection(new Vector3(0, 0, 0));
camera = cameraNode.CreateComponent<Camera>();
var lightNode = cameraNode.CreateChild();
lightNode.Position = new Vector3(5, 5, -5);
lightNode.SetDirection(new Vector3(0, 0, 0));
var light = lightNode.CreateComponent<Light>();
light.LightType = LightType.Directional;
light.Brightness = 1.5f;
light.CastShadows = true;
light.Color = Color.White;
light.Range = 10;
Well, the model didn't have all the necessary details exported...
A few days into 3d models and I can't figure out how to move an axis so I can start my rotations. for example.... I want to tilt X at 30 degrees and then I will be lined up to do each axis rotations. If I rotate X to 30 degrees it throws off Y and Z rotations. Any examples of how to do this in code behind? The closest example I found was in the Manipulator demo but it doesn't show the code on how the manipulator works.
private Transform3DGroup GetTransforms(Model3D model)
{
var transforms = new Transform3DGroup();
// Rotation around X
transforms.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0)));
// Rotation around Y
transforms.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0)));
// Rotation around Z
transforms.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0)));
// Translate transform (if required)
transforms.Children.Add(new TranslateTransform3D());
model.Transform = transforms;
return transforms;
}
private void SetRotation(double amountX, double amountY, double amountZ, Model3D model, Point3D center)
{
// Suppose we have a function that gives us all the transforms
// applied to this object
var transforms = GetTransforms(model);
var translation = transforms.Children[3];
// Suppose GetCenter() obtains the center point of an object
// in Point3D format
var translatedCenter = translation.Transform(center);
if (!(transforms.Children[0] is RotateTransform3D rotX)) throw new ArgumentNullException(nameof(rotX));
if (!(transforms.Children[1] is RotateTransform3D rotY)) throw new ArgumentNullException(nameof(rotY));
if (!(transforms.Children[2] is RotateTransform3D rotZ)) throw new ArgumentNullException(nameof(rotZ));
// Set the center point of transformation to the translated center of the object
rotX.CenterX = rotY.CenterX = rotZ.CenterX = translatedCenter.X;
rotX.CenterY = rotY.CenterY = rotZ.CenterY = translatedCenter.Y;
rotX.CenterZ = rotY.CenterZ = rotZ.CenterZ = translatedCenter.Z;
// Apply the angle amounts
((AxisAngleRotation3D) rotX.Rotation).Angle = amountX;
((AxisAngleRotation3D) rotY.Rotation).Angle = amountY;
((AxisAngleRotation3D) rotZ.Rotation).Angle = amountZ;
}
So I have 2 Image's I want the first Image to move towards the other Image I have both X,Y coordinates, how should this be done if I want it moving pixel by pixel towards the target Image?
Bare in mind, I'm using Windows-Universal I've tried DoubleAnimation but my knowledge in that stuff is really bad, I don't know where to start. The Image would sometimes have to move diagonally (across) rather than moving right then up.
How should I do this?
This is what I have so far:
private void MoveToTarget_Start()
{
moveToTimer = new DispatcherTimer();
moveToTimer.Tick += MoveToTarget_Tick;
moveToTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
moveToTimer.Start();
}
void MoveToTarget_Tick(object sender, object e)
{
}
First, you need to know how many pixels you need to move. For that, we can try to retrieve the absolute position of each element and compare them (there may be a more straightforward way, I just don't know how):
private Point GetAbsolutePosition(UIElement element)
{
var ttv = element.TransformToVisual(Window.Current.Content);
return ttv.TransformPoint(new Point(0, 0));
}
(taken from this answer)
From there, we retrieve the point of each element and compute the difference:
var position1 = GetAbsolutePosition(Image1);
var position2 = GetAbsolutePosition(Image2);
var offsetX = position2.X - position1.X;
var offsetY = position2.Y - position1.Y;
Now, we now of how many pixels we'll have to move on each axis. We add the TranslateTransform for the element (it may be better to do that beforehand directly from the XAML):
var translateTransform = new TranslateTransform();
image1.RenderTransform = translateTransform;
Finally, we create the animations, and target the TranslateTransform. Then we group them in a Storyboard, and start it:
var animationX = new DoubleAnimation()
{
From = 0,
To = offsetX,
Duration = TimeSpan.FromSeconds(2)
};
var animationY = new DoubleAnimation()
{
From = 0,
To = offsetY,
Duration = TimeSpan.FromSeconds(2)
};
Storyboard.SetTarget(animationX, translateTransform);
Storyboard.SetTargetProperty(animationX, "X");
Storyboard.SetTarget(animationY, translateTransform);
Storyboard.SetTargetProperty(animationY, "Y");
var storyboard = new Storyboard();
storyboard.Children.Add(animationX);
storyboard.Children.Add(animationY);
storyboard.Begin();
To be honest the best idea is to use DoubleAnimation and Storyboard classes.
I would set background as Canvas then You can animate it throught Canvas.SetLeft and Canvas.SetTop properties.
First You should create DoubleAnimationC class
DoubleAnimation da = new DoubleAnimation()
{
SpeedRatio = 3.0,
AutoReverse = false,
From = 0
To = 100
BeginTime = TimeSpan.FromSeconds(x),
};
Storyboard.SetTarget((Timeline)doubleAnimation, YOUR IMAGE);
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(Canvas.Top)"));
Of course change those properties as You like and now we must create StoryBoard class which will contains our animation
Storyboard sb = new Storyboard();
sb.Children.Add(da);
sb.Start();
Hope it helps!
How to draw a bezier with two colors? like the picture below?
I already have drawn the bezier, I can fill it with any color, however I cant the gradient working.
this is what im doing, im using a Path to create the bezier by the way.
private void test()
{
System.Windows.Media.GradientStop GradientStop1 = new System.Windows.Media.GradientStop();
System.Windows.Media.GradientStop GradientStop2 = new System.Windows.Media.GradientStop();
System.Windows.Media.LinearGradientBrush p_Fill; p_Fill = new System.Windows.Media.LinearGradientBrush(Colors.Blue, Colors.Red, new Point(0, 0.5), new Point(1, 0.5));
p_Fill.GradientStops.Add(GradientStop1);
p_Fill.GradientStops.Add(GradientStop2);
Bez.Fill = p_Fill;
}
This is how it should be
This is what I get
If you need a sharp cut between the 2 colored halves, you have to need more GradientStops:
var grad3 = new System.Windows.Media.GradientStop()
{Offset = 0.5, Color=Colors.Blue};
var grad4 = new System.Windows.Media.GradientStop()
{Offset = 0.5, Color=Colors.Red};
GradientStop2.Offset = 1;
p_Fill.GradientStops.Add(GradientStop1);
p_Fill.GradientStops.Add(grad3);
p_Fill.GradientStops.Add(grad4);
p_Fill.GradientStops.Add(GradientStop2);
Addtionally, you have to set the Brush for the Stroke, not the Fill. The StrokeThickness determines the thickness of the curve:
Bez.Stroke = p_Fill;
Bez.StrokeThickness = new Thickness(10);
I would like to dock an OxyPlot graph in my windows form and graph the function y = 2x - 7. I have downloaded OxyPlot and added the references to my project. I use the following code to add the plot to my form:
public partial class GraphForm : Form
{
public OxyPlot.WindowsForms.Plot Plot;
public Graph()
{
InitializeComponent();
Plot = new OxyPlot.WindowsForms.Plot();
Plot.Model = new PlotModel();
Plot.Dock = DockStyle.Fill;
this.Controls.Add(Plot);
Plot.Model.PlotType = PlotType.XY;
Plot.Model.Background = OxyColor.FromRgb(255, 255, 255);
Plot.Model.TextColor = OxyColor.FromRgb(0, 0, 0);
}
}
With this code I see the white background, the control has been created, but it's only a white background. I've looked around the members of the OxyPlot.Plot class but I couldn't find a way to plat my equation. How can I plot my equation in the graph?
You need to add some data to display, you add this to the Models Series property.
Line (X,Y) graph example.
public Graph()
{
InitializeComponent();
Plot = new OxyPlot.WindowsForms.Plot();
Plot.Model = new PlotModel();
Plot.Dock = DockStyle.Fill;
this.Controls.Add(Plot);
Plot.Model.PlotType = PlotType.XY;
Plot.Model.Background = OxyColor.FromRGB(255, 255, 255);
Plot.Model.TextColor = OxyColor.FromRGB(0, 0, 0);
// Create Line series
var s1 = new LineSeries { Title = "LineSeries", StrokeThickness = 1 };
s1.Points.Add(new DataPoint(2,7));
s1.Points.Add(new DataPoint(7, 9));
s1.Points.Add(new DataPoint(9, 4));
// add Series and Axis to plot model
Plot.Model.Series.Add(s1);
Plot.Model.Axes.Add(new LinearAxis(AxisPosition.Bottom, 0.0, 10.0));
Plot.Model.Axes.Add(new LinearAxis(AxisPosition.Left, 0.0, 10.0));
}
This Example: