<C#, Unity, Eigen> C#で行列演算ライブラリEigenの機能を使う
Unityで開発している際,4次以上の逆行列の演算が必要になったのでC#で使える行列演算ライブラリを探していました.しかしながら,使えそうなライブラリが見つからず,C++で扱える行列演算ライブラリ"Eigen"をC#で利用しようと考えました.以下,詳細です.
開発環境
- Windows7(x64)
- Visual Studio 2015
- Unity 5.4.1(x64)
概要
Eigenの機能をDLLとして出力
以下,サンプルコードです.Visual StudioのDLLプロジェクトを新規作成し,サンプルコードを追加後,ビルドしてDLLファイルを作成します.ちなみに今回のプロジェクト名は"EigenFuncs"としています.Unityの環境に合わせて32bitもしくは64bitでビルドします.
.hファイル
/*--------------*/ /* EigenFuncs.h */ /*--------------*/ #pragma once extern "C" { #ifdef EIGENFUNCS_EXPORTS #define EIGEN_FUNCS_API __declspec(dllexport) #else #define EIGEN_FUNCS_API __declspec(dllimport) #endif EIGEN_FUNCS_API void InverseMat_FullPivLU(int dim, float a[], float ans[]); } // extern "C"
.cppファイル
/*----------------*/ /* EigenFuncs.cpp */ /*----------------*/ // EigenFuncs.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。 // #include "stdafx.h" #include <stdio.h> #include <string.h> #include <vector> #include <Eigen/Core> #include <Eigen/Dense> #include <Eigen/Geometry> #include <Eigen/LU> #include "EigenFuncs.h" extern "C" { EIGEN_FUNCS_API void InverseMat_FullPivLU(int dim, float a[], float ans[]) { Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(dim, dim); int count = 0; for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { mat(c, r) = a[count]; count++; } } Eigen::FullPivLU< Eigen::MatrixXf > lu(mat); Eigen::MatrixXf InvMat = mat.inverse(); count = 0; for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { ans[count] = InvMat(c, r); count++; } } } } // extern "C"
DLLファイルをUnityのPluginsフォルダに置く
以下のようにする.Pluginsフォルダが無い場合は新規作成する.
Assets/Plugins/EigenFuncs.dll
C#でDLLのWrapperを書く
以下,DLLの関数をC#側で呼び出し,逆行列演算をする関数を実装したものです.
/*--------------*/ /* EigenFunc.cs */ /*--------------*/ using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; using System.Runtime.InteropServices; //Dll public class EigenFunc { [DllImport("EigenFuncs")] private static extern void InverseMat_FullPivLU(int dim, float[] a, float[] ans); private void Matrix2Array(float[,] Mat, ref float[] Arr) { int count = 0; int dim = (int)Mathf.Sqrt(Mat.Length); for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { Arr[count] = Mat[c, r]; count++; } } } private void Array2Matrix(float[] Arr, float[,] Mat) { int count = 0; int dim = (int)Mathf.Sqrt(Mat.Length); for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { Mat[c, r] = Arr[count]; count++; } } } public float[,] InverseMatrix(float[,] Mat) { float[] arr = new float[Mat.Length]; int dim = (int)Mathf.Sqrt(Mat.Length); float[,] AnsMat = new float[dim, dim]; Matrix2Array(Mat, ref arr); float[] ansArr = new float[Mat.Length]; InverseMat_FullPivLU(dim, arr, ansArr); Array2Matrix(ansArr, AnsMat); return (float[,])AnsMat.Clone(); } }