sharpDX 中的 N 体简单重力模拟?

问题描述 投票:0回答:1

我正在使用.net框架windowsforms gdi+在表单上绘制我的模拟并渲染它。到目前为止,我希望它渲染相同,但我想在 directX(SharpDX 2d) 中渲染它。我知道我的模拟有错误和其他类似的事情,但对我来说,它看起来很棒,作为简单的模拟(我仍在学习如何进行模拟)。如果有人知道我如何用我的代码做到这一点,我很感激,而且我也希望对此进行优化!

代码:

  1. 身体类:
    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() { }
    }
  1. 主要代码:
    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);
        }
    }
    .......
  1. 四叉树类(很快,我将添加这个算法):
 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();
        }
    }

这就是全部,我希望有人可以帮助我使用sharpDX 2D 渲染我的模拟。这是我第二次尝试 SharpDx,现在对我来说太难了!

c# winforms simulation sharpdx
1个回答
0
投票

代码的绘图部分并不难,因为您在代码中使用的绘图调用的 Direct2D 等效项大部分是相同的。

这是设置 Direct2D 设备的难点......

  • 简单的部分:更改绘图功能

在函数

DrawQuadTree
中,我可以看到你正在绘制一个矩形,那么你可以使用
DrawRectangle
。您使用
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);
}

我注意到你有一个函数

DrawVelocityVectors
但你没有在这里发布它的源代码。 如果您需要在该函数中画一条线,Direct2D 中提供了
DrawLine
函数。


  • 最困难的部分:设置 Direct2D

现在有两种方法可以做到这一点,传统方法和现代方法。

使用传统方式,您通常需要管理的事情更少。 用现代的方式你需要管理更多的东西,比如交换链, 但您将可以访问

ID2D1DeviceContext
, 它继承并扩展了
ID2D1RenderTarget
, 虽然你似乎还不需要它, 到目前为止,您使用的每个绘图功能都已在
ID2D1RenderTarget
界面中可用。

传统方式:

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;

您需要用

BeginDraw
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();
}

请注意,退出应用程序后,您有责任关闭所有 DirectX 接口

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();
}

现代方式:

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);
    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);
    dxgiSwapChain = new SharpDX.DXGI.SwapChain(dxgiFactory, d3d11Device, swapChainDescription);
#endif
    backBuffer = dxgiSwapChain.GetBackBuffer<SharpDX.DXGI.Surface>(0);

    dxgiDevice = d3d11Device.QueryInterface<SharpDX.DXGI.Device>();
    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;

除了

BeginDraw
EndDraw
之外,您还需要设置渲染目标并在交换链上调用
Present

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
}

如果您渲染的控制元素的大小可能会调整,您也需要处理它

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);
}

记得关闭所有界面

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();
    dxgiDevice.Dispose();
    backBuffer.Dispose();
    dxgiSwapChain.Dispose();
#if USE_D3D10
    d3d10Device.Dispose();
#else
    d3d11Device.Dispose();
#endif
    dxgiFactory.Dispose();
}
© www.soinside.com 2019 - 2024. All rights reserved.