控制流
init
从mian.cpp开始
Simulator\main.cpp
到getPBDWrapper是返回PBDWrapper的指针。
Simulator\PositionBasedDynamicsWrapper\PBDBoundarySimulator.h
这符合对象组合传递数据的模式,类似于责任链模式
让我们回到main.cpp
于是gui指针得到了PBD_Simulator_GUI_imgui类的对象。
这个对象里面包含m_pbdWrapper指针,也就是PBDWrapper的对象。
based->setGui只不过把gui指针传递给SimulatorBase的对象内部的指针m_gui而已。
来到SimulatorBase::run()
initSimulation中
初始化了boundarySimulator
让我们跳转到 Simulator\BoundarySimulator.h
发现除了
updateBoundaryForces()是被定义了的。其余的都是虚函数。定义推迟到子类。
因此这个类的意义就是一个接口。
那么真正实现它功能的子类是:
Simulator\PositionBasedDynamicsWrapper\PBDBoundarySimulator.h
它的init函数无非是ScenceLoader
run
让我们跳回最开始的SimulatorBase::run()
下一个就是runSimulation()
这个函数也没什么可说的,直接跳到其中的
即可
这次会跳转到Simulator_GUI_Base类下面的run
Simulator\GUI\Simulator_GUI_Base.h
同样的,这只是个接口类。
真正起作用的是
Simulator\GUI\imgui\Simulator_GUI_imgui.h
跳到run
跳到MiniGL的mainLoop
GUI\OpenGL\MiniGL.cpp
其中idleFunc是timeStep函数
它通过setClientIdleFunc来设定函数指针
它是在
Simulator\GUI\imgui\Simulator_GUI_imgui.cpp
中绑定的函数指针
所以idleFunc就是SimulatorBase::timeStep
timeStep
于是跳转到
Simulator\SimulatorBase.cpp
看图中的倒数第二行
这就是PBD的timeStep被调用的地方。
跳转到
Simulator\BoundarySimulator.h
这就是刚才那个边界类的接口。具体的实现在它的子类
Simulator\PositionBasedDynamicsWrapper\PBDBoundarySimulator.h
看这个实现类中的timeStep函数
他做了两件事:
- 调用子对象m_pbdWrapper中的timeStep
- 根据不同的BoundaryHandling方法选择不同的update方式。
rb指针:刚体对象
我们先来看第一件事
跳转到子对象的类PBDWrapper
Simulator\PositionBasedDynamicsWrapper\PBDWrapper.h
跳转到其timeStep方法
这里面两个对象指针:
pd是ParticleData
rb是rigidBody
我们其实现在就已经了解了如何控制刚体了:
通过
PBD::SimulationModel::RigidBodyVector &rb = m_model.getRigidBodies();
这个语句有点复杂,我们解释一下:
前面的PBD::SimulationModel::RigidBodyVector是它的类型
其中
是build\extern\install\PositionBasedDynamics\include\Simulation\SimulationModel.h
中的一个指针数组。
后面的getRigidBodies是SimulationModel类的一个方法。
同样位于
build\extern\PositionBasedDynamics\src\Ext_PBD\Simulation\SimulationModel.cpp
他们都是第三方的库的类。
如何知晓外界PBD库的?
注意!PBD::SimulationModel这个类是外界的PBD库。
那么编译器是怎么找到这个外界类的呢?
这就是CMake配置的问题了。
首先必定是#include了响应的头文件
头文件为
那么,CMake是怎么知道这个Simulation是PBD外界库的Simulation类而非Splishsplash内部的类呢?
这就让我们来到
Simulator\CMakeLists.txt
先设定一个变量SIMULATION_LINK_LIBRARIES,保存所有链接的对象。
同时设定SIMULATION_DEPENDENCIES
然后将SimulatorBase添加为一个目标,编译成静态库
然后把SIMULATION_LINK_LIBRARIES
链接到目标的静态库SimulatorBase上面。
其中add_dependencies这个命令的作用只是让SIMULATION_DEPENDENCIES先编译,从而保证链接库的时候,依赖项是存在的。
最后增加一个可执行目标,即SPHSimulator。我们所有的程序都是从这个目标开始的。
回归我们的原问题:include的时候怎么知道这个Simulation类是PBD的?
我们要搜索PBD_includes
跳转到 CMake\SetUpExternalProjects.cmake
发现最后一句是target_include_directories
这就是直接include到Simulation的秘密:
因为编译SimulartorBase静态库的时候,是和splishsplash这个目标(也是个静态库)互不干涉的。
TimeStepController::step
让我们回到PBDWrapper类
发现m_timeStep指针指向TimeStepController对象
这个类位于
build\extern\PositionBasedDynamics\src\Ext_PBD\Simulation\TimeStepController.h
于是step就是跳转到
注意看下面的positionConstraintProjection(model);
这个就是约束投影
因此这个就是PBD的模拟实现。