Trigonometrie und Projektion mit C#
Bei meinem aktuellen und umfangreichen Projekt “Galaxy War Commander” sind zahlreiche Trigonometrieverfahren unverzichtbar. So sind das errechnen von Himmelsrichtungen, Objektverschiebungen, Objektüberschneidungen, Projezieren sowie Unprojezieren zwischen 3D und 2D essenzielle bestandteile des Spiels.
public static bool Intersect (Vector2f Pos1, float Rad1, Vector2f Pos2, float Rad2) { float D = (float)( Math.Sqrt(((Pos1.X - Pos2.X) * (Pos1.X - Pos2.X)) + ((Pos1.Y - Pos2.Y) * (Pos1.Y - Pos2.Y)))); if (D <= (Rad1 + Rad2)) return true; return false; }
Zeigt den Code, welcher verwendet wird um heraus zu finden ob zwei Kreise sich überschneiden. Durch vergleichen von Entfernung der beiden Kreismittelpunkte und der Summe der Radi ergibt sich ein eindeutiges Ergebnis.
public static Vector2f Reposition (Vector2f Pos, float Ang, float Hyp) { float r = Ang * (float)Math.PI / 180.0f; float a = (float)(Math.Sin(r)) * Hyp; float b = (float)(Math.Cos(r)) * Hyp; return new Vector2f((Pos.X + b), (Pos.Y + a)); }
Verschiebt einen Punkt in die gegebene Himmelsrichtung Ang mit der Entfernung Hyp. Hier bediene ich mich dem rechtwinkligen Dreieck in dem ich durch die Katethenfunktionen die X sowie Y Verschiebung errechnen kann.
private Vector3f unproject2Dto3D(Vector3 Pos2D) { Matrix4 finalMatrix; Vector4 _in = new Vector4(Pos2D, 2.0f); Vector4 _out; float[] view = new float[4]; GL.GetFloat(GetPName.Viewport, view); Matrix4 modelMatrix = this.ModelView; Matrix4 projMatrix = this.Projection; Matrix4.Transpose(ref modelMatrix, out modelMatrix); Matrix4.Transpose(ref projMatrix, out projMatrix); finalMatrix = Matrix4.Mult(modelMatrix, projMatrix); finalMatrix.Invert(); _in.X = (_in.X - view[0]) / view[2]; _in.Y = (_in.Y - view[1]) / view[3]; _in.X = _in.X * 2.0f - 1.0f; _in.Y = _in.Y * 2.0f - 1.0f; _in.Z = _in.Z * 2.0f - 1.0f; _out = Vector4.Transform(_in, finalMatrix); if (_out.W == 0.0) throw new InvalidOperationException("_out.W went 0!"); Vector3f Out = new Vector3f( (_out.X / _out.W), (_out.Y / _out.W), (_out.Z / _out.W) ); return Out; }
Dieser Code unprojeziert Bildschirmkoordinaten auf 3D Koordinaten ohne auf eine spezielle Y oder Z ebene zu Zielen. Dabei wird, entgegen der Projektion, der Bildschirmpunkt anhand der invertierten Ergebnismatrix aus der multiplikation der ModelView- sowie Projektions-Matrix transformiert. Zu beachten ist auch das die per GL.GetFloat() ausgelesenen Matritzen in die Row-Major form umgewandelt werden müssen.
Um den unprojezierten Punkt nun auf eine Ebene zu versetzen auf der er verwendet werden kann unprojezieren wir die gleichen Bildschirmkoordinaten zwei mal mit unterschiedlicher virtueller Tiefe, was uns zwei Punkte gibt durch welche wir eine dreidimensionale Linie ziehen können. Anhand dieser Linie ist es dann möglich den unprojezierten Punkt auf der gewünschten Ebene zu errechnen.
private Vector3 unproject2Dto3D (Vector2f Pos2D, float TargetY) { Vector3f near = unproject2Dto3D( new Vector3(Pos2D.X, Pos2D.Y, 0.0f)); Vector3f far = unproject2Dto3D( new Vector3(Pos2D.X, Pos2D.Y, 10.0f)); float dist = VectorHelper.Distance(near, far); float u = -near.Y / (far.Y - near.Y); float ex = near.X + (far.X - near.X) * u; float ez = near.Z + (far.Z - near.Z) * u; return new Vector3(ex, TargetY, ez); }
Bei den gezeigten Beispielen wurden Klassen und Funktionen des OpenToolkit zur hilfe genommen um die Masse an Daten effizienter speichern zu können.