Related
Let's write a C# program of type "Windows Forms Application" to create a
application simulating the modulation and amplitude demodulation of signal transmission
Analog.
The carrier signal, the modulator signal, the modulated signal will be graphically displayed/
demodulated and the function of spectral density of signals.
I tried to color the picture boxes and they are displayed, but not their content.
My goal is to draw the 4 lines which I want to display in the 4 Picture boxes.
How can I modify this code to get the spec waveformsifice modulation and demodulation in amplitude of an analog signal?
My code:
using System;
using System.Windows.Forms;
using System.Drawing;
using static System.Math;
static class Program
{
[STAThread]
public static void Main()
{
Application.SetCompatibleTextRenderingDefault(true);
Application.Run(new ModulationDemodulationExample());
Application.EnableVisualStyles();
}
}
public class ModulationDemodulationExample : Form
{
// Declare controls and variables
private const int SignalLength = 100;
private PictureBox carrierSignalBox = new PictureBox();
private PictureBox modulatorSignalBox = new PictureBox();
private PictureBox modulatedSignalBox = new PictureBox();
private PictureBox demodulatedSignalBox = new PictureBox();
private double[] carrierSignal = new double[SignalLength];
private double[] modulatorSignal = new double[SignalLength];
private double[] modulatedSignal = new double[SignalLength];
private double[] demodulatedSignal = new double[SignalLength];
private double carrierFrequency = 2.0;
private Bitmap carrierSignalBmp;
private Bitmap modulatorSignalBmp;
private Bitmap modulatedSignalBmp;
private Bitmap demodulatedSignalBmp;
public ModulationDemodulationExample()
{
InitializeComponent();
}
private void InitializeComponent()
{
// Initialize form and controls
Text = "Modulation and Demodulation Simulator";
Size = new Size(800, 600);
PictureBox carrierSignalBox = new PictureBox()
{
Location = new Point(10, 10),
Size = new Size(360, 180),
Visible = true,
Name = "Carrier Signal"
};
PictureBox modulatorSignalBox = new PictureBox()
{
Location = new Point(10, 200),
Size = new Size(360, 180),
Visible = true,
Name = "Modulator Signal"
};
PictureBox modulatedSignalBox = new PictureBox()
{
Location = new Point(400, 10),
Size = new Size(360, 180),
Visible = true,
Name = "Modulated Signal"
};
PictureBox demodulatedSignalBox = new PictureBox()
{
Location = new Point(400, 200),
Size = new Size(360, 180),
Visible = true,
Name = "Demodulated Signal"
};
Controls.Add(carrierSignalBox);
Controls.Add(modulatorSignalBox);
Controls.Add(modulatedSignalBox);
Controls.Add(demodulatedSignalBox);
GenerateSignal();
CreateBitmaps();
DrawSignals();
}
private void GenerateSignal()
{
for (int i = 0; i < SignalLength; i++)
{
carrierSignal[i] = Math.Sin(2 * Math.PI * carrierFrequency * i);
modulatorSignal[i] = Math.Sin(2 * Math.PI * i);
modulatedSignal[i] = carrierSignal[i] * modulatorSignal[i];
demodulatedSignal[i] = modulatedSignal[i] * carrierSignal[i];
}
}
private void CreateBitmaps()
{
carrierSignalBmp = new Bitmap(carrierSignalBox.Width, carrierSignalBox.Height);
modulatorSignalBmp = new Bitmap(modulatorSignalBox.Width, modulatorSignalBox.Height);
modulatedSignalBmp = new Bitmap(modulatedSignalBox.Width, modulatedSignalBox.Height);
demodulatedSignalBmp = new Bitmap(demodulatedSignalBox.Width, demodulatedSignalBox.Height);
//carrierSignalBox.Image = carrierSignalBmp;
carrierSignalBox.DrawToBitmap(carrierSignalBmp, carrierSignalBox.ClientRectangle);
modulatorSignalBox.Image = modulatorSignalBmp;
modulatedSignalBox.Image = modulatedSignalBmp;
demodulatedSignalBox.Image = demodulatedSignalBmp;
}
private void DrawSignals()
{
DrawSignal(carrierSignalBox, carrierSignal, Color.Blue);
DrawSignal(modulatorSignalBox, modulatorSignal, Color.Red);
DrawSignal(modulatedSignalBox, modulatedSignal, Color.Green);
DrawSignal(demodulatedSignalBox, demodulatedSignal, Color.Black);
}
void DrawSignal(PictureBox pictureBox, double[] signal, Color color)
{
// Get the graphics object for the PictureBox
var graphics = pictureBox.CreateGraphics();
// Clear the PictureBox
graphics.Clear(Color.White);
// Set the pen color
var pen = new Pen(color);
// Scale the signal's values to the size of the PictureBox
var yScale = (double)pictureBox.Height / 2.0;
var xScale = (double)pictureBox.Width / (double)signal.Length;
// Draw the signal
for (int i = 0; i < signal.Length - 1; i++)
{
var x1 = (int)(i * xScale);
var y1 = (int)(yScale + (signal[i] * yScale));
var x2 = (int)((i + 1) * xScale);
var y2 = (int)(yScale + (signal[i + 1] * yScale));
graphics.DrawLine(pen, x1, y1, x2, y2);
}
}
}
I created the 4 signals but I can not display them in form ,I would have liked each signal to be displayed in the PictureBox.
You can change your program with an override Paint method for each PictureBox like this
public partial class ModulationDemodulationExample : Form
{
// Declare controls and variables
private const int SignalLength = 100;
private PictureBox carrierSignalBox = new PictureBox();
private PictureBox modulatorSignalBox = new PictureBox();
private PictureBox modulatedSignalBox = new PictureBox();
private PictureBox demodulatedSignalBox = new PictureBox();
private double[] carrierSignal = new double[SignalLength];
private double[] modulatorSignal = new double[SignalLength];
private double[] modulatedSignal = new double[SignalLength];
private double[] demodulatedSignal = new double[SignalLength];
private double carrierFrequency = 2.0;
public ModulationDemodulationExample()
{
InitializeComponent();
// Initialize form and controls
Text = "Modulation and Demodulation Simulator";
Size = new Size(800, 600);
PictureBox carrierSignalBox = new PictureBox()
{
Location = new Point(10, 10),
Size = new Size(360, 180),
Visible = true,
Name = "Carrier Signal"
};
carrierSignalBox.Paint += new PaintEventHandler(carrierSignalBox_Paint);
PictureBox modulatorSignalBox = new PictureBox()
{
Location = new Point(10, 200),
Size = new Size(360, 180),
Visible = true,
Name = "Modulator Signal"
};
modulatorSignalBox.Paint += new PaintEventHandler(modulatorSignalBox_Paint);
PictureBox modulatedSignalBox = new PictureBox()
{
Location = new Point(400, 10),
Size = new Size(360, 180),
Visible = true,
Name = "Modulated Signal"
};
modulatedSignalBox.Paint += new PaintEventHandler(modulatedSignalBox_Paint);
PictureBox demodulatedSignalBox = new PictureBox()
{
Location = new Point(400, 200),
Size = new Size(360, 180),
Visible = true,
Name = "Demodulated Signal"
};
demodulatedSignalBox.Paint += new PaintEventHandler(demodulatedSignalBox_Paint);
Controls.Add(carrierSignalBox);
Controls.Add(modulatorSignalBox);
Controls.Add(modulatedSignalBox);
Controls.Add(demodulatedSignalBox);
GenerateSignal();
}
private void GenerateSignal()
{
for (int i = 0; i < SignalLength; i++)
{
carrierSignal[i] = Math.Sin(2 * Math.PI * carrierFrequency * i);
modulatorSignal[i] = Math.Sin(2 * Math.PI * i);
modulatedSignal[i] = carrierSignal[i] * modulatorSignal[i];
demodulatedSignal[i] = modulatedSignal[i] * carrierSignal[i];
}
}
private void carrierSignalBox_Paint(object? sender, PaintEventArgs e)
{
DrawSignal(e.Graphics, carrierSignalBox, carrierSignal, Color.Blue);
}
private void modulatorSignalBox_Paint(object? sender, PaintEventArgs e)
{
DrawSignal(e.Graphics, modulatorSignalBox, modulatorSignal, Color.Red);
}
private void modulatedSignalBox_Paint(object? sender, PaintEventArgs e)
{
DrawSignal(e.Graphics, modulatedSignalBox, modulatedSignal, Color.Green);
}
private void demodulatedSignalBox_Paint(object? sender, PaintEventArgs e)
{
DrawSignal(e.Graphics, demodulatedSignalBox, demodulatedSignal, Color.Black);
}
void DrawSignal(Graphics graphics, PictureBox pictureBox, double[] signal, Color color)
{
// Get the graphics object for the PictureBox
//var graphics = pictureBox.CreateGraphics();
// Clear the PictureBox
graphics.Clear(Color.White);
// Set the pen color
var pen = new Pen(color);
// Scale the signal's values to the size of the PictureBox
var yScale = (double)pictureBox.Height / 2.0;
var xScale = (double)pictureBox.Width / (double)signal.Length;
// Draw the signal
for (int i = 0; i < signal.Length - 1; i++)
{
var x1 = (int)(i * xScale);
var y1 = (int)(yScale + (signal[i] * yScale));
var x2 = (int)((i + 1) * xScale);
var y2 = (int)(yScale + (signal[i + 1] * yScale));
graphics.DrawLine(pen, x1, y1, x2, y2);
}
}
}
When you want to redraw the PictureBox, just call the Invalidate method and the Paint event is automatically called. Example:
carrierSignalBox.Invalidate()
What I am doing is trying to place views on a sheet in Revit, either center page or center of one of four quadrants. But I have no experience building classes and I am struggling.
I want to set all my properties upon initialization and they should not change once the pickbox outline has been drawn.
I can draw the pick box with ViewPortOutline(UIDocument UIDoc) and if I taskDialog a pickBox.Min.X will return its values
but when I get to the 'setXYZParameter' this locks me in the pickbox and will not move to the dialogbox
if I comment out the 'setXYZParameter' the code moves forward to the dialog box but the ReturnOutline() pickBox.Min.X is null because the 'setXYZParameter' is commented out.
public class ViewPortOutline
{
public static XYZ VP_CenterPoint { get; set; }
public static XYZ VP_CenterPoint_Q1 { get; set; }
public static XYZ VP_CenterPoint_Q2 { get; set; }
public static XYZ VP_CenterPoint_Q3 { get; set; }
public static XYZ VP_CenterPoint_Q4 { get; set; }
private static UIDocument _UIDoc { get; set; }
public static PickedBox pickBox { get; set; }
public ViewPortOutline(UIDocument UIDoc)
{
_UIDoc = UIDoc;
PickBox();
}
public static void PickBox()
{
Selection selection = _UIDoc.Selection;
PickedBox pickBox = selection.PickBox(PickBoxStyle.Directional);//<<< Problem Here
setXYZParameters();
}
public static Outline ReturnOutline()
{
double minX = pickBox.Min.X;//<<< Null
double minY = pickBox.Min.Y;
double maxX = pickBox.Max.X;
double maxY = pickBox.Max.Y;
double sminX = Math.Min(minX, maxX);
double sminY = Math.Min(minY, maxY);
double smaxX = Math.Max(minX, maxX);
double smaxY = Math.Max(minY, maxY);
Outline outline = new Outline(new XYZ(sminX, sminY, 0), new XYZ(smaxX, smaxY, 0));
return outline;
}
public static void ReturnCenterPoints(UIDocument uidoc, Outline outline,
out XYZ midpoint, out XYZ q1c, out XYZ q2c, out XYZ q3c, out XYZ q4c)
{
double outlineDiagonal = outline.GetDiagonalLength();
double outlineCenter = outline.GetDiagonalLength() / 2;
XYZ bottomleft = (outline.MinimumPoint);
XYZ topleft = new XYZ(outline.MinimumPoint[0], outline.MaximumPoint[1], 0);
XYZ topright = (outline.MaximumPoint);
XYZ bottomright = new XYZ(outline.MaximumPoint[0], outline.MinimumPoint[1], 0);
midpoint = new XYZ((outline.MaximumPoint[0]) / 2, (outline.MaximumPoint[1]) / 2, 0);
XYZ midleft = new XYZ(outline.MinimumPoint[0], outline.MaximumPoint[1] / 2, 0);
XYZ midtop = new XYZ(outline.MaximumPoint[0] / 2, outline.MaximumPoint[1], 0);
XYZ midright = new XYZ(outline.MaximumPoint[0], outline.MaximumPoint[1] / 2, 0);
XYZ midbottom = new XYZ(outline.MaximumPoint[0] / 2, outline.MinimumPoint[1], 0);
Outline q1 = new Outline(midpoint, topright);
q1c = new XYZ(
q1.MaximumPoint[0] / 2,
q1.MaximumPoint[1] / 2, 0);
Outline q2 = new Outline(midleft, midtop);
q2c = new XYZ(
q2.MaximumPoint[0] / 2,
q2.MaximumPoint[1] / 2, 0);
Outline q3 = new Outline(bottomleft, midpoint);
q3c = new XYZ(
q3.MaximumPoint[0] / 2,
q3.MaximumPoint[1] / 2, 0);
Outline q4 = new Outline(midbottom, midright);
q4c = new XYZ(
q4.MaximumPoint[0] / 2,
q4.MaximumPoint[1] / 2, 0);
}
public static void setXYZParameters()
{
XYZ mp; XYZ q1c; XYZ q2c; XYZ q3c; XYZ q4c;
ReturnCenterPoints(_UIDoc, ReturnOutline(),
out mp, out q1c, out q2c, out q3c, out q4c);
VP_CenterPoint = mp;
VP_CenterPoint_Q1 = q1c;
VP_CenterPoint_Q2 = q2c;
VP_CenterPoint_Q3 = q3c;
VP_CenterPoint_Q4 = q4c;
}
}
ViewPortOutline outline = new ViewPortOutline(uidoc);
UnitSetup_Form form = new UnitSetup_Form(commandData);
form.ShowDialog();
I didn't pass my arguments...
setXYZParameters(pickBox);
public static Outline ReturnOutline(PickedBox pickBox)
public static void setXYZParameters(PickedBox pickBox)
ReturnCenterPoints(_UIDoc, ReturnOutline(pickBox),
out midpoint, out q1c, out q2c, out q3c, out q4c);
Programming is stressful, I spend more time debugging then creating...
I am trying to create a high level API with sharpdx. It has to be able to draw, but I am stuck on how to make it work with multiple Draw calls at the same time.
This is how I call the class
DirectXFinalD d33d = new DirectXFinalD();
Here I create a new object of the type viereck(rectangle)
Viereck viereck = new Viereck(0, 0, 0.2, 0.1, myBrush, myBrush, 1, false);
Here I pass the object to the class
d33d.DrawDirectX(viereck);
And it already works, but the problem is, I want it to be able that you can pass more objects at any given time, and let them be drawn.
I already tried to always update the vertexbuffer and always += the vertices, but the problem is that different shapes need different topologies. Here is the class:
namespace DrawHost
{
public class DirectXFinalD : DrawHost.DirectXBaseD<D3D11>, IDrawable
{
;
public DirectXFinalD(IDrawable objectToDraw = null, DataStream stream = null)
{
this.objectToDraw = objectToDraw;
if (stream == null)
stream = new DataStream(32 * 612500, true, true);
else
this.stream = stream;
}
protected override void Attach()
{
#region Shader
if (Renderer == null)
return;
device = Renderer.Device;
context = device.ImmediateContext;
// Compile Vertex and Pixel shaders
vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
vertexShader = new VertexShader(device, vertexShaderByteCode);
pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
pixelShader = new PixelShader(device, pixelShaderByteCode);
// Layout from VertexShader input signature
layout = new InputLayout(device, ShaderSignature.GetInputSignature(vertexShaderByteCode), new[] {
new InputElement("POSITION",0,Format.R32G32B32A32_Float,0,0),
new InputElement("COLOR",0,Format.R32G32B32A32_Float,16,0)
});
#endregion
if (objectToDraw == null) { }
else
{
float r = 0;
float g = 0;
float b = 0;
switch (objectToDraw.ToString())
{
#region Dreieck
case "Dreieck":
Dreieck dreieck = (Dreieck)objectToDraw;
topology = PrimitiveTopology.TriangleStrip;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)dreieck.Color).Color);
streamList.Add(new Vector4((Convert.ToSingle(dreieck.X)), (Convert.ToSingle(dreieck.Y) / 10), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
streamList.Add(new Vector4(((Convert.ToSingle(dreieck.X) + Convert.ToSingle(dreieck.Width))), -(Convert.ToSingle(dreieck.Y)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
streamList.Add(new Vector4(-(Convert.ToSingle(dreieck.X)), -((Convert.ToSingle(dreieck.Y) + Convert.ToSingle(dreieck.Height) )), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
break;
#endregion
#region Viereck
case "Viereck":
Viereck viereck = (Viereck)objectToDraw;
topology = PrimitiveTopology.TriangleStrip;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)viereck.Color).Color);
streamList.Add(new Vector4((Convert.ToSingle(viereck.X)), (Convert.ToSingle(viereck.Y)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4(((Convert.ToSingle(viereck.X))), (Convert.ToSingle(viereck.Y) + Convert.ToSingle(viereck.Height)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4((Convert.ToSingle(viereck.X) + Convert.ToSingle(viereck.Width)), (Convert.ToSingle(viereck.Y) ), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4((Convert.ToSingle(viereck.X) + Convert.ToSingle(viereck.Width)), ((Convert.ToSingle(viereck.Y) + Convert.ToSingle(viereck.Height))), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
break;
#endregion
#region Kreis
case "Kreis":
topology = PrimitiveTopology.Undefined;
Kreis kreis = (Kreis)objectToDraw;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)kreis.Color).Color);
for (float j = 0; j <= 360; j++)
{
for (double i = 0; i <= 360; i++) //254
{
double rad = i * (Math.PI / 180);
float x = (float)Math.Cos(rad) * ((float)kreis.Width / 2);
float y = (float)Math.Sin(rad) * ((float)kreis.Height / 2);
streamList.Add(new Vector4(x , y, 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
}
}
break;
#endregion
};
foreach (Vector4 a in streamList)
{
stream.WriteRange(new[] { a });
}
stream.Position = 0;
streamGV streamGV = new streamGV(stream);
//streamGV.GetList(streamList);
//streamList = null;
GC.Collect();
}
vertices = new Buffer(device, stream, new BufferDescription()
{
BindFlags = BindFlags.VertexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = (int)stream.Length,
Usage = ResourceUsage.Default,
StructureByteStride = 0,
});
stream.Dispose();
// Prepare All the stages
context.InputAssembler.InputLayout = (layout);
context.InputAssembler.PrimitiveTopology = topology;
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, 32, 0));
context.VertexShader.Set(vertexShader);
context.PixelShader.Set(pixelShader);
}
public override void RenderScene(DrawEventArgs args)
{
Renderer.Device.ImmediateContext.ClearRenderTargetView(Renderer.RenderTargetView, new Color4(0.6f, 0, 0, 0));
Renderer.Device.ImmediateContext.Draw((int)stream.Length, 0);
return;
}
public override void Case(DXElement dxviewer)
{
dxviewer11 = dxviewer;
}
public override void DrawDirectX(IDrawable objectToDraw)
{
this.objectToDraw = objectToDraw;
//dxviewer11.Renderer = new Scene_11();
//Renderer = new D3D11();
stream = new DataStream(32 * 612500, true, true);
streamGV strean = new streamGV();
dxviewer11.Renderer = new DirectXFinalD(objectToDraw, stream) { Renderer = new D3D11() };
}
private void ConvertColor(ref float r, ref float g, ref float b, System.Windows.Media.Color color)
{
r = (float)(color.R * 255);
g = (float)(color.G * 255);
b = (float)(color.B * 255);
}
}
How can I make it possible to draw all of them at the same time? I am using sharpdx as my renderform. One problem is that I always have to change the topology, for example triangle needs trianglelistbut for the circle I use Linestrip. Any help would be appreciated
As I can see you're calling the Attach ones. In this attach method you're only creating 1 vertex buffer, with a mesh depending on objectToDraw.
You should decouple you shader compilation code and your vertexbuffer setup.
You could create a class that will manage the vertexbuffer and 'knows' how to draw the mesh.
For example: (PSEUDO)
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public const int Stride = 16 + 16;
public Vector4 Pos;
public Color4 Color;
}
public class Mesh
{
private Vertex[] _vertices;
private int[] _indices;
private SharpDX.Direct3D11.Buffer _indexBuffer;
private SharpDX.Direct3D11.Buffer _vertexBuffer;
private VertexBufferBinding _vertexBufferBinding;
public Mesh(Vertex[] vertices, int[] indices)
{
// save the vertices in a field
_vertices = value;
var vbd = new BufferDescription(
SharpDX.Utilities.SizeOf<Vertex>() * _vertices.Length,
ResourceUsage.Immutable,
BindFlags.VertexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0);
// setup the vertex buffer
_vertexBuffer = SharpDX.Direct3D11.Buffer.Create<Vertex>(DX11.Device, _vertices, vbd);
// create the binding
_vertexBufferBinding = new VertexBufferBinding(_vertexBuffer, Vertex.Stride, 0);
_indices = value;
var ibd = new BufferDescription(
sizeof(int) * _indices.Length,
ResourceUsage.Immutable,
BindFlags.IndexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0);
// setup the index buffer
_indexBuffer = SharpDX.Direct3D11.Buffer.Create<int>(DX11.Device, _indices, ibd);
}
// the SelectBuffers will select the right vertex buffer.
// this could be combined with the Draw method, but I rather not
// You should call this ones even when you draw multiple the same mesh.
public void SelectBuffers()
{
DX11.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, _vertexBufferBinding);
DX11.Device.ImmediateContext.InputAssembler.SetIndexBuffer(_indexBuffer, SharpDX.DXGI.Format.R32_UInt, 0);
}
public void Draw()
{
DX11.Device.ImmediateContext.DrawIndexed(_indices.Length, 0, 0);
}
}
List<Mesh> _meshes = new List<Mesh>();
public void SetupShaders()
{
vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
vertexShader = new VertexShader(device, vertexShaderByteCode);
pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
...... etc
}
public Mesh SetupMesh(object objectToDraw)
{
switch(.....)
{
// .. implement your beautiful switch ;-)
}
return new Mesh(vertices, indices);
}
public void Init()
{
SetupShaders();
_meshes.Add(SetupMesh(new Dreieck(.....)));
_meshes.Add(SetupMesh(new Viereck(.....)));
_meshes.Add(SetupMesh(new Kreis(.....)));
}
public override void RenderScene(DrawEventArgs args)
{
Renderer.Device.ImmediateContext.ClearRenderTargetView(Renderer.RenderTargetView, new Color4(0.6f, 0, 0, 0));
foreach(var mesh in _meshes)
{
mesh.SelectBuffers();
mesh.Draw();
}
return;
}
Something like that...
I am currently learning OpenGL and I am trying to write simple solar system application similar to the one in tutorial, yet for some reason camera behavior is really weird and I am not sure what is causing this. I want my camera to look at the sun, but all I am getting are some really weird angles of planets or nothing at all, it might not even be pure camera problem. Can somebody tell me what exactly I am doing wrong? I would appreciate some code. If someone is willing to help and prefer to check application instead of reading code here, link is added below.
The camera here will be as close to the tutorial as possible (FPS), but I got also dragging/scrolling system instead of this.
public class Camera
{
private static float eyeX, eyeY, eyeZ;
private static float centerX, centerY, centerZ;
private const float movingSpeed = 0.3f;
private const float rotationSpeed = 0.25f;
private static double i, j, k;
public static float Height { get; set; }
public static float Slope { get; set; }
public void InitCamera()
{
eyeX = 0f;
eyeY = 15f;
eyeZ = 25f;
centerX = 0;
centerY = 2;
centerZ = 0;
Look();
}
public void Look()
{
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
Gl.LoadIdentity();
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
}
public void UpdateDirVector()
{
i = -Math.Sin(((double)Slope).ToRadians());
j = Math.Sin(((double)Height).ToRadians());
k = Math.Cos(((double)Slope).ToRadians());
centerX = eyeX - (float)i;
centerY = eyeY - (float)j;
centerZ = eyeZ - (float)k;
}
public static void CenterMouse()
{
if (GlCenter == null)
return;
var pos = (Point) GlCenter;
WinApi.SetCursorPos((int)Math.Round(pos.X), (int)Math.Round(pos.Y));
}
public void Update(int pressedButton)
{
if (GlCenter == null)
return;
var pos = (Point)GlCenter;
var halfHeight = GlHeight / 2;
var halfWidth = GlWidth / 2;
var position = new Pointer();
WinApi.GetCursorPos(ref position);
var diffX = (float)pos.X - position.x;
var diffY = (float)pos.Y - position.y;
if (position.y < halfHeight)
Height -= rotationSpeed * diffY;
else if (position.y > halfHeight)
Height += rotationSpeed * -diffY;
if (position.x < halfWidth)
Slope += rotationSpeed * -diffX;
else if (position.x > halfWidth)
Slope -= rotationSpeed * diffX;
UpdateDirVector();
CenterMouse();
if (pressedButton == 1) // LPM
{
eyeX -= (float)i * movingSpeed;
eyeY -= (float)j * movingSpeed;
eyeZ -= (float)k * movingSpeed;
}
else if (pressedButton == -1) // PPM
{
eyeX += (float)i * movingSpeed;
eyeY += (float)j * movingSpeed;
eyeZ += (float)k * movingSpeed;
}
Look();
}
}
Planet.cs:
public class Planet
{
private readonly PlanetTypes _planetType;
private readonly Position _position;
private float _orbitAngle;
private readonly float _sizeRadius;
private readonly float _velocity;
private readonly string _texturePath;
private uint _list;
private float _rotationAngle;
public Planet(float radius, PlanetTypes planetType, Position position, string texturePath, bool hasMoon)
{
_sizeRadius = radius;
_planetType = planetType;
_position = position;
_orbitAngle = Rng.Next(360);
_velocity = (float)Rng.NextDouble() * 0.3f;
_texturePath = texturePath;
}
public void Create()
{
var quadric = Gl.NewQuadric();
Gl.QuadricNormals(quadric, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadric, (int) OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(270, 1, 0, 0);
Gl.Sphere(quadric, _sizeRadius, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void DrawOrbit()
{
Gl.Begin(OpenGL.GL_LINE_STRIP);
for (var i = 0; i <= 360; i++)
Gl.Vertex(_position.X * (float)Math.Sin(i * Math.PI / 180), 0, _position.X * (float)Math.Cos(i * Math.PI / 180));
Gl.End();
}
public void Draw()
{
DrawOrbit();
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_orbitAngle += _velocity;
_rotationAngle += 0.6f;
Gl.Rotate(_orbitAngle, 0, 1, 0);
Gl.Translate(-_position.X, -_position.Y, -_position.Z);
Gl.Rotate(_rotationAngle, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Sun.cs
public class Sun
{
private uint _list;
private float _rotation;
private readonly string _texturePath;
public Sun(string texturePath)
{
_texturePath = texturePath;
}
public void Create()
{
var quadratic = Gl.NewQuadric();
Gl.QuadricNormals(quadratic, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadratic, (int)OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(90, 1, 0, 0);
Gl.Sphere(quadratic, 3, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void Draw()
{
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_rotation += 0.05f;
Gl.Rotate(_rotation, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Stars.cs
public class Stars
{
private readonly List<Position> starPositions = new List<Position>();
public void CreateStars(int amount)
{
var count = 0;
while (count <= amount)
{
var p = default(Position);
p.X = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Z = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Y = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
if (!(Math.Pow(Math.Pow(p.X, 2) + Math.Pow(p.Y, 2) + Math.Pow(p.Z, 2), 1 / 3f) > 15))
continue;
starPositions.Add(p);
count++;
}
}
public void Draw()
{
Gl.Begin(OpenGL.GL_POINTS);
Gl.Color(1, 1, 1);
Gl.PointSize(3);
foreach (var starPos in starPositions)
Gl.Vertex(starPos.X, starPos.Y, starPos.Z);
Gl.End();
}
}
SolarSystem.cs (basically a collection of everything above, enums aren't necessary, I left them cause they might be useful in the future)
public class SolarSystem
{
public static Random Rng { get; } = new Random();
private readonly Stars _stars;
private readonly Sun _sun;
private readonly List<Planet> _planets;
public SolarSystem()
{
Camera = new Camera();
_stars = new Stars();
_sun = new Sun("sun.bmp");
_planets = new List<Planet>();
}
public void CreateScene()
{
_planets.Add(new Planet(0.5f, PlanetTypes.Mercury, new Position(5, 0, 0), "mercury.bmp", false)); // tylko tutaj pliki, wszedzie indziej przeksztaĆcone na .bmp
_planets.Add(new Planet(0.7f, PlanetTypes.Venus, new Position(11, 0, 0), "venus.bmp", false));
_planets.Add(new Planet(1, PlanetTypes.Earth, new Position(15, 0, 0), "earth.bmp", true));
_planets.Add(new Planet(1, PlanetTypes.Mars, new Position(22, 0, 0), "mars.bmp", false));
_planets.Add(new Planet(1.5f, PlanetTypes.Jupiter, new Position(28, 0, 0), "jupiter.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Saturn, new Position(35, 0, 0), "saturn.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Uranus, new Position(41, 0, 0), "uranus.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Neptune, new Position(51, 0, 0), "neptune.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Pluto, new Position(60, 0, 0), "pluto.bmp", false));
_stars.CreateStars(500);
_sun.Create();
foreach (var planet in _planets)
planet.Create();
}
public Camera Camera { get; }
public void DrawScene()
{
_stars.Draw();
_sun.Draw();
foreach (var planet in _planets)
planet.Draw();
}
public enum PlanetTypes
{
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Neptune,
Uranus,
Pluto
}
}
API (including pointer position translation, because only this way I was able to center cursor, it will be removed with dragging camera system)):
public static class WinApi
{
[DllImport("GDI32.dll")]
public static extern void SwapBuffers(uint hdc);
[DllImport("user32.dll")]
public static extern void SetCursorPos(int x, int y);
[DllImport("user32.dll")]
public static extern void GetCursorPos(ref Pointer point);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
}
[DllImport("User32", EntryPoint = "ClientToScreen", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ClientToScreen(
IntPtr hWnd,
ref POINT pt);
[EnvironmentPermission(SecurityAction.LinkDemand, Unrestricted = true)]
public static Point? TransformToScreen(
Point point,
Visual relativeTo)
{
var hwndSource = PresentationSource.FromVisual(relativeTo) as HwndSource;
if (hwndSource == null)
return null;
var root = hwndSource.RootVisual;
// Transform the point from the root to client coordinates.
var transformToRoot = relativeTo.TransformToAncestor(root);
var pointRoot = transformToRoot.Transform(point);
var m = Matrix.Identity;
var transform = VisualTreeHelper.GetTransform(root);
if (transform != null)
{
m = Matrix.Multiply(m, transform.Value);
}
var offset = VisualTreeHelper.GetOffset(root);
m.Translate(offset.X, offset.Y);
var pointClient = m.Transform(pointRoot);
pointClient = hwndSource.CompositionTarget.TransformToDevice.Transform(pointClient);
var pointClientPixels = new POINT();
pointClientPixels.X = (0 < pointClient.X)
? (int)(pointClient.X + 0.5)
: (int)(pointClient.X - 0.5);
pointClientPixels.Y = (0 < pointClient.Y)
? (int)(pointClient.Y + 0.5)
: (int)(pointClient.Y - 0.5);
var pointScreenPixels = pointClientPixels;
if (ClientToScreen(
hwndSource.Handle,
ref pointScreenPixels))
{
return new Point(
pointScreenPixels.X,
pointScreenPixels.Y);
}
return new Point();
}
}
Extensions.cs
public static class Extensions
{
public static double ToDegrees(this double radians)
{
return radians * (180.0 / Math.PI);
}
public static double ToRadians(this double degrees)
{
return Math.PI * degrees / 180.0;
}
}
MainWindow.cs
public partial class MainWindow
{
private DispatcherTimer _dispatcherTimer;
private uint _hdc;
public static string _texturesPath = #"Data\Textures\";
private SolarSystem _solarSystem;
private int _movement;
public static OpenGL Gl { get; private set; }
public static Point? GlCenter { get; private set; }
public static int GlHeight { get; private set; }
public static int GlWidth { get; private set; }
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
ContentRendered += MainWindow_ContentRendered;
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
Gl = openGLControl.OpenGL;
var source = (HwndSource)PresentationSource.FromVisual(openGLControl);
var hWnd = source?.Handle;
if (hWnd != null) _hdc = (uint)hWnd;
_solarSystem = new SolarSystem();
_solarSystem.Camera.InitCamera();
float[] materialAmbient = { 0.5f, 0.5f, 0.5f, 1.0f };
float[] materialDiffuse = { 1f, 1f, 1f, 1.0f };
float[] materialShininess = { 10.0f };
float[] lightPosition = { 0f, 0f, 0f, 1.0f };
float[] lightAmbient = { 0.85f, 0.85f, 0.85f, 0.0f };
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightAmbient);
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPosition);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_SHININESS, materialShininess);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_DIFFUSE, materialDiffuse);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT, materialAmbient);
Gl.Enable(OpenGL.GL_LIGHTING);
Gl.Enable(OpenGL.GL_LIGHT0);
Gl.Enable(OpenGL.GL_DEPTH_TEST);
_solarSystem.CreateScene();
Gl.ClearColor(0, 0, 0, 1);
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl);
GlHeight = (int)openGLControl.Height;
GlWidth = (int)openGLControl.Width;
Camera.CenterMouse();
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += DispatcherTimer_Tick;
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
_solarSystem.Camera.Update(_movement);
_solarSystem.DrawScene();
WinApi.SwapBuffers(_hdc);
Gl.Flush();
}
private void OpenGLControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
_movement = 1;
else
_movement = -1;
}
private void OpenGLControl_MouseUp(object sender, MouseButtonEventArgs e)
{
_movement = 0;
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.C)
{
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl); // new Point((int)(Left), (int)(Top));
}
}
public static uint LoadTexture(string filename)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException(filename);
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var texture = new uint[1];
var id = texture[0];
Gl.GenTextures(1, texture);
Gl.BindTexture(OpenGL.GL_TEXTURE_2D, id);
var bmp = new Bitmap(filename);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, 3, bmpData.Width, bmpData.Height, 0, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bmpData.Scan0);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
bmp.UnlockBits(bmpData);
return id;
}
}
Link to whole Application:
https://www.dropbox.com/sh/uhfyeayxn8l7q9y/AAA8tFda5-ZLAjTUzJcwKUm6a?dl=0
UPDATE 1:
I have changed Look() function to:
public void Look()
{
Gl.MatrixMode(OpenGL.GL_PROJECTION);
Gl.LoadIdentity();
Gl.Viewport(0, 0, GlWidth, GlHeight);
Gl.Perspective(45.0f, GlWidth / (double) GlHeight, 1, 200.0);
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
And now it works.
Now I can see that my application is loading incorrect textures for some reason. I guess thats because method LoadTextures() I wrote, is using Gl.GenTextures(1, texture). (There is no Gen(Single)Texture method in sharpgl or I am getting it all wrong).
UPDATE 2:
So basically most of my textures doesn't work because they are not power of two, but from what I have read, they don't have to be anymore. SO my current question is: How to force sharpGL to display NPOT textures?
UPDATE 3:
Turns out I can load them like this, but well, they are upside down :).
_texture = new Texture();
...
Gl.Enable(OpenGL.GL_TEXTURE_2D);
_texture.Create(Gl, $"{_texturesPath}{_texturePath}");
_texture.Bind(Gl);
UPDATE 4:
I can flip texture to display it properly, but question is why this is happening?
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var bmp = new Bitmap($"{_texturesPath}{_texturePath}");
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
_texture.Create(Gl, bmp);
_texture.Bind(Gl);
UPDATE 5:
While question from Update 4 still stands, I got last question: How can I recalculate Camera so it is not limited to look up / down only to -90/90 degree?
You don't appear to be setting up your viewing frustrum correctly, and are using it in an uninitialized state. You have code for it, but it is commented out in MainWindow.xaml.cs:123.
You must setup the frustrum. At least once before drawing. It can be either perspective or orthographic.
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);
I've got the following code and wanted to know whether there was a way for me to assign the brickImage property to the array featured in my code. If so, how do I achieve this? I basically want to create an array of bricks to be displayed in multiple rows etc.
public class Brick
{
private int x, y, width, height;
private Image brickImage;
private Rectangle brickRec;
private Rectangle[] brickRecs;
public Rectangle BrickRec
{
get { return brickRec; }
}
public Brick()
{
x = 0;
y = 0;
width = 60;
height = 20;
brickImage = Breakout.Properties.Resources.brick_fw;
brickRec = new Rectangle(x, y, width, height);
Rectangle[] brickRecs =
{
new Rectangle(0, 0, 60, 20),
new Rectangle(0, 0, 121, 20),
new Rectangle(0, 0, 242, 20)
};
}
public void drawBrick(Graphics paper)
{
paper.DrawImage(brickImage, brickRec);
//paper.DrawImage(brickImage, brickRecs);
}
}
if you want to attach an image to a rectangle simply create your own class:
public class MyBrick
{
public Image Image {get; set;}
public Point[] Locations {get; set;}
}
then add this somewhere in your code
MyBrick[] brickRecs =
{
new MyBrick()
{
Locations =
{
new Point(60, 20),
new Point(10, 10),
//....
},
Image = ... // ADD IMAGE REFERENCE HERE
},
new MyBrick()
{
Locations =
{
new Point(90, 90),
new Point(5, 5),
//....
},
Image = ...
},
};