OpenGL-脚本-C#
原创
©著作权归作者所有:来自51CTO博客作者mb639813d0717d1的原创作品,请联系作者获取转载授权,否则将追究法律责任
Mono
就我个人的体验来说,C#
简单易学、开发速度快,用C#
编程是会让人上瘾的,这里想用C#
做脚本语言,自然而然就需要借助Mono
了。不过因为一些理论和定义又生涩又占篇幅,我不愿记录太多,留下关键字,便于搜索就足够了。使用Mono
还有一个重要原因就是Mono
可以植入到程序里面,有些软件有一个超级不好的体验就是在执行它之前,需要安装一堆的组件。我也就希望借助mono减少这些不好的体验。
Mono:http://www.mono-project.com/
Embedding Mono:http://www.mono-project.com/docs/advanced/embedding/
集成Mono
- 下载mono:http://www.mono-project.com/download/stable/
安装之后,可以将mono
的文件夹拷出来,因为原来的Mono
差不多占400M+的空间,所以我们可以根据自己的需求删除一些多余的东西,我大概删了一部分精简到190M,我看了一下Unity
的,它精简到了90M,可能我还有部分没有删掉。 - 将精简后的
mono
拷贝到自己的工程的相对路径下,其他还是跟C++
工程的配置一样,添加mono
的头文件路径,库路径,并添加附加依赖项,我这里是mono-2.0-sgen.lib
- 在工程目录下新建一个
C#
脚本
using System;
namespace NameSp
{
public class MainTest
{
public void Main()
{
}
}
}
- 在上面
Embedding Mono
的文档中知道要想在mono runtime
中执行c#脚本,需要先编译.cs
文件,我这里都使用的相对路径,其实我们可以把这个接口留给wxwidgets
的一个编译按钮,可以手动点击编译,然后再运行,这里为了快速下一步,我都是运行直接编译运行。
//编译脚本
void LoadCSharpLibrary::Compile()
{
const std::string scriptPath("MainTest.cs");
std::string command = "..\\..\\Libraries\\Mono\\bin\\mcs " + scriptPath + " -t:library";
//Compile the script
system(command.c_str());
}
- 运行
C#
,好多示例都是直接运行的静态函数,我这里就故意没有定义MainTest
的Main
函数为静态函数,让我们去生成一个对象再来调用。其实mono
的操作感觉跟反射的使用有点类似。最后调用这两个函数,因为我没有在MainTest
做任何明显的操作,所以没反应也正常,只要没报错,就先继续往下写。
void LoadCSharpLibrary::Load()
{
// Program.cs所编译dll所在的位置
const char* managed_binary_path = "MainTest.dll";
mono_set_dirs("..\\..\\Libraries\\Mono\\lib",
"..\\..\\Libraries\\Mono\\etc");
//获取应用域
domain = mono_jit_init("Test");
//加载程序集ManagedLibrary.dll
MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path);
MonoImage* image = mono_assembly_get_image(assembly);
//获取MonoClass
MonoClass* main_class = mono_class_from_name(image, "NameSp", "MainTest");
//获取要调用的MonoMethodDesc,主要调用过程
MonoMethodDesc* entry_point_method_desc = mono_method_desc_new("NameSp.MainTest::Main()", true);
MonoMethod* entry_point_method = mono_method_desc_search_in_class(entry_point_method_desc, main_class);
mono_method_desc_free(entry_point_method_desc);
MonoObject *instance = mono_object_new(domain, main_class);
//调用方法
mono_runtime_invoke(entry_point_method, instance, NULL, NULL);
//释放应用域
mono_jit_cleanup(domain);
}
C++与C#的交互
之所以能使用脚本能进行快速开发还有个很重要的原因就是我们给它封装足够多的接口,下面就来试试C#
调用C++
的接口。
//三角形的变换矩阵
glm::mat4 trans;
//缩放0.5倍
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
//逆时针旋转90度
trans = glm::rotate(trans, 90.0f, glm::vec3(0.0, 0.0, 1.0));
//向x轴位移1.5
trans = glm::translate(trans, glm::vec3(1.2, 0.0f, 0.0));
- 这里示例都比较简单,我准备将
90.0f
提出来作为一个变量,给C#
去设置,定义一个角度的变量,和设置角度的函数
//角度
static int _angle;
static void SetAngle(float angle);
//设置初始值
int OpenGLCanvas::_angle = 0;
//设置角度
void OpenGLCanvas::SetAngle(float angle)
{
_angle = angle;
}
//...
//三角形的变换矩阵
glm::mat4 trans;
//缩放0.5倍
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
const float angle = _angle;
//逆时针旋转角度
trans = glm::rotate(trans, angle, glm::vec3(0.0, 0.0, 1.0));
//向x轴位移1.5
trans = glm::translate(trans, glm::vec3(1.2, 0.0f, 0.0));
//...
- 在
C#
中声明C++
函数,并开始调用它
using System;
using System.Runtime.CompilerServices;
namespace NameSp
{
public class MainTest
{
[MethodImpl(MethodImplOptions.InternalCall)]
public extern static void SetAngle(float angle);
public void Main()
{
SetAngle(0.0f);
}
}
}
- 需要再在
mono
的接口中做一下函数绑定,我认为mono_add_internal_call
的理解就可以简单一点,就是在调用NameSp.MainTest::SetAngle
的时候,调用OpenGLCanvas::SetAngle
void LoadCSharpLibrary::Load()
{
// Program.cs所编译dll所在的位置
const char* managed_binary_path = "MainTest.dll";
mono_set_dirs("..\\..\\Libraries\\Mono\\lib",
"..\\..\\Libraries\\Mono\\etc");
//获取应用域
domain = mono_jit_init("Test");
//加载程序集ManagedLibrary.dll
MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path);
MonoImage* image = mono_assembly_get_image(assembly);
//获取MonoClass
MonoClass* main_class = mono_class_from_name(image, "NameSp", "MainTest");
//获取要调用的MonoMethodDesc
MonoMethodDesc* entry_point_method_desc = mono_method_desc_new("NameSp.MainTest::Main()", true);
MonoMethod* entry_point_method = mono_method_desc_search_in_class(entry_point_method_desc, main_class);
mono_method_desc_free(entry_point_method_desc);
//函数绑定
mono_add_internal_call("NameSp.MainTest::SetAngle", reinterpret_cast<void*>(OpenGLCanvas::SetAngle));
MonoObject *instance = mono_object_new(domain, main_class);
//调用方法
mono_runtime_invoke(entry_point_method, instance, NULL, NULL);
//释放应用域
mono_jit_cleanup(domain);
}
- 编译运行, 正正的一个三角形
- 然后我们再将
C#
的设置角度改变一下
using System;
using System.Runtime.CompilerServices;
namespace NameSp
{
public class MainTest
{
[MethodImpl(MethodImplOptions.InternalCall)]
public extern static void SetAngle(float angle);
public void Main()
{
SetAngle(135.0f);
}
}
}
- 截图可以看到三角形确实旋转了,就证明我们的
mono
已经植入成功了
扩展
用脚本还有一个不错的想法就是自己可以将脚本封装成可视化编程,后面可以来试试