Architecture: DotScene needs callback/listener implementation
To implement features like physics and custom user stuff the callback/listener/delegate interface needs to be implemented to avoid uncontrollable external dependencies of DotScene plugin and general use in games and user apps where custom logic is needed for nodes/elements processing. Side note: the biggest problem is not implementation but ownership of the listener in source code as plugin can't export header files and this header would not "belong" in OgreMain. But this issue have to be solved somehow to move farther.
The suggesion:
- Implement singleton DotScene::Config inside DotScene among listener headers.
- If DotScene is enabled this header is seen by all Ogre components and dependencies by CMake, can be added to OgreMain's PUBLIC include path for that.
- This singleton gets being accessible anywhere, the listeners can be added/removed.
- DotSceneLoader reads the configuration from that singleton and configures parameters, runs listeners, etc.
This way it will be zero-cost on user side without external dependencies. If plugin is not available then header is not available too. If plugin is not loaded the data will not be used. It is waste but harmless. But if it is not included, no waste on user side too.
how about this? Not pretty, but does not need any new API:
/// user
static void callback(SceneNode* sn) {}
snode->getUserObjectBindings().setUserAny("childCreatedListener", &callback);
/// dotscene
auto cb = any_cast<void(*)(SceneNode*)>(rootNode->getUserObjectBindings().getUserAny("childCreatedListener"));
Well, the callback requires to be functor i.e. user-defined class with called method or operator()... i.e. it needs own state. For my case with ECS usage it is not really a problem, but... Another approach would be to provide a std::pair of void * and callback but it looks not very safe as won't be checked by compiler... Also I wonder if compiler will even allow that with current set of options...
auto cb = any_cast<std::pair<void *, void (*)(SceneNode *, /*more params */> >(rootNode->getUserObjectBindings().getUserAny("childCreatedListener"));
The additional problem is setting default hooks for modules like OgreBullet to prevent dependency multiplication. This have to be still set through codec methods... which makes me thinking that would be better direction for that approach...
auto listener = std::function<void(SceneNode*)>([](SceneNode* sn) {});
snode->getUserObjectBindings().setUserAny("callback", listener);
auto cb = any_cast<std::function<void(SceneNode*)>>(snode->getUserObjectBindings().getUserAny("callback"));
I guess std::function<void(SceneNode*)>([](SceneNode* sn) {}); is just plain C function pointer without context and adding anything here will produce different type... I updated message above, please check...
the functor will require class with operator(); and that will produce different type and that won't pass any_cast
#include <iostream>
#include <functional>
class MyFunctor {
public:
void operator()(int x) const {
std::cout << "MyFunctor called with: " << x << std::endl;
}
};
int main() {
MyFunctor functor_instance;
std::function<void(int)> func = functor_instance; // std::function stores the functor
// Invoke the functor through std::function
func(10);
return 0;
}
as that is template magic
Will try that properly, may be that would end up easier to do with std::function though... Probably I will learn about new C++ things...
no, std::function can be anything that matches the template signature (raw C function, bound class method, lambda), see:
https://en.cppreference.com/w/cpp/utility/functional/function.html#Example
Ok, I'll try with std::function but still some way to set default hooks/listeners is needed to allow OgreBullet to add them which will solve dependency issue.
I currently busy with collision of btGhostObject vs btRigidBody then get back to this.
I guess the default hooks should be set using codec interface, i.e. OgreBullet checks if "scene" codec is available and sets some default options. Somehow...
Well, it is possible to force user to set some Anys on root node with default hooks but it sounds unfriendly (can make OgreBullet function which will do that) But that sounds quirky.
whats wrong with handling bullet directly in dotscene, like we do with Terrain?
https://github.com/OGRECave/ogre/blob/cca1be99b285b27d24dd57aab58794cbb9e39d65/PlugIns/DotScene/src/DotSceneLoader.cpp#L293
The problem is DotScene becomes dependent on Bullet instantly via OgreBullet. And when static building OgreBullet for some reason does not propagate Bullet dependency via CMake exports. Also that will make every external library depend on Bullet even not using/depending on OgreBullet (because that will add dependency).... Explaining short we get dependency explosion. I.e. in something like ogre-procedural you see Bullet's unresolved symbols which is totally unexpected by logic. If using hooks, nothing like that happens.
The difference with Terrain is Terrain is self-contained without external/potentially external dependencies.