Third Party Extensions by Singleton
In order to make more components can be easily plugged into the engine, a type mechanism needs to be designed so that
users can easily define functions. The important issue of these is to incorporate component updates into the main loop.
For example, the ComponentManager
built into the current engine. These updates may only require a script, and may need
to rely on the WebGPU interface. Therefore, a separate function in the engine is removed from the main loop for
expansion:
void ForwardApplication::updateGPUTask(wgpu::CommandEncoder& commandEncoder) {
_shadowManager->draw(commandEncoder);
_lightManager->draw(commandEncoder);
}
Currently ShadowManager
and LightManager
have been separated from Scene
as separate manager types. In addition to
inserting the main loop, what is more important is how the components are collected into the manager class for unified
management and event dispatch. Since these manager classes are constructed by the user in their own Application, rather
than the default objects stored in Scene
, it is impossible to save their own pointers to the manager class
when onEnable
like ComponentManager
among. In order to solve this problem, the singleton pattern is introduced,
which enables types such as light components to be registered directly through a singleton:
void PointLight::_onEnable() {
LightManager::getSingleton().attachPointLight(this);
}
void PointLight::_onDisable() {
LightManager::getSingleton().detachPointLight(this);
}
Singleton Template Classâ
In order to facilitate the implementation of the singleton pattern, a template class is provided to:
/*
* Template class for creating single-instance global classes.
*/
template<typename T>
class Singleton {
private:
/** @brief Explicit private copy constructor. This is a forbidden operation.*/
Singleton(const Singleton<T> &);
/** @brief Private operator= . This is a forbidden operation. */
Singleton &operator=(const Singleton<T> &);
protected:
static T *msSingleton;
public:
Singleton(void) {
assert(!msSingleton);
msSingleton = static_cast< T * >( this );
}
~Singleton(void) {
assert(msSingleton);
msSingleton = 0;
}
static T &getSingleton(void) {
assert(msSingleton);
return (*msSingleton);
}
static T *getSingletonPtr(void) {
return msSingleton;
}
};
Template classes use singular template recursion to construct in subclasses:
class LightManager : public Singleton<LightManager> {
public:
static LightManager &getSingleton(void);
static LightManager *getSingletonPtr(void);
};
template<> inline LightManager* Singleton<LightManager>::msSingleton{nullptr};
LightManager *LightManager::getSingletonPtr(void) {
return msSingleton;
}
LightManager &LightManager::getSingleton(void) {
assert(msSingleton);
return (*msSingleton);
}
note
The singleton design can also make a series of compile-time parameters that depend on static methods become run-time parameters.
Third Party Extensionsâ
For third-party extensions, several types are generally involved:
- The manager class is constructed using the singleton pattern.
- The component inherits
Component
and inserts itself into the manager class inonEnable
. - Inherit
updateGPUTask
to insert GPU-related update functions into the main loop, and inheritScript
to insert GPU-independent update functions into the main loop. Available viaLightManager
,ShadowManager
Check out similar implementations. Subsequent components such as particles and cloth will be implemented with similar architectures.