Reputation: 11
i was using .net framework windowsforms gdi+ to draw my simulation on form and render it. So far, i want it to render the same, but i want render it in directX(SharpDX 2d). I know my simulation have bugs and other such a things, but for me it looks great, as simple simulation(I'm still learning about making simulations). If someone know how can i do this with my code, i appreciate it, and also i want this to be optimized!
Code:
public class Body
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public double Mass { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double VX { get; set; }
public double VY { get; set; }
public double AX { get; set; }
public double AY { get; set; }
public int Radius { get; set; }
public bool IsSelected { get; set; }
public bool IsStatic { get; set; }
public bool Visible { get; set; }
public Brush BodyColor { get; set; } = Brushes.Red;
public List<Point> OrbitTrail { get; set; } = new List<Point>();
public double Elasticity { get; set; }
public double FrictionCoefficient { get; set; }
public double Rotation { get; set; }
public double AngularVelocity { get; set; }
public double AngularAcceleration { get; set; }
public BigInteger Temperature { get; set; }
public List<Body> AttachedObjects { get; set; } = new List<Body>();
public string Type { get; set; } = "Ellipse";
public Body() { }
}
public partial class GravitySim : Form
{
#region variables
private double G = 6.67430e-11;
private QuadTree quadTree;
private double timeStep = 1.0;
private int numSimulationSteps = 5000;
private const double scaleFactor = 1e5;
private const double MassScaleFactor = 1e22;
private bool drawQuadTree = false;
private bool selectionMode = true;
private bool velocityVectors = true;
private List<Body> currentPreset = null;
// Declare a variable for real-time acceleration factor
private double realTimeAcceleration = 1.0;
private int maxOrbitPoints = 50000;
private const double TargetFPS = 120.0;
private const double TargetFrameTime = 1000.0 / TargetFPS;
private List<Body> bodies;
private Timer timer;
private Stopwatch frameStopwatch = new Stopwatch();
private Stopwatch renderStopwatch = new Stopwatch();
private Stopwatch SimStopwatch = new Stopwatch();
private int framesCount = 0;
private double frameRate = 0.0;
private double frames;
#endregion
public GravitySim()
{
InitializeComponent();
InitializeSimulation();
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
// Inicjalizacja KONTROLEK
this.MouseDoubleClick += Form1_MouseDoubleClick;
}
#region basicFNC
private void StartRenderTimer()
{
renderStopwatch.Restart();
}
private void StopRenderTimer()
{
renderStopwatch.Stop();
double renderTime = renderStopwatch.Elapsed.TotalMilliseconds;
label28.Text = $"Render Time: {renderTime:F2} ms";
}
private void InitializeSimulation()
{
InitializeColorComboBox();
InitializePresets();
List<Body> bodies = new List<Body>()
{
new Body { Mass = 6e5, X = 200 + 350, Y = 500, VX = 0, VY = 0, Radius = 20, IsStatic = true, BodyColor = Brushes.Red },
new Body { Mass = 4e4, X = 450 + 300, Y = 350, VX = 0, VY = -22 / scaleFactor, Radius = 5, IsStatic = false, BodyColor = Brushes.Blue },
new Body { Mass = 2.5e4, X = 100 + 300, Y = 400, VX = -40 / scaleFactor, VY = 18 / scaleFactor, Radius = 5, IsStatic = false, BodyColor = Brushes.Yellow },
// Dodaj kolejne ciała według potrzeb
};
LoadPreset(bodies);
quadTree = new QuadTree(new Rectangle(0, 0, ClientSize.Width - panel1.Width, ClientSize.Height), 1);
QuadTreeInsertBodies();
// Inicjalizacja timera
timer = new Timer();
timer.Interval = 1; // Czas w milisekundach między krokami symulacji
timer.Tick += Timer_Tick;
SimStopwatch.Start();
}
private void QuadTreeInsertBodies()
{
quadTree.Clear();
foreach (var body in bodies)
{
quadTree.Insert(body);
}
}
private void LoadPreset(List<Body> bodies)
{
this.bodies = bodies;
this.Invalidate();
}
private void InitializePresets()
{
comboBox2.Items.Add("Example preset");
comboBox2.Items.Add("Earth orbiting Sun - 2 bodies");
comboBox2.SelectedIndex = 0;
}
private void InitializeColorComboBox()
{
// Pobierz właściwości Brushes za pomocą refleksji
var brushProperties = typeof(Brushes).GetProperties();
// Dodaj kolory do ComboBox
foreach (var brushProperty in brushProperties)
{
Brush brush = (Brush)brushProperty.GetValue(null, null);
// Sprawdź, czy kolor jest SolidColorBrush (możesz dostosować warunek do innych rodzajów pędzli, jeśli to konieczne)
if (brush is SolidBrush)
{
comboBox1.Items.Add(brushProperty.Name);
}
}
// Ustaw domyślny kolor
comboBox1.SelectedIndex = 0;
}
#endregion
private Body selectedBody = null;
private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (selectionMode)
{
Point mousePosition = e.Location;
// Check if the mouse click is within the bounds of any body
bool clickedOnBody = false;
foreach (var body in bodies)
{
if (IsMouseOnBody(mousePosition, body))
{
// Unselect the currently selected body
if (selectedBody != null)
{
selectedBody.IsSelected = false;
}
body.IsSelected = true;
selectedBody = body;
clickedOnBody = true;
// Pobierz kolor z Brush
Color BodyColor = (body.BodyColor as SolidBrush)?.Color ?? Color.Black;
// Znajdź indeks koloru ciała w comboBox1
int colorIndex = comboBox1.Items.IndexOf(BodyColor.Name);
// Jeśli indeks jest większy lub równy zeru, ustaw go jako zaznaczony
if (colorIndex >= 0)
{
comboBox1.SelectedIndex = colorIndex;
this.BodyColor.BackColor = BodyColor;
}
checkBox4.Checked = selectedBody.IsStatic;
if (body.Mass > Math.Pow(10, 9) || body.Mass < Math.Pow(10, -3)) // Sprawdź, czy masa jest większa niż miliard
{
label9.Text = $"Mass: {ConvertToScientificNotation(body.Mass)} kg";
bodyMass.Text = ConvertToScientificNotation(body.Mass) + " kg";
}
else
{
label9.Text = $"Mass: {body.Mass.ToString("F2")} kg";
bodyMass.Text = body.Mass.ToString("F2") + " kg";
}
break;
}
}
// Clicked outside of any body, unselect all bodies
if (!clickedOnBody)
{
if (selectedBody != null)
{
selectedBody.IsSelected = false;
selectedBody = null;
}
foreach (var body in bodies)
{
body.IsSelected = false;
}
}
this.Invalidate(); // Redraw to reflect the changes
// Dodaj kod, który decyduje, czy panel2 powinien być widoczny
if (selectedBody != null)
{
// Panel powinien być widoczny, ponieważ jest wybrana jakakolwiek ciało
rjButton8.Location = new Point(5, 403);
label24.Location = new Point(75, 443);
comboBox2.Location = new Point(15, 460);
rjButton10.Location = new Point(6, 487);
panel2.Show();
rjButton9.Show();
rjButton13.Show();
label16.Show();
}
else
{
// Panel powinien być ukryty, ponieważ nie jest wybrane żadne ciało
rjButton8.Location = new Point(5, 191);
label24.Location = new Point(76, 231);
comboBox2.Location = new Point(12, 248);
rjButton10.Location = new Point(3, 275);
panel2.Hide();
rjButton9.Hide();
rjButton13.Hide();
label16.Hide();
}
}
}
private void Timer_Tick(object sender, EventArgs e)
{
if (timeStep >= 5000 | numSimulationSteps > 75000)
{
Application.Exit();
this.Close();
MessageBox.Show("An unexpected error occured! Too high timestep. Program has succesfully exited");
}
framesCount++;
frames++;
for (int step = 0; step < numSimulationSteps; step++)
{
UpdatePositions();
CalculateGravity();
// Check collisions and handle them
HandleCollisions();
// Update the QuadTree every frame
UpdateQuadTree();
foreach (var body in bodies)
{
CheckWindowCollision(body);
}
}
this.Invalidate(); // Wywołuje ponownie Paint, aby zaktualizować wyświetlanie ciał
// Update frame rate every second
if (frameStopwatch.ElapsedMilliseconds >= 1000)
{
frameRate = framesCount / (frameStopwatch.ElapsedMilliseconds / 1000.0);
framesCount = 0;
frameStopwatch.Restart();
}
}
private void UpdateQuadTree()
{
// Clear the QuadTree and reinsert all bodies
quadTree.Clear();
foreach (var body in bodies)
{
quadTree.Insert(body);
}
}
private void HandleCollisions()
{
for (int i = 0; i < bodies.Count; i++)
{
for (int j = i + 1; j < bodies.Count; j++)
{
Body body1 = bodies[i];
Body body2 = bodies[j];
double distanceSquared = (body1.X - body2.X) * (body1.X - body2.X) + (body1.Y - body2.Y) * (body1.Y - body2.Y);
double sumOfRadiiSquared = (body1.Radius + body2.Radius) * (body1.Radius + body2.Radius);
if (distanceSquared <= sumOfRadiiSquared)
{
// Introduce a damping factor to reduce velocity after collision
double dampingFactor = 0; // Możesz dostosować tę wartość według potrzeb
body1.VX *= dampingFactor;
body1.VY *= dampingFactor;
// Usunięcie zjedzonego ciała
if (body1.Mass > body2.Mass)
{
// Destroy body2
bodies.Remove(body2);
}
else
{
// Destroy body1
bodies.Remove(body1);
// Dodałem dodatkowy współczynnik tłumienia prędkości dla ciała, które pozostało po zjedzeniu innego ciała
body2.VX *= dampingFactor;
body2.VY *= dampingFactor;
}
}
}
}
}
private bool CheckWindowCollision(Body body)
{
int windowWidth = this.ClientSize.Width - 251;
int windowHeight = this.ClientSize.Height;
bool collisionDetected = false;
if (body.X - body.Radius < 0)
{
//body.VX = Math.Abs(body.VX); // Bounce off left border
collisionDetected = true;
}
else if (body.X + body.Radius > windowWidth)
{
//body.VX = -Math.Abs(body.VX); // Bounce off right border
collisionDetected = true;
}
if (body.Y - body.Radius < 0)
{
//body.VY = Math.Abs(body.VY); // Bounce off top border
collisionDetected = true;
}
else if (body.Y + body.Radius > windowHeight)
{
//body.VY = -Math.Abs(body.VY); // Bounce off bottom border
collisionDetected = true;
}
return collisionDetected;
}
private void UpdatePositions()
{
foreach (var body in bodies)
{
if (!body.IsStatic)
{
body.X += body.VX * timeStep;
body.Y += body.VY * timeStep;
}
}
}
private void CalculateGravity()
{
for (int i = 0; i < bodies.Count; i++)
{
for (int j = i + 1; j < bodies.Count; j++)
{
Body body1 = bodies[i];
Body body2 = bodies[j];
double dx = body2.X - body1.X;
double dy = body2.Y - body1.Y;
double distanceSquared = dx * dx + dy * dy;
double distance = Math.Sqrt(distanceSquared);
double forceMagnitude = (G * body1.Mass * body2.Mass) / distanceSquared;
double forceX = forceMagnitude * (dx / distance);
double forceY = forceMagnitude * (dy / distance);
body1.VX += forceX / body1.Mass * timeStep;
body1.VY += forceY / body1.Mass * timeStep;
body2.VX -= forceX / body2.Mass * timeStep;
body2.VY -= forceY / body2.Mass * timeStep;
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
frameStopwatch.Start();
label13.Text = $"Body count: {bodies.Count()}";
label2.Text = "VX: N/A, VY: N/A";
label7.Text = "PX: N/A, PY: N/A";
label9.Text = "Mass: N/A kg";
checkBox1.Checked = Settings.Default.debugMode;
checkBox2.Checked = Settings.Default.selectionMode;
checkBox3.Checked = Settings.Default.velocityVectors;
rjButton8.Location = new Point(5, 191);
label24.Location = new Point(76, 231);
comboBox2.Location = new Point(12, 248);
rjButton10.Location = new Point(3, 275);
}
private TimeSpan simulatedTime = TimeSpan.Zero;
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
g.TextContrast = 0;
StartRenderTimer();
// Rysowanie ciał
foreach (var body in bodies)
{
DrawBody(g, body, body.BodyColor); // Kolor można dostosować w zależności od ciała
if (body.IsSelected)
{
DrawSelectionSquare(g, body);
}
if (velocityVectors)
{
// Rysowanie wektorów prędkości
DrawVelocityVectors(g, body);
}
}
if (drawQuadTree)
{
// Rysuj Quad Tree
DrawQuadTree(g, quadTree.Root);
}
StopRenderTimer();
double totalForceX = 0.0;
double totalForceY = 0.0;
for (int i = 0; i < bodies.Count; i++)
{
for (int j = i + 1; j < bodies.Count; j++)
{
Body body1 = bodies[i];
Body body2 = bodies[j];
double dx = body2.X - body1.X;
double dy = body2.Y - body1.Y;
double distanceSquared = dx * dx + dy * dy;
double distance = Math.Sqrt(distanceSquared);
double forceMagnitude = (G * body1.Mass * body2.Mass) / distanceSquared;
double forceX = forceMagnitude * (dx / distance);
double forceY = forceMagnitude * (dy / distance);
body1.VX += forceX / body1.Mass * timeStep;
body1.VY += forceY / body1.Mass * timeStep;
body2.VX -= forceX / body2.Mass * timeStep;
body2.VY -= forceY / body2.Mass * timeStep;
totalForceX += forceX;
totalForceY += forceY;
}
}
if (selectedBody != null)
{
label2.Text = $"VX: {(selectedBody.VX * scaleFactor).ToString("F2")}, VY: {(selectedBody.VY * scaleFactor).ToString("F2")}";
label7.Text = $"PX: {selectedBody.X.ToString("F2")}, PY: {selectedBody.Y.ToString("F2")}";
if (selectedBody.Mass > Math.Pow(10, 9) || selectedBody.Mass < Math.Pow(10, -3)) // Sprawdź, czy masa jest większa niż miliard
{
label9.Text = $"Mass: {ConvertToScientificNotation(selectedBody.Mass)} kg";
}
else
{
label9.Text = $"Mass: {selectedBody.Mass.ToString("F2")} kg";
}
}
else
{
// If no body is selected, reset the labels
label2.Text = "VX: N/A, VY: N/A";
label7.Text = "PX: N/A, PY: N/A";
label9.Text = "Mass: N/A kg";
}
// Update label26.Text with the formatted time
double adjustedElapsedMilliseconds = SimStopwatch.ElapsedMilliseconds;
TimeSpan formattedRealTime = TimeSpan.FromMilliseconds(adjustedElapsedMilliseconds);
string formattedTime = $"{formattedRealTime.Hours:D2}h:{formattedRealTime.Minutes:D2}m:{formattedRealTime.Seconds:D2}s";
label26.Text = $"Real Time: {formattedTime}";
// Update label27.Text with the formatted simulated time
simulatedTime += TimeSpan.FromSeconds(timeStep); // Increment simulated time
// Ensure that simulatedTime is not negative
// Dodaj obliczenia dla formatowania czasu symulacji w latach, dniach, godzinach, minutach i sekundach
int years = simulatedTime.Days / 365;
int days = simulatedTime.Days % 365;
string formattedSimTime = $"{years:D2}yr:{days:D3}d:{simulatedTime.Hours:D2}h:{simulatedTime.Minutes:D2}m:{simulatedTime.Seconds:D2}s";
label27.Text = $"Simulated Time: {formattedSimTime}";
label3.Text = $"F: {Math.Sqrt(totalForceX * totalForceX + totalForceY * totalForceY).ToString("F8")}N";
label4.Text = $"Timestep: {timeStep.ToString("F3")}";
label5.Text = $"numSimulationSteps: {numSimulationSteps}";
label10.Text = $"Frame Rate: {frameRate.ToString("F2")} fps";
label6.Text = $"Frames: {frames} frames";
label13.Text = $"Body count: {bodies.Count()}";
if (bodies.Count <= 1)
{
timer.Stop();
MessageBox.Show("Simulation has ended! Reason: No enough bodies, F=0");
//Dodaj w tej linijce resetowanie do pozycji początkowych
// Wywołanie metody resetowania
ResetBodiesToInitialPositions();
rjButton1.Enabled = true;
rjButton2.Enabled = false;
timeStep = 1;
floatTrackBar1.Value = 1;
trackBar1.Value = 5000;
numSimulationSteps = 5000;
frames = 0;
panel2.Hide();
// Odświeżenie widoku
this.Invalidate();
}
}
private void DrawQuadTree(Graphics g, QuadTree.QuadTreeNode node)
{
if (node != null)
{
float thickness = 0.5f; // Grubość linii
Pen pen = new Pen(Brushes.White, thickness);
Point[] polygonPoints = new Point[]
{
new Point((int)node.Bounds.Left, (int)node.Bounds.Top),
new Point((int)node.Bounds.Right, (int)node.Bounds.Top),
new Point((int)node.Bounds.Right, (int)node.Bounds.Bottom),
new Point((int)node.Bounds.Left, (int)node.Bounds.Bottom)
};
g.DrawPolygon(pen, polygonPoints);
if (node.HasChildren)
{
foreach (var child in node.Children)
{
DrawQuadTree(g, child);
}
}
}
}
static string ConvertToScientificNotation(double value)
{
return value.ToString("0.###e0");
}
static string ConvertFromScientificNotation(string value)
{
if (double.TryParse(value, out double result))
{
return result.ToString();
}
return "Invalid input";
}
private void DrawSelectionSquare(Graphics g, Body body)
{
float x = (float)(body.X - body.Radius - 3); // Adjusting for padding
float y = (float)(body.Y - body.Radius - 3); // Adjusting for padding
float diameter = 2 * body.Radius + 6; // Adjusting for padding
g.DrawRectangle(new Pen(Brushes.Yellow, 1), x, y, diameter, diameter);
}
private bool IsMouseOnBody(Point mousePosition, Body body)
{
double distance = Math.Sqrt(Math.Pow(mousePosition.X - body.X, 2) + Math.Pow(mousePosition.Y - body.Y, 2));
return distance <= body.Radius;
}
private void DrawBody(Graphics g, Body body, Brush brush)
{
float x = (float)(body.X - body.Radius);
float y = (float)(body.Y - body.Radius);
float diameter = 2 * body.Radius;
g.FillEllipse(brush, x, y, diameter, diameter);
}
}
.......
public class QuadTree
{
public class QuadTreeNode
{
public Rectangle Bounds { get; }
public List<Body> Bodies { get; } = new List<Body>();
public QuadTreeNode[] Children { get; private set; }
public int MaxBodiesPerNode { get; private set; } = 4;
public bool HasChildren => Children != null;
private bool ShouldSubdivide() => !HasChildren && Bodies.Count >= MaxBodiesPerNode;
public QuadTreeNode(Rectangle bounds, int maxBodiesPerNode)
{
Bounds = bounds;
MaxBodiesPerNode = maxBodiesPerNode;
}
public void Subdivide()
{
int width = Bounds.Width / 2;
int height = Bounds.Height / 2;
Children = new QuadTreeNode[4];
Children[0] = new QuadTreeNode(new Rectangle(Bounds.Left, Bounds.Top, width, height), MaxBodiesPerNode);
Children[1] = new QuadTreeNode(new Rectangle(Bounds.Left + width, Bounds.Top, width, height), MaxBodiesPerNode);
Children[2] = new QuadTreeNode(new Rectangle(Bounds.Left, Bounds.Top + height, width, height), MaxBodiesPerNode);
Children[3] = new QuadTreeNode(new Rectangle(Bounds.Left + width, Bounds.Top + height, width, height), MaxBodiesPerNode);
}
}
public QuadTreeNode Root { get; private set; }
public QuadTree(Rectangle bounds, int maxBodiesPerNode = 4)
{
Root = new QuadTreeNode(bounds, maxBodiesPerNode);
}
public void Clear()
{
Root = new QuadTreeNode(Root.Bounds, Root.MaxBodiesPerNode);
}
public void Insert(Body body)
{
Insert(Root, body);
}
private void Insert(QuadTreeNode node, Body body)
{
if (node.Bounds.Contains(new Point((int)body.X, (int)body.Y)))
{
if (!node.HasChildren && node.Bodies.Count < node.MaxBodiesPerNode)
{
node.Bodies.Add(body);
}
else
{
if (!node.HasChildren)
{
node.Subdivide();
DistributeBodies(node);
}
foreach (var child in node.Children)
{
Insert(child, body);
}
}
}
}
private void DistributeBodies(QuadTreeNode node)
{
foreach (var storedBody in node.Bodies)
{
foreach (var child in node.Children)
{
Insert(child, storedBody);
}
}
node.Bodies.Clear();
}
}
Thats all for it, i hope someone can help me with rendering my simulation using sharpDX 2D. It is my second time trying sharpDx and it is too hard for me now!
Upvotes: -1
Views: 120
Reputation: 373
The drawing part of the code is not hard, since the Direct2D equivalent of the draw calls that you use in your code are mostly the same.
It's the part of setting up a Direct2D device that is hard...
In the function DrawQuadTree
, I can see that you are drawing a rectangle, then you can just use DrawRectangle
. Is there a reason why you were using DrawPolygon
?
private SharpDX.Direct2D1.SolidColorBrush WhiteBrush;
// It's better to create a brush once and reuse it
// instead of creating a new one every time
private void DrawQuadTree(SharpDX.Direct2D1.RenderTarget context, QuadTree.QuadTreeNode node)
{
if (node != null)
{
float thickness = 0.5f;
RawRectangleF rect = new RawRectangleF(
node.Bounds.Left,
node.Bounds.Top,
node.Bounds.Right,
node.Bounds.Bottom);
context.DrawRectangle(rect, WhiteBrush, thickness);
if (WhiteBrush == null)
{
WhiteBrush = new SharpDX.Direct2D1.SolidColorBrush(context,
new RawColor4(1.0f, 1.0f, 1.0f, 1.0f));
}
if (node.HasChildren)
{
foreach (var child in node.Children)
{
DrawQuadTree(context, child);
}
}
}
}
private SharpDX.Direct2D1.SolidColorBrush YellowBrush;
// It's better to create a brush once and reuse it
// instead of creating a new one every time
private void DrawSelectionSquare(SharpDX.Direct2D1.RenderTarget context, Body body)
{
float x = (float)(body.X - body.Radius - 3);
float y = (float)(body.Y - body.Radius - 3);
float diameter = 2 * body.Radius + 6;
RectangleF convert = new RectangleF(x, y, width: diameter, height: diameter);
// convert "X Y Width Height" to "Left Top Right Bottom"
RawRectangleF rect = new RawRectangleF(
convert.Left, convert.Top, convert.Right, convert.Bottom);
if (YellowBrush == null)
{
YellowBrush = new SharpDX.Direct2D1.SolidColorBrush(context,
new RawColor4(1.0f, 1.0f, 0.0f, 1.0f));
}
context.DrawRectangle(rect, YellowBrush);
}
private void DrawBody(SharpDX.Direct2D1.RenderTarget context, Body body,
SharpDX.Direct2D1.Brush brush)
{
float x = (float)(body.X - body.Radius);
float y = (float)(body.Y - body.Radius);
float diameter = 2 * body.Radius;
SharpDX.Direct2D1.Ellipse ellipse = new SharpDX.Direct2D1.Ellipse(
center: new RawVector2(x, y),
radiusX: diameter,
radiusY: diameter);
context.FillEllipse(ellipse, brush);
}
I've noticed that you have a function DrawVelocityVectors
but you didn't post its source code here.
If you need to draw a line in that function, the DrawLine
function is available in Direct2D.
now there are two ways to do this, the legacy way and the modern way.
with the legacy way, you will generally need to manage fewer things.
with the modern way you need to manage much more stuff like swap chain,
but you will have access to ID2D1DeviceContext
,
which inherits from and expands upon ID2D1RenderTarget
,
although you didn't seem to need it yet,
every draw function you use is already available in the ID2D1RenderTarget
interface so far.
The legacy way:
private SharpDX.Direct2D1.Factory d2dFactory;
private SharpDX.Direct2D1.WindowRenderTarget d2dRenderTarget;
private void Form1_Load(object sender, EventArgs e)
{
// ...
IntPtr TargetHWND = Controls.Find("name of the control you will render to", true).First().Handle;
d2dFactory = new SharpDX.Direct2D1.Factory();
RenderTargetProperties properties = new RenderTargetProperties(
RenderTargetType.Default,
new PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied),
0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
HwndRenderTargetProperties hwndRenderTargetProperties = new HwndRenderTargetProperties
{
Hwnd = TargetHWND,
PixelSize = new SharpDX.Size2(0, 0),
#if VSYNC
PresentOptions = PresentOptions.None, // turn on VSync
#else
PresentOptions = PresentOptions.Immediately, // turn off VSync
#endif
};
d2dRenderTarget = new SharpDX.Direct2D1.WindowRenderTarget(
d2dFactory, properties, hwndRenderTargetProperties);
// Create all the brushes you need
WhiteBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(1.0f, 1.0f, 1.0f, 1.0f));
YellowBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(1.0f, 1.0f, 0.0f, 1.0f));
RedBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f));
BlueBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(0.0f, 0.0f, 1.0f, 1.0f));
}
private SharpDX.Direct2D1.SolidColorBrush WhiteBrush;
private SharpDX.Direct2D1.SolidColorBrush YellowBrush;
private SharpDX.Direct2D1.SolidColorBrush RedBrush;
private SharpDX.Direct2D1.SolidColorBrush BlueBrush;
You need to surround your draw operation with BeginDraw
and EndDraw
private RawColor4 backgroundColor = new RawColor4(0.0f, 0.0f, 0.0f, 1.0f);
// change the background color to your liking
private void Form1_Paint(object sender, PaintEventArgs e)
{
d2dRenderTarget.BeginDraw();
d2dRenderTarget.Clear(backgroundColor);
// draw calls...
d2dRenderTarget.EndDraw();
}
Notice that it's your responsibility to close all the DirectX interfaces after you exit the application
private void Form1_Closed(object sender, EventArgs e)
{
d2dRenderTarget.Flush(); // wait for all operations on the GPU to finish
WhiteBrush.Dispose();
YellowBrush.Dispose();
RedBrush.Dispose();
BlueBrush.Dispose();
d2dRenderTarget.Dispose();
d2dFactory.Dispose();
}
The modern way:
private SharpDX.DXGI.Factory dxgiFactory;
#if USE_D3D10
private SharpDX.Direct3D10.Device d3d10Device;
#else
private SharpDX.Direct3D11.Device d3d11Device;
#endif
private SharpDX.DXGI.SwapChain dxgiSwapChain;
private SharpDX.DXGI.Surface backBuffer;
private SharpDX.DXGI.Device dxgiDevice;
private SharpDX.Direct2D1.Device d2dDevice;
private SharpDX.Direct2D1.DeviceContext d2dContext;
private SharpDX.Direct2D1.Bitmap1 d2dTarget;
private void Form1_Load(object sender, EventArgs e)
{
IntPtr TargetHWND = Controls.Find("name of the control you will render to", true).First().Handle;
dxgiFactory = new SharpDX.DXGI.Factory1();
SwapChainDescription swapChainDescription = new SwapChainDescription
{
ModeDescription = new ModeDescription(Format.B8G8R8A8_UNorm),
SampleDescription = new SampleDescription(1, 0),
Usage = Usage.RenderTargetOutput,
BufferCount = 2,
OutputHandle = TargetHWND,
IsWindowed = true,
SwapEffect = SwapEffect.Discard,
Flags = SwapChainFlags.None,
};
#if USE_D3D10
d3d10Device = new SharpDX.Direct3D10.Device(SharpDX.Direct3D10.DriverType.Hardware,
SharpDX.Direct3D10.DeviceCreationFlags.BgraSupport);
dxgiDevice = d3d10Device.QueryInterface<SharpDX.DXGI.Device>();
dxgiSwapChain = new SharpDX.DXGI.SwapChain(dxgiFactory, d3d10Device, swapChainDescription);
#else
d3d11Device = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware,
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport,
SharpDX.Direct3D.FeatureLevel.Level_11_1, SharpDX.Direct3D.FeatureLevel.Level_11_0);
dxgiDevice = d3d11Device.QueryInterface<SharpDX.DXGI.Device>();
dxgiSwapChain = new SharpDX.DXGI.SwapChain(dxgiFactory, d3d11Device, swapChainDescription);
#endif
backBuffer = dxgiSwapChain.GetBackBuffer<SharpDX.DXGI.Surface>(0);
d2dDevice = new SharpDX.Direct2D1.Device(dxgiDevice);
d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice, DeviceContextOptions.None);
d2dTarget = new Bitmap1(d2dContext, backBuffer);
// create all the brushes you need
WhiteBrush = new SolidColorBrush(d2dContext, new RawColor4(1.0f, 1.0f, 1.0f, 1.0f));
YellowBrush = new SolidColorBrush(d2dContext, new RawColor4(1.0f, 1.0f, 0.0f, 1.0f));
RedBrush = new SolidColorBrush(d2dContext, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f));
BlueBrush = new SolidColorBrush(d2dContext, new RawColor4(0.0f, 0.0f, 1.0f, 1.0f));
}
private SharpDX.Direct2D1.SolidColorBrush WhiteBrush;
private SharpDX.Direct2D1.SolidColorBrush YellowBrush;
private SharpDX.Direct2D1.SolidColorBrush RedBrush;
private SharpDX.Direct2D1.SolidColorBrush BlueBrush;
Apart from BeginDraw
and EndDraw
, you will additionally need to set render target and call Present
on the swap chain
private RawColor4 backgroundColor = new RawColor4(0.0f, 0.0f, 0.0f, 1.0f);
// change the background color to your liking
private void Form1_Paint(object sender, PaintEventArgs e)
{
d2dContext.BeginDraw();
d2dContext.Target = d2dTarget;
d2dContext.Clear(backgroundColor);
// draw calls...
d2dContext.Target = null;
d2dContext.EndDraw();
#if VSYNC
dxgiSwapChain.Present(1, PresentFlags.None);
#else
dxgiSwapChain.Present(0, PresentFlags.None);
#endif
}
If the size of the control element you rendering to may be resized, you will need to handle it as well
private void Form1_SizeChanged(object sender, EventArgs e)
{
Size Resolution = Controls.Find("name of the control you will render to", true).First().Size;
d2dContext.Flush(); // wait for all operations on the GPU to finish
d2dTarget.Dispose();
backBuffer.Dispose();
dxgiSwapChain.ResizeBuffers(2, Resolution.Width, Resolution.Height, Format.B8G8R8A8_UNorm, SwapChainFlags.None);
backBuffer = dxgiSwapChain.GetBackBuffer<SharpDX.DXGI.Surface>(0);
d2dTarget = new Bitmap1(d2dContext, backBuffer);
}
remember to close all the interfaces
private void Form1_Closed(object sender, EventArgs e)
{
d2dContext.Flush(); // wait for all operations on the GPU to finish
WhiteBrush.Dispose();
YellowBrush.Dispose();
RedBrush.Dispose();
BlueBrush.Dispose();
d2dTarget.Dispose();
d2dContext.Dispose();
d2dDevice.Dispose();
backBuffer.Dispose();
dxgiSwapChain.Dispose();
dxgiDevice.Dispose();
#if USE_D3D10
d3d10Device.Dispose();
#else
d3d11Device.Dispose();
#endif
dxgiFactory.Dispose();
}
Upvotes: 0