diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index f29000d..9c63d3e 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -11,7 +11,8 @@ "dependencies/include/SDL2", "dependencies/imgui", "${default}" - ] + ], + "cppStandard": "c++20" } ], "version": 4 diff --git a/dependencies/src-csharp.config b/dependencies/src-csharp.config new file mode 100755 index 0000000..f679ca8 --- /dev/null +++ b/dependencies/src-csharp.config @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +DEP_WORKING_DIR='src-csharp' +if [[ "$TASK" = *_dbg ]]; then + DEP_BUILD_COMMAND='dotnet build src-csharp.sln -o bin -c Debug' +else + DEP_BUILD_COMMAND='dotnet build src-csharp.sln -o bin -c Release' +fi +DEP_CLEAN_COMMAND='rm -rf bin obj' +DEP_OTHER_OUT_FILES='bin/Ougge.dll' diff --git a/project.config b/project.config index 407c39b..95b7b07 100755 --- a/project.config +++ b/project.config @@ -6,7 +6,7 @@ PROJECT="ougge" CMP_C="gcc" CMP_CPP="g++" STD_C="c11" -STD_CPP="c++17" +STD_CPP="c++20" WARN_C="-Wall -Wno-discarded-qualifiers -Wextra -Wno-unused-parameter" WARN_CPP="-Wall -Wextra -Wno-unused-parameter" SRC_C="$(find src -name '*.c')" @@ -16,7 +16,7 @@ SRC_CPP="$(find src -name '*.cpp')" # See cbuild/example_dependency_configs DEPENDENCY_CONFIGS_DIR='dependencies' # List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space. -ENABLED_DEPENDENCIES='precompiled resources imgui' +ENABLED_DEPENDENCIES='precompiled resources imgui src-csharp' # OBJDIR structure: # ├── objects/ - Compiled object files. Cleans on each call of build task diff --git a/src-csharp/GameObject.cs b/src-csharp/GameObject.cs new file mode 100644 index 0000000..235cb4b --- /dev/null +++ b/src-csharp/GameObject.cs @@ -0,0 +1,24 @@ +using System.Management.Instrumentation; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace Ougge; + +[StructLayout(LayoutKind.Sequential)] +public struct Transform +{ + Vector2 scale; + Vector2 position; + float rotation; +} + +public class Component +{ + GameObject owner; +} + +public class GameObject +{ + Transform transform { get; } + GameObject? parent; +} \ No newline at end of file diff --git a/src-csharp/Script.cs b/src-csharp/Script.cs index 69801e4..0713b16 100644 --- a/src-csharp/Script.cs +++ b/src-csharp/Script.cs @@ -1,5 +1,4 @@ using System; -using System.Data; namespace Ougge; diff --git a/src/Game/GameObject.cpp b/src/Game/GameObject.cpp new file mode 100644 index 0000000..42ab42b --- /dev/null +++ b/src/Game/GameObject.cpp @@ -0,0 +1,29 @@ +#include "GameObject.hpp" + +std::ostream& operator<<(std::ostream& s, Transform& t){ + s<<"{ position: {x: "< +#include #include "../math.hpp" -#include +#include "../UsefulException.hpp" +#include "../Mono/Mono.hpp" -class GameObject { +class GameObject; + +class Component { + Mono::ObjectHandle object_handle; + GameObject* owner; public: - Vec2 scale = { 1, 1 }; - Vec2 position; - angle_t rotation; - -private: - // std::set components; + inline Component(Mono::Object managed_obj, GameObject* owner) + : object_handle(managed_obj), owner(owner) {} }; +struct Transform { + Vec2 scale = { 1, 1 }; + Vec2 position = { 0, 0 }; + angle_t rotation = 0; +}; + +std::ostream& operator<<(std::ostream& s, Transform& t); + + +class GameObject { + Transform transform; + std::map components; + Mono::ObjectHandle object_handle; + GameObject* parent; +public: + GameObject(Mono::Object managed_obj, GameObject* parent); + + GameObject& operator=(GameObject&& o); + + inline Transform& getTransform(){ return transform; } + bool tryGetComponent(const std::u16string& name, Component** out_component); + bool tryAddComponent(const std::u16string& name, Component&& component); +}; diff --git a/src/Game/GameObjectPool.cpp b/src/Game/GameObjectPool.cpp new file mode 100644 index 0000000..5a08b8a --- /dev/null +++ b/src/Game/GameObjectPool.cpp @@ -0,0 +1,127 @@ +#include "GameObjectPool.hpp" +#include +#include + +GameObjectPool::GameObjectPool(u32 size) +{ + useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64"); + this->size = size; + first_unused_index = 0; + buffer = new char[size*sizeof(GameObject)]; + used_indices = new u64[size/64]; + std::memset(buffer, 0, size*sizeof(GameObject)); + std::memset(used_indices, 0, size/8); +} + +GameObjectPool::~GameObjectPool() +{ + // int i = 0; + for(auto&& p : *this){ + // std::cout<<"~GameObjectPool i="<= size) + return -1; + if(!isIndexUsed(startIndex)) + return startIndex; + + u32 i = startIndex/64; + // mark previous bits as used + u64 u = used_indices[i] | ( (1<= size) + return -1; + if(isIndexUsed(startIndex)) + return startIndex; + + u32 i = startIndex/64; + // mark previous bits as unused + u64 u = used_indices[i] & !( (1<= size) + throw UsefulException(format("index %i is out of size %i", index, size)); + if(!isIndexUsed(index)) + throw UsefulException(format("there is no object at index %i", index)); + return ((GameObject*)buffer)[index]; +} + +std::pair GameObjectPool::put_new(GameObject&& new_obj) +{ + u32 i = first_unused_index; + if(i == (u32)-1) + throw UsefulException("can't put new GameObject to GameObjectPool because it's full"); + GameObject& r = ( ((GameObject*)buffer)[i] = std::move(new_obj) ); + used_indices[i] |= 1<<(i%64); // mark index bit as used + first_unused_index = getNearestUnusedIndex(i+1); + return std::pair(i, r); +} + +void GameObjectPool::remove(u32 index) +{ + if(index >= size) + throw UsefulException(format("index %i is out of size %i", index, size)); + if(!isIndexUsed(index)) + throw UsefulException(format("there is no object at index %i", index)); + ((GameObject*)buffer)[index].~GameObject(); + used_indices[index/64] ^= ~(1<<(index%64)); // mark index bit as unused + if(index < first_unused_index) + first_unused_index = index; +} + +GameObjectPool::iterator::iterator(GameObjectPool* p, u32 index) + : p(p), index(index) +{ +} + +std::pair GameObjectPool::iterator::operator*() +{ + if(index >= p->size) + throw UsefulException("can't get value of end() iterator"); + GameObject& r = ((GameObject*)p->buffer)[index]; + return std::pair(index, r); +} + +GameObjectPool::iterator& GameObjectPool::iterator::operator++() +{ + index = p->getNearestUsedIndex(index+1); + return *this; +} \ No newline at end of file diff --git a/src/Game/GameObjectPool.hpp b/src/Game/GameObjectPool.hpp new file mode 100644 index 0000000..c6c91c5 --- /dev/null +++ b/src/Game/GameObjectPool.hpp @@ -0,0 +1,37 @@ +#include "GameObject.hpp" + +class GameObjectPool { + void* buffer; + u64* used_indices; + u32 size; + u32 first_unused_index; + + bool isIndexUsed(u32 index); + u32 getNearestUnusedIndex(u32 startIndex); + u32 getNearestUsedIndex(u32 startIndex); + + friend class iterator; +public: + + ///@param size must be a multiple of 64 + GameObjectPool(u32 size); + ~GameObjectPool(); + GameObject& get(u32 index); + std::pair put_new(GameObject&& new_obj); + void remove(u32 index); + + class iterator { + GameObjectPool* p; + u32 index = 0; + + public: + iterator(GameObjectPool* p, u32 index); + std::pair operator*(); + iterator& operator++(); + inline bool operator!=(const iterator& o) const { return index != o.index; }; + inline bool operator==(const iterator& o) const { return index == o.index; }; + }; + + inline iterator begin() { return iterator(this, 0); } + inline iterator end() { return iterator(this, -1); } +}; diff --git a/src/Mono/Mono.hpp b/src/Mono/Mono.hpp index 510ed65..11001c1 100644 --- a/src/Mono/Mono.hpp +++ b/src/Mono/Mono.hpp @@ -124,4 +124,25 @@ public: std::shared_ptr loadAssembly(const std::string& name); }; +class ObjectHandle { + Object object; + u32 gc_handle; +public: + inline ObjectHandle(Object obj) : object(obj) { gc_handle = mono_gchandle_new(obj, false); } + inline ObjectHandle(const ObjectHandle& o) = delete; + inline ObjectHandle(ObjectHandle&& o) { + object = o.object; + gc_handle = o.gc_handle; + o.gc_handle = 0; + }; + inline ObjectHandle& operator=(ObjectHandle&& o) { + object = o.object; + gc_handle = o.gc_handle; + o.gc_handle = 0; + return *this; + }; + inline ~ObjectHandle() { if(gc_handle) mono_gchandle_free(gc_handle); } + inline Object getObject() { return object; } +}; + } diff --git a/src/UsefulException.hpp b/src/UsefulException.hpp index 39955d6..d925ae7 100644 --- a/src/UsefulException.hpp +++ b/src/UsefulException.hpp @@ -18,4 +18,4 @@ public: virtual char const* what() const noexcept; }; -#define useful_assert(EXPR, ERRMSG) if(!EXPR) throw UsefulException(ERRMSG); +#define useful_assert(EXPR, ERRMSG) if(!(EXPR)) throw UsefulException(ERRMSG); diff --git a/src/main.cpp b/src/main.cpp index 87e92ff..1d8be50 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,15 @@ #define SDL_MAIN_HANDLED #include +#include #include "GUI/MainWindow.hpp" #include "Resources/Resources.hpp" #include "Game/Scene.hpp" #include "format.hpp" #include "UsefulException.hpp" #include "Mono/Mono.hpp" +#include "Game/GameObjectPool.hpp" +#include +#include using namespace ougge; @@ -24,15 +28,24 @@ int main(int argc, const char** argv){ Mono::RuntimeJIT mono; std::cout<<"initialized mono jit runtime"<getClass("Ougge", "ExampleScript"); - auto scriptInstance = mono_object_new(mono.getDomain(), c); - mono_runtime_object_init(scriptInstance); - auto scriptUpdate = Mono::Method(c, "Update"); - updateCallbacks.push_back([scriptInstance, scriptUpdate](f64 deltaTime) -> void { - scriptUpdate(scriptInstance, deltaTime); - }); + auto gameObjectClass = a->getClass("Ougge", "GameObject"); + for(int i = 0; i < 32*1024; i++){ + Mono::Object gameObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass); + p.put_new(GameObject(gameObjectManaged, nullptr)); + GameObject& o = p.get(0); + std::cout<<'['<(c, "Update"); + // updateCallbacks.push_back([scriptInstance, scriptUpdate](f64 deltaTime) -> void { + // scriptUpdate(scriptInstance, deltaTime); + // }); GUI::MainWindow w; w.open("ougge", update);