diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2023-12-21 17:44:03 -0500 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2023-12-23 11:42:23 -0500 |
| commit | fa7fc343a0e444da72938fad58d219cf52228976 (patch) | |
| tree | 24630be0c54f9768a13f32c5970558768e343543 | |
| parent | 6fa3b137c74536d2bab77f3309ca5b4c60953fe0 (diff) | |
plugin checkpoint - multiple plugins and collision callbacks
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | editor/editor.cpp | 53 | ||||
| -rw-r--r-- | editor/main.cpp | 10 | ||||
| -rw-r--r-- | example/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | example/example.cpp | 27 | ||||
| -rw-r--r-- | example/example.hpp | 14 | ||||
| -rw-r--r-- | example/example_export.h | 42 | ||||
| -rw-r--r-- | src/components.hpp | 3 | ||||
| -rw-r--r-- | src/ecs.cpp | 51 | ||||
| -rw-r--r-- | src/ecs.hpp | 2 | ||||
| -rw-r--r-- | src/game.cpp | 39 | ||||
| -rw-r--r-- | src/physics.cpp | 32 | ||||
| -rw-r--r-- | src/plugins.cpp | 74 | ||||
| -rw-r--r-- | src/plugins.hpp | 22 | ||||
| -rw-r--r-- | src/project.cpp | 19 | ||||
| -rw-r--r-- | src/window.cpp | 18 |
16 files changed, 375 insertions, 46 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ede1a9..0900da7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,3 +198,4 @@ add_dependencies(pke shaders) add_subdirectory(editor) add_subdirectory(runtime) add_subdirectory(test) +add_subdirectory(example) diff --git a/editor/editor.cpp b/editor/editor.cpp index 5016070..db97802 100644 --- a/editor/editor.cpp +++ b/editor/editor.cpp @@ -696,6 +696,23 @@ void RecordImGuiUBO() { ImGui::End(); } +bool RecordImGui_CallbackSelectModal(long &selectedIndex) { + bool returnValue = false; + if (ImGui::BeginPopupModal("CallbackSelect", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + long count = 0; + auto *signatures = PkePlugin_GetSortedSignatures(count); + for (long i = 0; i < count; ++i) { + const CallbackSignature &sig = signatures[i]; + if (ImGui::Selectable(sig, selectedIndex == i)) { + returnValue = true; + selectedIndex = i; + } + } + ImGui::EndPopup(); + } + return returnValue; +} + void RecordImGuiModalCreateEntityType() { if (ImGui::BeginPopupModal("CreateEntityType", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { static char modelsDir[64]; @@ -835,6 +852,24 @@ void RecordImGui_CompGrBinds(bool readonly, CompGrBinds *component) { ImGui::InputScalar("InstanceBufferMaxCount", ImGuiDataType_U32, &component->instanceBufferMaxCount, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("Instance Count", ImGuiDataType_U32, &component->instanceCounter, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly); + static long index = -1; + if (ImGui::Button("Clear")) { + index = -1; + component->collisionCallback.name[0] = '\0'; + } + ImGui::SameLine(); + if (ImGui::Button("Change")) { + index = -1; + ImGui::OpenPopup("CallbackSelect"); + } + ImGui::SameLine(); + ImGui::Text("Collision Callback: '%s'", component->collisionCallback.name); + if (RecordImGui_CallbackSelectModal(index)) { + long x = 0; + memcpy(component->collisionCallback.name, PkePlugin_GetSortedSignatures(x)[index], 16); + PkePlugin_SetSignatureFunc(&component->collisionCallback); + } + ImGui::Spacing(); } @@ -868,6 +903,24 @@ void RecordImGui_CompInstPos(bool readonly, CompInstance *component) { changed = ImGui::InputScalar("Phys - Collision Layer", ImGuiDataType_U16, &component->physicsLayer, nullptr, nullptr, nullptr, inputTextFlags) || changed; changed = ImGui::InputScalar("Phys - Collision Mask", ImGuiDataType_U16, &component->physicsMask, nullptr, nullptr, nullptr, inputTextFlags) || changed; + static long index = -1; + if (ImGui::Button("Clear")) { + index = -1; + component->collisionCallback.name[0] = '\0'; + } + ImGui::SameLine(); + if (ImGui::Button("Change")) { + index = -1; + ImGui::OpenPopup("CallbackSelect"); + } + ImGui::SameLine(); + ImGui::Text("Collision Callback: '%s'", component->collisionCallback.name); + if (RecordImGui_CallbackSelectModal(index)) { + long x = 0; + memcpy(component->collisionCallback.name, PkePlugin_GetSortedSignatures(x)[index], 16); + PkePlugin_SetSignatureFunc(&component->collisionCallback); + } + ImGui::InputScalar("Phys - Rigid Body", ImGuiDataType_U64, &component->bt.rigidBody, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("Phys - Motion State", ImGuiDataType_U64, &component->bt.motionState, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); diff --git a/editor/main.cpp b/editor/main.cpp index af0e66a..2dee016 100644 --- a/editor/main.cpp +++ b/editor/main.cpp @@ -23,10 +23,12 @@ int main(int argc, char *argv[]) { { pkeSettings.isSimulationPaused = true; pkeSettings.isShowingEditor = true; - pkePlugin.OnInit = PkeEditor_Init; - pkePlugin.OnTick = PkeEditor_Tick; - pkePlugin.OnTeardown = PkeEditor_Teardown; - pkePlugin.OnImGuiRender = PkeEditor_RecordImGui; + LoadedPkePlugins.Push({ + .OnInit = PkeEditor_Init, + .OnTick = PkeEditor_Tick, + .OnTeardown = PkeEditor_Teardown, + .OnImGuiRender = PkeEditor_RecordImGui, + }); } // run PkeArgs_Parse(argc, argv); diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..a895b5d --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,14 @@ +project(pke_example VERSION 0.0) + +set(PKE_EDITOR_SOURCE_FILES + # example_export.h + example.hpp + example.cpp +) + +add_library(pke_example + ${PKE_EDITOR_SOURCE_FILES} +) + +target_link_libraries(pke_example PUBLIC pke) +target_include_directories(pke_example PUBLIC pke) diff --git a/example/example.cpp b/example/example.cpp new file mode 100644 index 0000000..b870806 --- /dev/null +++ b/example/example.cpp @@ -0,0 +1,27 @@ + +#include "example.hpp" + +#include "components.hpp" + +void OnEntityTypeCollision(const EntityHandle &lhs, const EntityHandle &rhs) { + fprintf(stdout, "[Example::OnEntityTypeCollision] Called\n"); +} + +void OnEntityCollision(const EntityHandle &lhs, const EntityHandle &rhs) { + fprintf(stdout, "[Example::OnEntityCollision] Called\n"); +} + +void OnInit() { + PkeArray_Add(&pkePluginCallbacks, PkeCallback { + .name = "OnEntTypeColl", + .func = reinterpret_cast<void *>(OnEntityTypeCollision), + }); + PkeArray_Add(&pkePluginCallbacks, PkeCallback { + .name = "OnEntColl", + .func = reinterpret_cast<void *>(OnEntityCollision), + }); +} + +PKEPluginInterface pkePluginInterface { + .OnInit = OnInit, +}; diff --git a/example/example.hpp b/example/example.hpp new file mode 100644 index 0000000..c64b3e9 --- /dev/null +++ b/example/example.hpp @@ -0,0 +1,14 @@ +#ifndef PKE_EXAMPLE_HPP +#define PKE_EXAMPLE_HPP + +#include "plugins.hpp" + +// EXAMPLE_EXPORT void OnEntityTypeCollision(const EntityHandle &lhs, const EntityHandle &rhs); +// void OnEntityTypeCollision(const EntityHandle &lhs, const EntityHandle &rhs); + +// EXAMPLE_EXPORT void OnEntityCollision(const EntityHandle &lhs, const EntityHandle &rhs); +// void OnEntityCollision(const EntityHandle &lhs, const EntityHandle &rhs); + +extern PKEPluginInterface pkePluginInterface; + +#endif /* PKE_EXAMPLE_HPP */ diff --git a/example/example_export.h b/example/example_export.h new file mode 100644 index 0000000..53bf208 --- /dev/null +++ b/example/example_export.h @@ -0,0 +1,42 @@ + +#ifndef EXAMPLE_EXPORT_H +#define EXAMPLE_EXPORT_H + +#ifdef EXAMPLE_STATIC_DEFINE +# define EXAMPLE_EXPORT +# define EXAMPLE_NO_EXPORT +#else +# ifndef EXAMPLE_EXPORT +# ifdef extension_EXPORTS + /* We are building this library */ +# define EXAMPLE_EXPORT extern "C" __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define EXAMPLE_EXPORT __attribute__((visibility("default"))) +# endif +# endif + +# ifndef EXAMPLE_NO_EXPORT +# define EXAMPLE_NO_EXPORT __attribute__((visibility("hidden"))) +# endif +#endif + +#ifndef EXAMPLE_DEPRECATED +# define EXAMPLE_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef EXAMPLE_DEPRECATED_EXPORT +# define EXAMPLE_DEPRECATED_EXPORT EXAMPLE_EXPORT EXAMPLE_DEPRECATED +#endif + +#ifndef EXAMPLE_DEPRECATED_NO_EXPORT +# define EXAMPLE_DEPRECATED_NO_EXPORT EXAMPLE_NO_EXPORT EXAMPLE_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef EXAMPLE_NO_DEPRECATED +# define EXAMPLE_NO_DEPRECATED +# endif +#endif + +#endif /* EXAMPLE_EXPORT_H */ diff --git a/src/components.hpp b/src/components.hpp index e99481e..86c5727 100644 --- a/src/components.hpp +++ b/src/components.hpp @@ -4,6 +4,7 @@ #include "dynamic-array.hpp" #include "macros.hpp" #include "physics.hpp" +#include "plugins.hpp" #include <BulletDynamics/Dynamics/btRigidBody.h> #include <LinearMath/btDefaultMotionState.h> @@ -65,6 +66,7 @@ struct CompGrBinds { uint32_t instanceCounter = 0; uint32_t instanceBufferMaxCount = 0; VkDeviceSize instanceOffsets = 0; + PkeCallback collisionCallback{}; }; struct InstPos { @@ -84,6 +86,7 @@ struct CompInstance { PhysicsCollision physicsLayer = PhysicsCollision{1}; PhysicsCollision physicsMask = PhysicsCollision{1}; InstBt bt; + PkeCallback collisionCallback{}; bool isNeedingUpdated = false; }; diff --git a/src/ecs.cpp b/src/ecs.cpp index 4580abe..c33caaa 100644 --- a/src/ecs.cpp +++ b/src/ecs.cpp @@ -327,6 +327,57 @@ void ECS_Tick_Late(double delta) { } } +void FindComponents(EntityHandle handle, Entity *&ent, CompInstance *&compInst, CompGrBinds *&grBinds) { + ECS_GetEntity_Inner(handle, ent); + if (ent == nullptr) + return; + GrBindsHandle grBindsHandle = ent->grBindsHandle; + if (ent->instanceHandle != InstanceHandle_MAX) { + compInst = &Comp_Instance_BucketContainer.buckets[ent->instanceHandle.bucketIndex].instances[ent->instanceHandle.itemIndex]; + if (grBindsHandle == GrBindsHandle_MAX) + grBindsHandle = compInst->grBindsHandle; + } + if (grBindsHandle != GrBindsHandle_MAX) { + grBinds = &Comp_GrBinds_BucketContainer.buckets[grBindsHandle.bucketIndex].compGrBinds[grBindsHandle.itemIndex]; + } +} + +void ECS_HandleCollision(EntityHandle lhs, EntityHandle rhs) { + Entity *lhsEnt = nullptr; + CompInstance *lhsInst = nullptr; + CompGrBinds *lhsGrBinds = nullptr; + FindComponents(lhs, lhsEnt, lhsInst, lhsGrBinds); + + Entity *rhsEnt = nullptr; + CompInstance *rhsInst = nullptr; + CompGrBinds *rhsGrBinds = nullptr; + FindComponents(rhs, rhsEnt, rhsInst, rhsGrBinds); + + void (*lhsColFunc)(EntityHandle, EntityHandle) = nullptr; + void (*rhsColFunc)(EntityHandle, EntityHandle) = nullptr; + + if (lhsInst && lhsInst->collisionCallback.func != nullptr) { + lhsColFunc = reinterpret_cast<void (*)(EntityHandle, EntityHandle)>(lhsInst->collisionCallback.func); + } + if (lhsColFunc == nullptr && lhsGrBinds && lhsGrBinds->collisionCallback.func) { + lhsColFunc = reinterpret_cast<void (*)(EntityHandle, EntityHandle)>(lhsGrBinds->collisionCallback.func); + } + + if (rhsInst && rhsInst->collisionCallback.func != nullptr) { + rhsColFunc = reinterpret_cast<void (*)(EntityHandle, EntityHandle)>(rhsInst->collisionCallback.func); + } + if (rhsColFunc == nullptr && rhsGrBinds && rhsGrBinds->collisionCallback.func) { + rhsColFunc = reinterpret_cast<void (*)(EntityHandle, EntityHandle)>(rhsGrBinds->collisionCallback.func); + } + + if (lhsColFunc) { + lhsColFunc(lhs, rhs); + } + if (rhsColFunc) { + rhsColFunc(lhs, rhs); + } +} + CompGrBinds &ECS_CreateGrBinds(EntityHandle entHandle) { assert(entHandle != EntityHandle_MAX); diff --git a/src/ecs.hpp b/src/ecs.hpp index 7ac08e2..4deacaf 100644 --- a/src/ecs.hpp +++ b/src/ecs.hpp @@ -27,6 +27,8 @@ void ECS_MarkForRemoval(EntityHandle entHandle); uint64_t ECS_GetEntities_BucketCount(); Entity *ECS_GetEntities(uint64_t bucketIndex, uint64_t &itemCount); +void ECS_HandleCollision(EntityHandle lhs, EntityHandle rhs); + CompGrBinds &ECS_CreateGrBinds(EntityHandle entHandle); CompGrBinds *ECS_GetGrBinds(EntityHandle entHandle); CompGrBinds *ECS_GetGrBinds(GrBindsHandle grBindsHandle); diff --git a/src/game.cpp b/src/game.cpp index 20e67a0..5df32a0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -47,6 +47,7 @@ const char *PKE_FILE_INSTANCE_POS_SCALE = "InstPos::Scale: "; const char *PKE_FILE_INSTANCE_PHYSICS_MASS = "InstPos::Mass: "; const char *PKE_FILE_INSTANCE_PHYSICS_COLLISION_LAYER = "InstPos::CollisionLayer: "; const char *PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK = "InstPos::CollisionMask: "; +const char *PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE = "Inst::CollisionCallbackSignature: "; const char *PKE_FILE_CAMERA_POS = "Cam::Pos: "; const char *PKE_FILE_CAMERA_ROT = "Cam::Rot: "; @@ -141,6 +142,9 @@ void SerializeInstance(std::ofstream &stream, const CompInstance &comp) { if (collisionMask != c.physicsMask) { stream << PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK << static_cast<PhysicsCollision_T>(collisionMask) << std::endl; } + if (comp.collisionCallback.name[0] != '\0') { + stream << PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE << comp.collisionCallback.name << std::endl; + } } void ParseCamera(LevelHandle levelHandle, std::ifstream &stream) { @@ -238,6 +242,7 @@ void ParseInstance(EntityHandle parentEntHandle, std::ifstream &stream) { instPos.posRot.setIdentity(); instPos.scale = btVector3(1, 1, 1); instPos.mass = 1.f; + comp.collisionCallback.name[0] = '\0'; char entTypeCode[21]; memset(reinterpret_cast<void *>(entTypeCode), '\0', 21); while (stream.getline(readLine, readLineLength)) { @@ -255,6 +260,9 @@ void ParseInstance(EntityHandle parentEntHandle, std::ifstream &stream) { auto entityHandle = ECS_CreateEntity(parentEntHandle); auto &compInst = ECS_CreateInstance(entityHandle, et.entityHandle); + strncpy(compInst.collisionCallback.name, comp.collisionCallback.name, 16); + PkePlugin_SetSignatureFunc(&compInst.collisionCallback); + compInst.physicsLayer = comp.physicsLayer; compInst.physicsMask = comp.physicsMask; btVector3 localInertia(0, 0, 0); @@ -356,6 +364,11 @@ void ParseInstance(EntityHandle parentEntHandle, std::ifstream &stream) { assert(result == STR2NUM_ERROR::SUCCESS); continue; } + if (strstr(readLine, PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE)) { + uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE); + strncpy(comp.collisionCallback.name, readLine + prefixLen, 16); + continue; + } } } @@ -496,9 +509,11 @@ void Game_Tick(double delta) { ECS_Tick(delta); PkeInput_Tick(delta); - // TODO invoke external ticks here - if (pkePlugin.OnTick) { - pkePlugin.OnTick(delta); + const auto pluginCount = LoadedPkePlugins.Count(); + for (long i = 0; i < pluginCount; ++i) { + if (LoadedPkePlugins[i].OnTick != nullptr) { + LoadedPkePlugins[i].OnTick(delta); + } } EntityType_Tick_Late(delta); @@ -517,11 +532,14 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { CreateWindow(windowProps); PkeInput_Init(); EntityType_Init(); - if (pkeSettings.args.pluginPath) { - Pke_LoadPlugin(pkeSettings.args.pluginPath); + if (pkeSettings.args.pluginPath != nullptr) { + PkePlugin_Load(pkeSettings.args.pluginPath); } - if (pkePlugin.OnInit) { - pkePlugin.OnInit(); + const long pluginCount = LoadedPkePlugins.Count(); + for (long i = 0; i < pluginCount; ++i) { + if (LoadedPkePlugins[i].OnInit != nullptr) { + LoadedPkePlugins[i].OnInit(); + } } PkeProject_Load(pkeSettings.args.projectPath); @@ -612,8 +630,11 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { #ifndef NDEBUG Pke_DebugPrint(); #endif - if (pkePlugin.OnTeardown) { - pkePlugin.OnTeardown(); + const auto pluginCount = LoadedPkePlugins.Count(); + for (long i = 0; i < pluginCount; ++i) { + if (LoadedPkePlugins[i].OnTeardown) { + LoadedPkePlugins[i].OnTeardown(); + } } Game_Teardown(); Event_Teardown(); diff --git a/src/physics.cpp b/src/physics.cpp index e960059..4825955 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -3,6 +3,7 @@ #include "components.hpp" #include "dynamic-array.hpp" +#include "ecs.hpp" #include "game-settings.hpp" #include <LinearMath/btAlignedAllocator.h> @@ -26,6 +27,11 @@ btCollisionDispatcher *btDispatcher = nullptr; btBroadphaseInterface *btBroadphase = nullptr; btConstraintSolver *btSolver = nullptr; +struct EntityCollision { + EntityHandle a, b; +}; +PkeArray<EntityCollision> collisionsThisTick{}; + void *pke_btAlignedAllocFunc(size_t size, int alignment) { void *ptr = Pke_New(size, alignment, MemBkt_Bullet); bulletAllocs->Push({ptr, size}); @@ -68,8 +74,25 @@ void pke_btFreeFunc(void *memBlock) { struct CollisionHandlerStruct : public btOverlapFilterCallback { ~CollisionHandlerStruct() override {} bool needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const override { - return (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) || + auto collided = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) | (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask); + if (collided) { + const auto *col0 = static_cast<btCollisionObject *>(proxy0->m_clientObject); + const auto *col1 = static_cast<btCollisionObject *>(proxy1->m_clientObject); + if (col0 && col1) { + EntityHandle ent0{}; + ent0.hash = reinterpret_cast<uint64_t>(col0->getUserPointer()); + EntityHandle ent1{}; + ent1.hash = reinterpret_cast<uint64_t>(col1->getUserPointer()); + if (ent0 != EntityHandle_MAX && ent1 != EntityHandle_MAX) { + PkeArray_Add<EntityCollision>(&collisionsThisTick, { + .a = ent0, + .b = ent1, + }); + } + } + } + return collided; } } collisionHandlerStruct; @@ -102,7 +125,12 @@ void Physics_Init() { int32_t Physics_Tick(double delta) { if (pkeSettings.isSimulationPaused == true) return 0; - return BtDynamicsWorld->stepSimulation(delta, 1); + PkeArray_SoftReset(&collisionsThisTick); + auto tickCount = BtDynamicsWorld->stepSimulation(delta, 1); + for (long i = 0; i < collisionsThisTick.next; ++i) { + ECS_HandleCollision(collisionsThisTick.data[i].a, collisionsThisTick.data[i].b); + } + return tickCount; } void Physics_Teardown() { diff --git a/src/plugins.cpp b/src/plugins.cpp index bfd2e9b..5b0b817 100644 --- a/src/plugins.cpp +++ b/src/plugins.cpp @@ -1,22 +1,24 @@ -#include "macros.hpp" #include "plugins.hpp" -#include <cstdio> -#include <dlfcn.h> +#include "array.hpp" +#include "macros.hpp" -PKEPluginInterface pkePlugin{}; +#include <cassert> +#include <cstdio> +#include <cstring> -void *loadedPlugin = nullptr; +#ifdef WIN32 + // TODO +#else + #include <dlfcn.h> +#endif -void Pke_UpdatePlugin(const PKEPluginInterface &plugin) { - pkePlugin.OnInit = plugin.OnInit; - pkePlugin.OnTick = plugin.OnTick; - pkePlugin.OnTeardown = plugin.OnTeardown; - pkePlugin.OnImGuiRender = plugin.OnImGuiRender; -} +DynArray<PKEPluginInterface> LoadedPkePlugins; +PkeArray<PkeCallback> pkePluginCallbacks; +PkeArray<CallbackSignature> sortedSignatures; -void Pke_LoadPlugin(const char *path) { +void PkePlugin_Load(const char *path) { if (path == nullptr || path == CAFE_BABE(void)) { return; } @@ -33,19 +35,43 @@ void Pke_LoadPlugin(const char *path) { dlclose(extension); return; } - Pke_UpdatePlugin(*interface); - loadedPlugin = interface; + interface->pluginHandle = extension; + LoadedPkePlugins.Push(*interface); +} + +PkeCallback *PkePlugin_FindSignature(const CallbackSignature &sig) { + for (long i = 0; i < pkePluginCallbacks.next; ++i) { + if (strncmp(sig, pkePluginCallbacks.data[i].name, 16) == 0) { + return &pkePluginCallbacks.data[i]; + } + } + return nullptr; } -void Pke_UnloadPlugin() { - pkePlugin = { - .OnInit = nullptr, - .OnTick= nullptr, - .OnTeardown= nullptr, - .OnImGuiRender= nullptr, - }; - if (loadedPlugin != nullptr && loadedPlugin != CAFE_BABE(void)) { - dlclose(loadedPlugin); - loadedPlugin = CAFE_BABE(void); +void PkePlugin_SetSignatureFunc(PkeCallback *sig) { + assert(sig != nullptr); + if (sig->name[0] == '\0') { + return; + } + auto *registeredSig = PkePlugin_FindSignature(sig->name); + if (registeredSig == nullptr) { + fprintf(stdout, "[WARN] [PkePlugin_SetSignatureFunc] Failed to find configured signature: '%s'\n", sig->name); + return; + } + sig->func = registeredSig->func; +} + +int pstrncmp(const void* a, const void* b) +{ + return strncmp(static_cast<const CallbackSignature *>(a)[0], static_cast<const CallbackSignature *>(b)[0], 16); +} +CallbackSignature *PkePlugin_GetSortedSignatures(long &count) { + if (sortedSignatures.next != pkePluginCallbacks.next) { + while (sortedSignatures.next != pkePluginCallbacks.next) { + PkeArray_Add(&sortedSignatures, pkePluginCallbacks.data[sortedSignatures.next].name); + } + qsort(sortedSignatures.data, sortedSignatures.next, sizeof(CallbackSignature), pstrncmp); } + count = sortedSignatures.next; + return sortedSignatures.data; } diff --git a/src/plugins.hpp b/src/plugins.hpp index 2f34c12..64c19f0 100644 --- a/src/plugins.hpp +++ b/src/plugins.hpp @@ -1,16 +1,32 @@ #ifndef PKE_PLUGINS_HPP #define PKE_PLUGINS_HPP +#include "array.hpp" +#include "dynamic-array.hpp" + struct PKEPluginInterface { + // for internal use only + void *pluginHandle = nullptr; void (*OnInit)() = nullptr; void (*OnTick)(double delta) = nullptr; void (*OnTeardown)() = nullptr; void (*OnImGuiRender)() = nullptr; }; -extern PKEPluginInterface pkePlugin; +using CallbackSignature = char[16]; +struct PkeCallback { + // the 16 char signature(name) of a function + CallbackSignature name = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; + // the address of the function to call - populated on startup + void *func = nullptr; +}; + +extern DynArray<PKEPluginInterface> LoadedPkePlugins; +extern PkeArray<PkeCallback> pkePluginCallbacks; -void Pke_LoadPlugin(const char *path); -void Pke_UnloadPlugin(); +void PkePlugin_Load(const char *path); +PkeCallback *PkePlugin_FindSignature(const CallbackSignature &sig); +void PkePlugin_SetSignatureFunc(PkeCallback *sig); +CallbackSignature *PkePlugin_GetSortedSignatures(long &count); #endif /* PKE_PLUGINS_HPP */ diff --git a/src/project.cpp b/src/project.cpp index e5b5583..08c7c2a 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -27,6 +27,7 @@ const char* const PKE_PROJ_FILE_ENTITY_TYPE_MODELS_DIR = "ModelsDir: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_MODEL_FILE = "ModelFile: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE = "EntityTypeCode: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_HANDLE = "EntityHandle: "; +const char* const PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE = "CollisionCallbackSignature: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT = "StartingInstanceCount: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX = "Importer_GLTF::AccessorIndexVertex: "; const char* const PKE_PROJ_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL = "Importer_GLTF::AccessorIndexNormal: "; @@ -61,6 +62,7 @@ void Proj_SerializeEntityType(std::ofstream &stream, const EntityType &et) { char handleStr[19] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; snprintf(handleStr, 19, "0x%016lX", et.entityHandle.hash); EntityType e{}; + CompGrBinds *grBinds = ECS_GetGrBinds(et.entityHandle); if (et.modelsDir != e.modelsDir) stream << PKE_PROJ_FILE_ENTITY_TYPE_MODELS_DIR << et.modelsDir << std::endl; if (et.modelFile != e.modelFile) @@ -69,6 +71,11 @@ void Proj_SerializeEntityType(std::ofstream &stream, const EntityType &et) { stream << PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE << et.entityTypeCode << std::endl; if (et.entityHandle != e.entityHandle) stream << PKE_PROJ_FILE_ENTITY_TYPE_ENTITY_HANDLE << handleStr << std::endl; + if (grBinds) { + if (grBinds->collisionCallback.name[0] != '\0') { + stream << PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE << grBinds->collisionCallback.name << std::endl; + } + } if (et.startingInstanceCount != e.startingInstanceCount) stream << PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT << et.startingInstanceCount << std::endl; if (et.Importer_GLTF.AccessorIndexVertex != e.Importer_GLTF.AccessorIndexVertex) @@ -126,6 +133,8 @@ void Proj_ParseProjectSettings(std::ifstream &stream) { */ void Proj_ParseEntityType(std::ifstream &stream) { + CallbackSignature collisionSig; + collisionSig[0] = '\0'; EntityType et{}; while (stream.getline(projReadLine, projReadLineLength)) { if (strcmp(PKE_PROJ_FILE_OBJ_END, projReadLine) == 0) { @@ -136,6 +145,11 @@ void Proj_ParseEntityType(std::ifstream &stream) { et.entityHandle = ECS_CreateEntity(); EntityType_Load(et); GlobalEntityTypes.Push(et); + CompGrBinds *grBinds = ECS_GetGrBinds(et.entityHandle); + if (grBinds) { + strncpy(grBinds->collisionCallback.name, collisionSig, 16); + PkePlugin_SetSignatureFunc(&grBinds->collisionCallback); + } return; } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_MODELS_DIR)) { @@ -171,6 +185,11 @@ void Proj_ParseEntityType(std::ifstream &stream) { assert(result == STR2NUM_ERROR::SUCCESS); continue; } + if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE)) { + uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_COLLISION_CALLBACK_SIGNATURE); + memcpy(collisionSig, projReadLine + prefixLen, 16); + continue; + } if (strstr(projReadLine, PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT)) { uint64_t prefixLen = strlen(PKE_PROJ_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT); STR2NUM_ERROR result = str2num(et.startingInstanceCount, projReadLine + prefixLen); diff --git a/src/window.cpp b/src/window.cpp index f9b6ff2..e7d4506 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2428,13 +2428,23 @@ void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { vkCmdDraw(commandBuffer, 3, 1, 0, 0); // ImGui - if (pkePlugin.OnImGuiRender) { + const auto count = LoadedPkePlugins.Count(); + bool any = false; + for (long i = 0; i < count; ++i) { + if (LoadedPkePlugins[i].OnImGuiRender != nullptr) { + any = true; + break; + } + } + if (any) { ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - - pkePlugin.OnImGuiRender(); - + for (long i = 0; i < count; ++i) { + if (LoadedPkePlugins[i].OnImGuiRender != nullptr) { + LoadedPkePlugins[i].OnImGuiRender(); + } + } ImGui::Render(); auto drawData = ImGui::GetDrawData(); const bool isMinimized = drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f; |
