我们将采用调式的办法一步步跟随程序运行,从而认识大概程序运行的轨迹
1 入口:main.cpp
入口在main.cpp,main函数总共只有大概30行。
做的就是三件事:
- 创建一个SimulatorBase对象并初始化(通过加载json文件)
- 选择是否使用GUI和哪种GUI
- 运行,并且运行结束后删除实例。
首先,实例化一个SimulatorBase的对象,名为base
1.1 初始化并加载场景文件
然后调用init方法,输入参数
该步骤会弹出选择窗口让你选择scene文件
如图
选择DambreakModel.json之后加载溃坝的例子。
选择完毕后会跳出命令窗口,表示加载json文件成功
1.2 选择GUI
如果使用GUI则进入if
如果不使用GUI跳到48行,执行run方法
在使用GUI的情况下,分为两种:一种是使用imgui,一种是使用tweakbar。两者都是C++的GUI库。
这里默认使用了imgui
isStaticScene表示是否是运动的边界。
如果不是调用Simulator_GUI_imgui
如果是运动边界,采用PBD相应的GUI
这一步只是选择,不进行计算
得到gui这个对象,相当于一个指针,再通过setGui反馈到base实例中去。
1.3 运行
这一步就是主体
在此处按F11进入run方法,跳转到SimulatorBase.cpp
2 SimulatorBase.cpp
通过run跳转到SimulatorBase.cpp
进入之后,发现只有三行:
- 初始化模拟
- 运行模拟
- 结束时清空
进入initSimulation
该语句会弹出空白的GUI。
此时的命令行
其余的按下不表,先来看runSimulation
其前面都是一些参数设置,最关键的是最后一行
我们进入最后一行
Simulator_GUI_imgui.cpp
此时跳入Simulator_GUI_imgui.cpp
其文件位置在
上一步中,m_gui->run()直接跳到了该文件的这一个函数。
然后再进入mainloop函数
MiniGL.cpp
此时跳入文件MiniGL.cpp, 其位置在
在该文件的mainloop函数
通过调试,我们找到了模拟计算实际计算的那一行(如下图)
即idlefunc()这一行
它是一个函数指针,这使用了cpp11的一个新特性,即std::function。
关于这一个知识点请查阅相关内容,简要来说,它就是一个包装,相当于任何函数的一个引用(或者说函数指针)。这样做的好处就是当你想在不同情况调用不同函数的时候,采用不同的引用就行了。
也正是由于用了std::function来包装,导致难以看到是具体怎么跳到实际模拟计算的内容的。我们观察堆栈,发现显示“外部代码”。
这个外部代码其实就是c++的STL
其实经过九曲十八弯地调来调去,我们能找到下一步,即timeStep()
它位于
好了,到这里就进入了实际仿真计算的代码。
GUI外壳的部分到此结束,下面就是核心计算代码。我们下次再讲。
代码流回顾
至此我们的调用顺序为:
- main()中调用base->run()
- SimulatorBase::run()中调用runSimulation()
- SimulatorBase::runSimulation()中调用m_gui->run()
- Simulator_GUI_imgui::run()中调用MiniGL::mainLoop()
- MiniGL::mainLoop()中调用idlefunc()
- SimulatorBase::timeStep()