はじめに
(心の声) System.Numerics.Vector3 からOpenTK.Vector3に変換できない!
周辺の小さなライブラリでVectorを使いたいときはSystem.Numerics.Vector3で作成し、いざ3D系(OpenTK)アプリで使用しようとするとOpenTK.Vector3に変換しないといけない。実態は両方ともfloat * 3 のメモリなのに....
世の中には様々なVector系構造体があります。System.Numerics.Vector3をはじめとして、OpenTKや3dの各種ラップライブラリでVector構造体が宣言されています。
ほぼすべての構造体がfloat * 3 (x,y,z)のフィールドを持ちますが、相互に変換となるとnewしなおすしかありません。float * 3だと、Marshalやポインタを使ったアプローチはコストに見合わなさそう。
ということで、構造体のメモリレイアウトを重ねることで複数種類のVectorを取り出そうというのが今回のアプローチです。
早速ですが、答えは以下になります。FieldOffsetでメモリの位置を重ねています。
[StructLayout(LayoutKind.Explicit)]
public struct Tvec
{
[FieldOffset(0)]
public System.Numerics.Vector3 nvec;
[FieldOffset(0)]
public OpenTK.Vector3 tkvec;
[FieldOffset(0)]
public float X;
[FieldOffset(4)]
public float Y;
[FieldOffset(8)]
public float Z;
}
使ってみます。
void Main()
{
System.Numerics.Vector3 vecn = new System.Numerics.Vector3(1, 2, 3);
Tvec vec = new Tvec();
vec.nvec = vecn;
Console.WriteLine("OpenTK->" + vec.tkvec.ToString());
Console.WriteLine("System.Numerics->" + vec.nvec.ToString());
Console.WriteLine("float X->" +vec.X);
Console.WriteLine("float Y->" +vec.Y);
Console.WriteLine("float Z->" +vec.Z);
}
---------------------------
OpenTK->(1, 2, 3)
System.Numerics-><1, 2, 3>
float X->1
float Y->2
float Z->3
めでたしめでたし...
と言いたいところですけど結局vec.nvec = vecn;で構造体コピーしてるわけだから普通にnewすれば良かったというオチです。相互に何度も行き来するときはこちらの方がよいのかな?
(追記 19.08.15)
ちょっとふがいないのでnewせずにメモリを共用する方法も記載します。
最近使用できるようになったSpanを使用します。
.Net Core2.1以上でない場合、nugetからSystem.Memoryをインストールします。
System.Numerics.Vector3[] n_vec = new System.Numerics.Vector3[]
{
new System.Numerics.Vector3(1,0,0),
new System.Numerics.Vector3(0,1,0),
new System.Numerics.Vector3(0,0,1),
};
var n_vec_span = n_vec.AsSpan();
var tk_vec_span = MemoryMarshal.Cast<System.Numerics.Vector3, OpenTK.Vector3>(n_vec_span).ToArray();
たぶんこの方法が最近の現実解な気がします。