diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 14ba23c..376aead 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,7 @@ +default: + artifacts: + expire_in: 1 day + stages: - build - test @@ -9,25 +13,18 @@ build_code: stage: build image: "registry.gitlab.com/mergul/bubel-ecs:latest" script: - - mkdir build - /bin/bash /compile_ecs.sh - - cp artifacts/* build/ - - cp -r public build/ artifacts: expire_in: 1h paths: - - build - rules: - - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' - when: always - - when: always + - binaries allow_failure: true test_dmd_debug: stage: test image: frolvlad/alpine-glibc script: - - build/dmd_debug_unittest + - binaries/dmd_debug_unittest artifacts: reports: junit: test_report.xml @@ -35,7 +32,7 @@ test_dmd: stage: test image: frolvlad/alpine-glibc script: - - build/dmd_release_unittest + - binaries/dmd_release_unittest artifacts: reports: junit: test_report.xml @@ -43,7 +40,7 @@ test_dmd_betterC: stage: test image: frolvlad/alpine-glibc script: - - build/dmd_release_unittest_bc + - binaries/dmd_release_unittest_bc artifacts: reports: junit: test_report.xml @@ -56,15 +53,30 @@ coverage_test_dmd: - build_code script: - mkdir reports - - build/dmd_unittest_cov + - binaries/dmd_unittest_cov after_script: - bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c +build_wasm: + stage: build + image: "registry.gitlab.com/mergul/bubel-ecs:latest" + script: + - /bin/bash /compile_wasm.sh + - /bin/bash /gen_doc.sh + artifacts: + expire_in: 1h + paths: + - build + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + when: always + allow_failure: true + emscripten: stage: build_emscripten image: "registry.gitlab.com/mergul/bubel-ecs/emscripten:latest" dependencies: - - build_code + - build_wasm script: - /bin/bash /build.sh rules: diff --git a/README.md b/README.md index d3f6816..b223f72 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,183 @@ -# Dynamic Entity Component System +# Bubel Entity Component System [![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) [![codecov](https://codecov.io/gl/Mergul/bubel-ecs/branch/master/graph/badge.svg?token=Unm0TJhFoW)](https://codecov.io/gl/Mergul/bubel-ecs) -Entity-Component-System implementation in D language. +BubelECS is Entity-Component-System architectural pattern implementation in D language. +Library aims to delivery fast and flexible architecture for developing games. Library is **@nogc** and **betterC** compatible. WASM is supported thorugh Emscripten. +Project haven't any external dependencies. + +BubelECS was tested on Linux, Windows, Android and WASM. + +**Currently library is in beta stage so some significant API changes can appear.** + +## Design + +For core information about Entity-Component-System architectural pattern please read definition described at [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system). + +Main design principles are: + + * **Data oriented design** - components memory is packed into tight block so iterating over entity components is cache friendly + * **Fast and safe EntityID** - every entity is referenced by its unique ID. Accessing by ID is safe even if entity don'y exist. Access by ID is constant time operation. + * **Multithreading support** - whole library was developed with multithreading in mind. Adding components is thread-friendly so you get usable EntityID as early as possible. Operations like adding or removing components are deferred to end of frame. Dependencies between systems parallel execution are generated automatically. + * **Good logic separation** - system needs information only about components which it's use, there is no relation between systems. Systems can be compiled as multiple separate libraries and used together. + * **Flexible execution model** - system iterate over entities which meet specified conditions. Components can be marked as required, optional or excluded. Systems are exectued in specific order determined by system priority. + * **Builtin events handling** - library has builtin support for event handlig to makes easier communication between different entities. + * **Hot-reload** - hot-reloading for systems should be as easy as possible. In other words library should give functionality to support hot-reload of systems and components with minimal work. **(WIP!)**. + +There are some assumptions that should be considered when developing application: + + * Iterating over components is fastest way of access data so it's should be main way of making calculations. + * Using of direct access and events should be used very wisely and minimized. + * Direct component access is faster than events, because events must buffer memory and call multiple system handler callbacks. + * Components can be used to marking entities, assingment to systems and changing logic of entity in runtime. Using too much component based marking can lead to memory fragmentation and performence drop. + * Systems give great opporunity to separate game logic. Systems can be enabled and disabled easly in runtime. It's can be used to enabling some debug systems if needed. Additionaly this concept makes game logic changes easier to deal with. If you develop your application wisely it should be trivial to change some core logic by changing only several systems or adding some new systems. Every entity can easily takes some behaviour from different entity type by adding several components. + +### Features + + * ECS architectural pattern + * Data oriented design + * Safe identifiers (EntityID) + * EntityTemplates + * Basic events handling + * Easy systems ordering + * Automatic multithreaded jobs generating + * Runtime and fast components adding and removing + * Listeners for adding and removing entity components inside systems + * Update passes + * Calling custom callbacks for system entity groups + * betterC compatibility + * Emscripten compatibility + +### Planned + + * Worlds - every world works as separate environment. Entities don't with entities from different world. Systems and components should be registered for every world separately. + * Hot-reload support - currently only reloading systems (their functions) works. There is possibility to serialize every entity and system, but it's should be provided by library itself with special callbacks and helpers. Additionaly planned is system which helps with taking new EntityID from serialized identifiers. + * External dependencies - ability to provide dependencies between system which isn't related to components. + * Static components - this components will have different memory model and can be accessed only directly. It will be slower to access but won't trigger memory copy when component is added. It should fix problem with big components which isn't needed by systems iteration callbacks or is required rarely. + * Better EventManager - there are several optimization and improvements that can be added in the future. + * More demos and examples - demo appliaction is very basic now, but in future more minigames and sanbox mode (opportunity to mix many components and systems) are planned. + * C API - in highly depends on amount of work required. Makes possible to use library from different languages. + * GPU compute - idea in draft stage. Special components and systems whose data wolud be on GPU memory. + * More smaller improvements... + +For more information about design and usage feel free to read [documentation](https://mergul.gitlab.io/bubel-ecs/ecs.html)**(WIP)** and [WIKI](https://gitlab.com/Mergul/bubel-ecs/-/wikis/home). + +## Build Instructions + +To build library you needs recent D compiler and optionally Emscripten (with python) to build web version. Currenlty tested are: LDC, DMD and GDC. \ +Supported build systems are DUB and Meson. + +##### DUB +```shell +#available configurations: library, dynlib, library-betterC, dynlib-betterC +dub build -c library -b release +``` + +##### Meson +```shell +#use '-DbetterC=true ' to build betterC code +meson build . #add '--buildtype=release' to build release code +cd build +ninja +``` + +##### Emscripten +```shell +python compile_wasm.py -opt +``` + +##### Documentation +```shell +adrdox -i source/bubel/ecs/ -o docs/ -s skeleton.html +``` + +For more detailed build options please check documentation for used build system. + +## Demos + +Repository contain demo application. You can check demo [online](https://mergul.gitlab.io/bubel-ecs/ecs_demo.html) or build it form source code using DUB. \ +Online demo support multithreading and page tries to check if client support WASM multithreading or not and loads properly JS and WASM code. It was tested on Chrome, Firefox, Opera, Brave on Linux and Android. On firefox there is problem with multithreaded version so if demo don't works please try to disable shared_memory in browser flags. + +## Code example + +```d + +struct Position +{ + mixin ECS.Components; //makes struct component + float x; + float y; +} + +struct Velocity +{ + mixin ECS.Components; + //default values works + float x = 0.1; + float y = 1; +} + +struct StaticFlag +{ + mixin ECS.Components; +} + +struct UpdateSystem +{ + mixin ECS.System; //makes struct system + + ECS.ExcludedComponents!(StaticFlag); //prevents static entities from update + + struct EntitiesData + { + int length; //entities count + @readonly Entity[] entities; //entities arrays, entity contain ID only + Position[] positions; //positions array + @readonly Velocity[] velocities; //veocities array, readonly (Multithreading tag) + } + + void onUpdate(EntitiesData data) //update callback, called multiple times per frame for every entities types + { + foreach(i; 0..data.length) //iterate over entities + { + data.positions[i].x += data.velocities[i].x * dt; + data.positions[i].y += data.velocities[i].y * dt; + } + } +} + +void main() +{ + manager.beginRegister(); + //register components + manager.registerComponent!Position; + manager.registerComponent!Velocity; + manager.registerComponent!StaticFlag; + //register system with priority 0 + manager.registerSystem!UpdateSystem(0); + manager.endRegister(); + + //allocate template + EntityTemplate* tmpl = manager.allocateEmplate([Velocity.component_id, Position.component_id].staticArray); + scope (exit) manager.freeTemplate(tmpl); + + //gets pointer to template component data + Position* position = tmpl.getComponent!Position; + foreach(i; 0 .. 100) + { + position.x = i % 10; + position.y = i / 10; + manager.addEntity(tmpl); + } + + manager.begin(); //start frame + manager.update(); //update all systems, there onUpdate callbacks are called + manager.end(); //end frame +} + +``` + +## Links + +Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \ +Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html diff --git a/codecov.yml b/codecov.yml index a6afdbc..5672706 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,10 @@ ignore: - "tests/*" - - "**/traits*" \ No newline at end of file + - "**/traits*" + +coverage: + status: + project: + default: + threshold: 5 + patch: off \ No newline at end of file diff --git a/demos/assets/shaders/base.fp b/demos/assets/shaders/base.fp index 043fa58..d86e92e 100644 --- a/demos/assets/shaders/base.fp +++ b/demos/assets/shaders/base.fp @@ -3,6 +3,31 @@ precision mediump float; precision lowp sampler2D; precision lowp samplerCube; + +#ifdef GLES + #define TEX(x,y) texture2D(x,y) + #if __VERSION__ >290 + #define M_IN in mediump + #define L_IN in lowp + #else + #define M_IN varying mediump + #define L_IN varying lowp + #endif +#else + #define TEX(x,y) texture(x,y) + #if __VERSION__ > 320 + #define M_IN in + #define L_IN in + #else + #define M_IN varying + #define L_IN varying + #endif +#endif + + +M_IN vec2 uv; +M_IN vec4 color; +/* #ifdef GLES #if __VERSION__ >290 in mediump vec2 uv; @@ -15,7 +40,7 @@ precision lowp samplerCube; #else varying vec2 uv; #endif -#endif +#endif*/ //layout(binding = 0)uniform sampler2D tex; @@ -23,20 +48,8 @@ uniform sampler2D tex; //layout(location = 0) out vec4 outColor; -void main() { - - #ifdef GLES - #if __VERSION__ >290 - gl_FragColor = texture(tex,uv); - #else - gl_FragColor = texture2D(tex,uv); - #endif - #else - #if __VERSION__ > 320 - gl_FragColor = texture(tex,uv); - #else - gl_FragColor = texture2D(tex,uv); - #endif - #endif +void main() +{ + gl_FragColor = TEX(tex,uv) * color; if(gl_FragColor.a < 0.01)discard; } diff --git a/demos/assets/shaders/base.vp b/demos/assets/shaders/base.vp index 42ed4a7..6c4c183 100644 --- a/demos/assets/shaders/base.vp +++ b/demos/assets/shaders/base.vp @@ -2,12 +2,37 @@ precision highp float; precision highp int; precision lowp sampler2D; precision lowp samplerCube; - #ifdef GLES #if __VERSION__ >290 - layout(location = 0) uniform vec4 matrix_1; - layout(location = 1) uniform vec4 matrix_2; - layout(location = 2) uniform vec4 uv_transform; + #define LOC(x) layout(location = x) + #define ATT in + #define M_OUT out mediump + #define L_OUT out lowp + #else + #define LOC(x) + #define ATT attribute + #define M_OUT varying mediump + #define L_OUT varying lowp + #endif +#else + #if __VERSION__ > 320 + #define LOC(x) layout(location = x) + #define ATT in + #define M_OUT out + #define L_OUT out + #else + #define LOC(x) + #define ATT attribute + #define M_OUT varying + #define L_OUT varying + #endif +#endif +/* +#ifdef GLES + #if __VERSION__ >290 + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; layout(location = 0) in vec2 positions; layout(location = 1) in vec2 tex_coords; @@ -43,13 +68,39 @@ precision lowp samplerCube; varying vec2 uv; #endif +#endif*/ + +#define VBO_BATCH 1 + +M_OUT vec2 uv; +L_OUT vec4 color; + +LOC(0) ATT vec2 positions; +LOC(1) ATT vec2 tex_coords; + +#ifdef VBO_BATCH + LOC(2) ATT float depth; + LOC(3) ATT vec4 vcolor; +#else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + uniform vec4 vcolor; + + float depth = matrix_2.z; #endif void main() { + #ifdef VBO_BATCH + vec3 position = vec3(positions*4.0,1.0); + uv = tex_coords; + #else + vec3 position = mat3(matrix_1.x,matrix_1.y,0,matrix_1.z,matrix_1.w,0,matrix_2.xy,1.0) * vec3(positions,1.0); + uv = tex_coords * uv_transform.zw + uv_transform.xy; + #endif - vec3 position = mat3(matrix_1.x,matrix_1.y,0,matrix_1.z,matrix_1.w,0,matrix_2.xy,1) * vec3(positions,1.0); - uv = tex_coords * uv_transform.zw + uv_transform.xy; - - gl_Position = vec4(position.xy,0,1.0); + color = vcolor * 2.0; + + gl_Position = vec4(position.xy,depth,1.0); } diff --git a/demos/assets/textures/atlas.png b/demos/assets/textures/atlas.png index c628969..a799eb7 100644 Binary files a/demos/assets/textures/atlas.png and b/demos/assets/textures/atlas.png differ diff --git a/demos/dub.json b/demos/dub.json index 43d248c..1c1fe65 100644 --- a/demos/dub.json +++ b/demos/dub.json @@ -18,6 +18,9 @@ "libs-windows-x86_64": ["libs/windows/x64/SDL2","libs/windows/x64/SDL2_Image","libs/windows/x64/cimgui"], "libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"], "lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"], + "dflags-ldc" : [ + "--ffast-math" + ], "configurations" : [ { "name" : "default", diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index 374fbc1..52136d4 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -1,6 +1,6 @@ module mmutils.thread_pool; -import ecs.atomic; +import bubel.ecs.atomic; //import core.stdc.stdio; //import core.stdc.stdlib : free, malloc, realloc; @@ -16,6 +16,7 @@ version (Posix)version = MM_USE_POSIX_THREADS; version (WebAssembly) { + version = MM_NO_LOGS; extern(C) struct FILE { @@ -32,7 +33,7 @@ else version (D_BetterC) { - import ecs.std; + import bubel.ecs.std; extern (C) __gshared int _d_eh_personality(int, int, size_t, void*, void*) { return 0; @@ -181,6 +182,12 @@ void instructionPause() __builtin_ia32_pause(); } + else version(GNU) + { + import gcc.builtins; + + __builtin_ia32_pause(); + } else version (DigitalMars) { asm @@ -188,7 +195,6 @@ void instructionPause() rep; nop; } - } else { @@ -799,6 +805,7 @@ struct ThreadPool alias FlushLogsDelegaste = void delegate(ThreadData* threadData, JobLog[] logs); /// Type of delegate to flush logs FlushLogsDelegaste onFlushLogs; /// User custom delegate to flush logs, if overriden defaultFlushLogs will be used. Can be sset after initialize() call int logsCacheNum; /// Number of log cache entries. Should be set before setThreadsNum is called + int tryWaitCount = 2000; ///Number of times which tryWait are called before timedWait call. Higher value sets better response but takes CPU time even if there are no jobs. private: ThreadData*[gMaxThreadsNum] threadsData; /// Data for threads align(64) shared int threadsNum; /// Number of threads currentlu accepting jobs @@ -808,6 +815,46 @@ private: JobData[4] resumeJobs; /// Dummu jobs to resume some thread public: + + static int getCPUCoresCount() + { + version(Windows) + { + import core.sys.windows.winbase : SYSTEM_INFO, GetSystemInfo; + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; + } + else version (linux) + { + version(D_BetterC) + { + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + return cast(int)sysconf(_SC_NPROCESSORS_ONLN); + } + else + { + import core.sys.linux.sched : CPU_COUNT, cpu_set_t, sched_getaffinity; + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + + cpu_set_t set = void; + if (sched_getaffinity(0, cpu_set_t.sizeof, &set) == 0) + { + int count = CPU_COUNT(&set); + if (count > 0) + return cast(uint) count; + } + return cast(int)sysconf(_SC_NPROCESSORS_ONLN); + } + } + else version(Posix) + { + import core.sys.posix.unistd; + return cast(int)sysconf(_SC_NPROCESSORS_ONLN); + } + else return -1; + } + int jobsDoneCount() { int sum; @@ -1141,17 +1188,19 @@ public: foreach (ref log; logs) { - size += log.name.length; // size of name + size += log.name.length + 1; // size of name } char* buffer = cast(char*) malloc(size); foreach (ref log; logs) { - + char[100] name_buffer; + name_buffer[0 .. log.name.length] = log.name; + name_buffer[log.name.length] = 0; size_t charWritten = snprintf(buffer + used, size - used, `{"name":"%s", "pid":1, "tid":%lld, "ph":"X", "ts":%lld, "dur":%lld }, %s`, - log.name.ptr, threadData.threadId + 1, log.time, log.duration, "\n".ptr); + name_buffer.ptr, threadData.threadId + 1, log.time, log.duration, "\n".ptr); used += charWritten; } @@ -1447,7 +1496,21 @@ private void threadFunc(ThreadData* threadData) if (data is null) { // Thread does not have own job and can not steal it, so wait for a job - bool ok = threadData.semaphore.timedWait(1_000 + !acceptJobs * 10_000); + int tryWait = 0; + //bool ok = threadData.semaphore.timedWait(1_000 + !acceptJobs * 10_000); + bool ok = true; + while(!threadData.semaphore.tryWait()) + { + tryWait++; + if(tryWait>threadPool.tryWaitCount) + { + ok = false; + break; + } + static foreach(i;0..10)instructionPause(); + } + if(!ok)ok = threadData.semaphore.timedWait(1_000 + !acceptJobs * 10_000); + if (ok) { diff --git a/demos/source/app.d b/demos/source/app.d index 150d653..ede93b5 100644 --- a/demos/source/app.d +++ b/demos/source/app.d @@ -6,9 +6,9 @@ import cimgui.cimgui; import game_core.job_updater; -import ecs.manager; -import ecs.core; -import ecs.std; +import bubel.ecs.manager; +import bubel.ecs.core; +import bubel.ecs.std; import ecs_utils.gfx.renderer; import ecs_utils.imgui_bind; @@ -58,6 +58,7 @@ struct Launcher uint style = 3; uint entities_count; bool multithreading; + int threads = 1; ulong timer_freq; double delta_time; uint fps; @@ -95,6 +96,7 @@ struct Launcher void switchDemo(void function() start, bool function() loop, void function() end, void function(SDL_Event*) event, void function(vec2, Tool, int) tool, const (char)* tips) { gui_manager.clear(); + //launcher.ent if(this.end)this.end(); @@ -102,6 +104,14 @@ struct Launcher manager.update("clean"); manager.end(); + foreach(ref system; manager.systems) + { + if(system.id != CountSystem.system_id && system.id != CleanSystem.system_id)system.disable(); + } + + /*launcher.manager.getSystem(CountSystem.system_id).enable(); + launcher.manager.getSystem(CleanSystem.system_id).enable();//*/ + if(start)start(); this.loop = loop; this.end = end; @@ -259,7 +269,6 @@ void mainLoop(void* arg) launcher.repeat_time -= range; launcher.tool((launcher.mouse.position*launcher.scalling)-launcher.render_position, launcher.used_tool, launcher.tool_size); } - } version(WebAssembly) @@ -316,6 +325,22 @@ void mainLoop(void* arg) if(igMenuItemBool("Multithreading", null, launcher.multithreading, true)) { launcher.multithreading = !launcher.multithreading; + if(launcher.multithreading) + { + launcher.job_updater.pool.setThreadsNum(launcher.threads); + } + else + { + launcher.job_updater.pool.setThreadsNum(1); + } + } + igSetNextItemWidth(0); + igLabelText("Threads:",null); + igSameLine(0,4); + if(igSliderInt("##Threads",&launcher.threads, 1, 32, null))//"Multithreading", null, launcher.multithreading, true)) + { + launcher.job_updater.pool.setThreadsNum(launcher.threads); + //launcher.threads = !launcher.multithreading; } if(igBeginMenu("Show",true)) { @@ -439,7 +464,7 @@ void mainLoop(void* arg) if(launcher.show_demo_wnd) { igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, 30), ImGuiCond_Once, ImVec2(0,0)); - igSetNextWindowSize(ImVec2(250, 250), ImGuiCond_Once); + igSetNextWindowSize(ImVec2(250, 500), ImGuiCond_Once); if(igBegin("Demo",&launcher.show_demo_wnd,0)) { ImDrawList* draw_list = igGetWindowDrawList(); @@ -539,11 +564,14 @@ void mainLoop(void* arg) launcher.renderer.clear(); double loop_time = launcher.getTime(); + launcher.job_updater.pool.tryWaitCount = 10000; if(launcher.loop && !launcher.loop()) { quit(); *cast(bool*)arg = false; } + launcher.job_updater.pool.tryWaitCount = 0; + loop_time = launcher.getTime() - loop_time; double draw_time = launcher.getTime(); @@ -686,10 +714,10 @@ int main(int argc, char** argv) setStyle(3); - launcher.job_updater = Mallocator.make!ECSJobUpdater(12); + launcher.job_updater = Mallocator.make!ECSJobUpdater(1); //launcher.job_updater.onCreate(); - EntityManager.initialize(12); + EntityManager.initialize(32, 1<<16); launcher.manager = EntityManager.instance; //launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID; @@ -709,10 +737,16 @@ int main(int argc, char** argv) launcher.renderer.initialize(); + import mmutils.thread_pool : ThreadPool; + launcher.threads = ThreadPool.getCPUCoresCount(); + if(launcher.threads < 2)launcher.threads = 2; + launcher.gui_manager = Mallocator.make!GUIManager(); { import demos.simple; + import demos.space_invaders; + // launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,&spaceInvadersTool,SpaceInvaders.tips); launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips); } @@ -741,6 +775,8 @@ int main(int argc, char** argv) } } + EntityManager.destroy(); + return 0; } @@ -785,7 +821,15 @@ void loadGFX() GfxConfig.materials[0].compile(); GfxConfig.materials[0].bindAttribLocation("positions",0); GfxConfig.materials[0].bindAttribLocation("tex_coords",1); + GfxConfig.materials[0].bindAttribLocation("depth",2); + GfxConfig.materials[0].bindAttribLocation("vcolor",3); GfxConfig.materials[0].link(); + + /* import std.stdio; + writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions")); + writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords")); + writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth")); + writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/ GfxConfig.materials[0].data.uniforms = Mallocator.makeArray!(Material.Uniform)(3); GfxConfig.materials[0].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0); diff --git a/demos/source/demos/bullet_madnes.d b/demos/source/demos/bullet_madnes.d index 0996e2e..420d6a3 100644 --- a/demos/source/demos/bullet_madnes.d +++ b/demos/source/demos/bullet_madnes.d @@ -6,11 +6,11 @@ import bindbc.sdl; import cimgui.cimgui; -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; import ecs_utils.gfx.texture; import ecs_utils.math.vector; diff --git a/demos/source/demos/chipmunk2d.d b/demos/source/demos/chipmunk2d.d deleted file mode 100644 index 25b9c66..0000000 --- a/demos/source/demos/chipmunk2d.d +++ /dev/null @@ -1,19 +0,0 @@ -module demos.chipmunk2d; - -import app; - -import bindbc.sdl; - -import cimgui.cimgui; - -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; - -import ecs_utils.gfx.texture; -import ecs_utils.math.vector; -import ecs_utils.utils; - -extern(C): \ No newline at end of file diff --git a/demos/source/demos/events.d b/demos/source/demos/events.d deleted file mode 100644 index 620ff4d..0000000 --- a/demos/source/demos/events.d +++ /dev/null @@ -1,19 +0,0 @@ -module demos.events; - -import app; - -import bindbc.sdl; - -import cimgui.cimgui; - -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; - -import ecs_utils.gfx.texture; -import ecs_utils.math.vector; -import ecs_utils.utils; - -extern(C): \ No newline at end of file diff --git a/demos/source/demos/flag_component.d b/demos/source/demos/flag_component.d deleted file mode 100644 index a1863b9..0000000 --- a/demos/source/demos/flag_component.d +++ /dev/null @@ -1,19 +0,0 @@ -module demos.flag_component; - -import app; - -import bindbc.sdl; - -import cimgui.cimgui; - -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; - -import ecs_utils.gfx.texture; -import ecs_utils.math.vector; -import ecs_utils.utils; - -extern(C): \ No newline at end of file diff --git a/demos/source/demos/physics.d b/demos/source/demos/physics.d index 6e9ba4a..5fccc0b 100644 --- a/demos/source/demos/physics.d +++ b/demos/source/demos/physics.d @@ -6,11 +6,11 @@ import bindbc.sdl; import cimgui.cimgui; -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; import ecs_utils.gfx.texture; import ecs_utils.math.vector; diff --git a/demos/source/demos/simple.d b/demos/source/demos/simple.d index edd20c6..313cc00 100644 --- a/demos/source/demos/simple.d +++ b/demos/source/demos/simple.d @@ -6,11 +6,11 @@ import bindbc.sdl; import cimgui.cimgui; -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; import ecs_utils.gfx.texture; import ecs_utils.math.vector; @@ -47,22 +47,27 @@ struct CTexture struct DrawSystem { - mixin ECS.System!1; + mixin ECS.System!32; struct EntitiesData { uint length; + //uint thread_id; + uint job_id; @readonly CTexture[] textures; @readonly CLocation[] locations; } void onUpdate(EntitiesData data) { + if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached foreach(i; 0..data.length) { - launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), 0, 0 , 0); + launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), cast(ushort)(data.locations[i].y), 0x80808080, 0, 0, 0, data.job_id); + // launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), 0, 0x80808080, 0, 0, 0, data.job_id); //draw(renderer, data.textures[i].tex, data.locations[i], vec2(32,32), vec4(0,0,1,1)); } + //if(data.thread_id == 0)launcher.renderer.pushData(); } } diff --git a/demos/source/demos/snake.d b/demos/source/demos/snake.d index 6bb8cef..01e9a36 100644 --- a/demos/source/demos/snake.d +++ b/demos/source/demos/snake.d @@ -6,18 +6,18 @@ import bindbc.sdl; import cimgui.cimgui; -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; -import ecs.vector; +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; +import bubel.ecs.vector; import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; -import std.array : staticArray; +//import std.array : staticArray; enum float px = 1.0/512.0; @@ -30,8 +30,9 @@ struct MapElement empty = 0, apple = 1, wall = 2, + snake = 3, - snake_head_up = 5, + /* snake_head_up = 5, snake_head_down = 6, snake_head_left = 7, snake_head_right = 8, @@ -44,13 +45,31 @@ struct MapElement snake_turn_rd = 15, snake_turn_ru = 16, snake_vertical = 17, - snake_horizontal = 18 + snake_horizontal = 18*/ } Type type; EntityID id; } +enum SnakePart : ubyte +{ + head_up = 0, + head_down = 1, + head_left = 2, + head_right = 3, + tail_up = 4, + tail_down = 5, + tail_left = 6, + tail_right = 7, + turn_ld = 8, + turn_lu = 9, + turn_rd = 10, + turn_ru = 11, + vertical = 12, + horizontal = 13 +} + struct Snake { __gshared const (char)* tips = "Use \"WASD\" keys to move."; @@ -71,6 +90,16 @@ struct Snake MapElement[map_size * map_size] map; + ~this() @nogc nothrow + { + if(snake_destroy_particle_frames)Mallocator.dispose(snake_destroy_particle_frames); + if(smoke_frames)Mallocator.dispose(smoke_frames); + if(apple_tmpl)launcher.manager.freeTemplate(apple_tmpl); + if(snake_tmpl)launcher.manager.freeTemplate(snake_tmpl); + if(snake_destroy_particle)launcher.manager.freeTemplate(snake_destroy_particle); + texture.destory(); + } + MapElement element(ivec2 pos) { uint index = pos.x + pos.y * map_size; @@ -100,43 +129,11 @@ struct Snake } if(base_pos.x == random_pos.x && base_pos.y == random_pos.y)return; } - CILocation* location = apple_tmpl.getComponent!CILocation; - *location = random_pos; - Entity* apple = launcher.manager.addEntity(apple_tmpl); + //CILocation* location = apple_tmpl.getComponent!CILocation; + //*location = random_pos; + //Entity* apple = + launcher.manager.addEntity(apple_tmpl,[CILocation(random_pos).ref_].staticArray); } - - void drawMap() - { - foreach(x; 0 .. map_size) - { - foreach(y; 0 .. map_size) - { - switch(element(ivec2(x,y)).type) - { - case MapElement.Type.apple:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0 , 0);break; - - case MapElement.Type.snake_head_up:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(48*px,112*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_head_down:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(48*px,144*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_head_left:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,128*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_head_right:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(32*px,128*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_tail_up:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(16*px,112*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_tail_down:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,112*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_tail_left:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(32*px,112*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_tail_right:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,144*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_turn_ld:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(64*px,128*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_turn_lu:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(32*px,144*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_turn_rd:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(16*px,144*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_turn_ru:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(64*px,112*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_vertical:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(16*px,128*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake_horizontal:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(48*px,128*px,16*px,16*px), 0, 0 , 0);break; - - case MapElement.Type.wall:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,0,1,1), 0, 0 , 0);break; - default:break; - } - } - } - } - } struct Animation @@ -184,6 +181,7 @@ struct CSnake mixin ECS.Component; + struct Parts { uint length = 0; @@ -217,6 +215,7 @@ struct CSnake } Parts parts; + CMovement.Direction direction; } struct CApple @@ -287,7 +286,7 @@ struct ParticleSystem { uint length; @readonly Entity[] entities; - @readonly CParticle[] particle; + CParticle[] particle; } void onUpdate(EntitiesData data) @@ -358,7 +357,7 @@ struct AnimationRenderSystem { foreach(i;0..data.length) { - launcher.renderer.draw(snake.texture, cast(vec2)cast(ivec2)data.location[i].location, vec2(16,16), data.animation[i].frames[cast(int)(data.animation[i].time)], 0, 0 , 0); + launcher.renderer.draw(snake.texture, cast(vec2)cast(ivec2)data.location[i].location, vec2(16,16), data.animation[i].frames[cast(int)(data.animation[i].time)], -1, 0x80808080); } } } @@ -368,8 +367,8 @@ struct MoveSystem mixin ECS.System!64; EntityTemplate* destroy_template; - CLocation* destroy_location; - CParticleVector* destroy_vector; + //CLocation* destroy_location; + //CParticleVector* destroy_vector; struct EntitiesData { @@ -383,8 +382,8 @@ struct MoveSystem void setTemplates() { destroy_template = snake.snake_destroy_particle; - destroy_location = destroy_template.getComponent!CLocation; - destroy_vector = destroy_template.getComponent!CParticleVector; + //destroy_location = destroy_template.getComponent!CLocation; + //destroy_vector = destroy_template.getComponent!CParticleVector; } void moveLocation(ref CILocation location, CMovement.Direction direction) @@ -424,133 +423,56 @@ struct MoveSystem else .snake.element(MapElement(),location); } - static CMovement.Direction getDirection(ivec2 p1, ivec2 p2) - { - if(p1.x - p2.x == -1)return CMovement.direction.right; - else if(p1.x - p2.x == 1)return CMovement.direction.left; - else if(p1.y - p2.y == -1)return CMovement.direction.up; - else if(p1.y - p2.y == 1)return CMovement.direction.down; - else if(p1.x - p2.x > 1)return CMovement.direction.right; - else if(p1.x - p2.x < -1)return CMovement.direction.left; - else if(p1.y - p2.y > 1)return CMovement.direction.up; - else return CMovement.direction.down; - } - - static MapElement.Type snakePart(ivec2 p1, ivec2 p2, ivec2 p3) - { - CMovement.Direction direction = getDirection(p1, p2); - CMovement.Direction direction2 = getDirection(p1, p3); - uint case_ = direction*4 + direction2; - final switch(case_) - { - case 0:return MapElement.Type.snake_horizontal; - case 1:return MapElement.Type.snake_horizontal; - case 2:return MapElement.Type.snake_turn_lu; - case 3:return MapElement.Type.snake_turn_ru; - case 4:return MapElement.Type.snake_horizontal; - case 5:return MapElement.Type.snake_horizontal; - case 6:return MapElement.Type.snake_turn_ld; - case 7:return MapElement.Type.snake_turn_rd; - case 8:return MapElement.Type.snake_turn_lu; - case 9:return MapElement.Type.snake_turn_ld; - case 10:return MapElement.Type.snake_vertical; - case 11:return MapElement.Type.snake_vertical; - case 12:return MapElement.Type.snake_turn_ru; - case 13:return MapElement.Type.snake_turn_rd; - case 14:return MapElement.Type.snake_vertical; - case 15:return MapElement.Type.snake_vertical; - } - } - - static MapElement.Type snakeTail(ivec2 p1, ivec2 p2) - { - CMovement.Direction direction = getDirection(p1, p2); - final switch(direction) - { - case CMovement.Direction.up:return MapElement.Type.snake_tail_up; - case CMovement.Direction.down:return MapElement.Type.snake_tail_down; - case CMovement.Direction.left:return MapElement.Type.snake_tail_left; - case CMovement.Direction.right:return MapElement.Type.snake_tail_right; - } - } - void onUpdate(EntitiesData data) { if(data.snakes) { foreach(i; 0..data.length) { + data.snakes[i].direction = data.movement[i].direction; ivec2 new_location = data.location[i]; moveLocation(data.location[i], data.movement[i].direction); final switch(snake.element(data.location[i].location).type) { - case MapElement.Type.snake_head_up:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_head_down:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_head_left:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_head_right:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_tail_up:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_tail_down:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_tail_left:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_tail_right:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_turn_ld:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_turn_lu:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_turn_rd:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_turn_ru:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_vertical:goto case(MapElement.Type.snake_horizontal); - case MapElement.Type.snake_horizontal: - foreach(ivec2 loc; data.snakes[i].parts) + case MapElement.Type.snake: + foreach(loc; data.snakes[i].parts) { - destroy_location.x = loc.x * 16; - destroy_location.y = loc.y * 16; + //destroy_location.x = loc.x * 16; + //destroy_location.y = loc.y * 16; snake.element(MapElement(MapElement.Type.empty, EntityID()),loc); - launcher.manager.addEntity(snake.snake_destroy_particle); + launcher.manager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(loc * 16)).ref_].staticArray); + + CLocation destroy_location; foreach(j;0..10) { destroy_location.x = loc.x * 16 + randomf() * 8 - 4; destroy_location.y = loc.y * 16 + randomf() * 8 - 4; - destroy_vector.velocity = vec2(randomf(),randomf())*0.4-0.2; + //destroy_vector.velocity = vec2(randomf(),randomf())*0.4-0.2; snake.element(MapElement(MapElement.Type.empty, EntityID()),loc); - launcher.manager.addEntity(snake.snake_destroy_particle); + launcher.manager.addEntity(snake.snake_destroy_particle, [destroy_location.ref_, CParticleVector(vec2(randomf(),randomf())*0.4-0.2).ref_].staticArray); } } - destroy_location.x = new_location.x * 16; - destroy_location.y = new_location.y * 16; + //destroy_location.x = new_location.x * 16; + //destroy_location.y = new_location.y * 16; snake.element(MapElement(MapElement.Type.empty, EntityID()),new_location); - launcher.manager.addEntity(snake.snake_destroy_particle); + launcher.manager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(new_location * 16)).ref_].staticArray); launcher.manager.removeEntity(data.entities[i].id); break; + case MapElement.Type.wall:break; - //launcher.manager.removeEntity(data.entities[i].id); - //break; + case MapElement.Type.empty: moveSnake(data.snakes[i], new_location); - final switch(data.movement[i].direction) - { - case CMovement.Direction.up: - snake.element(MapElement(MapElement.Type.snake_head_up, data.entities[i].id),data.location[i].location); - break; - case CMovement.Direction.right: - snake.element(MapElement(MapElement.Type.snake_head_right, data.entities[i].id),data.location[i].location); - break; - case CMovement.Direction.down: - snake.element(MapElement(MapElement.Type.snake_head_down, data.entities[i].id),data.location[i].location); - break; - case CMovement.Direction.left: - snake.element(MapElement(MapElement.Type.snake_head_left, data.entities[i].id),data.location[i].location); - break; - } + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location); if(data.snakes[i].parts.length > 1) { - MapElement.Type elem_type = snakePart(data.snakes[i].parts[$-1],data.location[i],data.snakes[i].parts[$-2]); - snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[$-1]); - elem_type = snakeTail(data.snakes[i].parts[1], data.snakes[i].parts[0]); - snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[0]); + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]); + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]); } else if(data.snakes[i].parts.length == 1) { - MapElement.Type elem_type = snakeTail(data.location[i], data.snakes[i].parts[0]); - snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[0]); + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]); } break; case MapElement.Type.apple: @@ -559,29 +481,13 @@ struct MoveSystem if(data.snakes[i].parts.length > 1) { - MapElement.Type elem_type = snakePart(data.snakes[i].parts[$-1],data.location[i],data.snakes[i].parts[$-2]); - snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[$-1]); + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]); } else if(data.snakes[i].parts.length == 1) { - MapElement.Type elem_type = snakeTail(data.location[i], new_location); - snake.element(MapElement(elem_type, data.entities[i].id),new_location); - } - final switch(data.movement[i].direction) - { - case CMovement.Direction.up: - snake.element(MapElement(MapElement.Type.snake_head_up, data.entities[i].id),data.location[i].location); - break; - case CMovement.Direction.right: - snake.element(MapElement(MapElement.Type.snake_head_right, data.entities[i].id),data.location[i].location); - break; - case CMovement.Direction.down: - snake.element(MapElement(MapElement.Type.snake_head_down, data.entities[i].id),data.location[i].location); - break; - case CMovement.Direction.left: - snake.element(MapElement(MapElement.Type.snake_head_left, data.entities[i].id),data.location[i].location); - break; + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),new_location); } + snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location); snake.addApple(); break; } @@ -593,10 +499,10 @@ struct MoveSystem { final switch(data.movement[i].direction) { - case CMovement.Direction.down:data.location[i].location.y -= 1;break; - case CMovement.Direction.up:data.location[i].location.y += 1;break; - case CMovement.Direction.left:data.location[i].location.x -= 1;break; - case CMovement.Direction.right:data.location[i].location.x += 1;break; + case CMovement.Direction.down:data.location[i].y -= 1;break; + case CMovement.Direction.up:data.location[i].y += 1;break; + case CMovement.Direction.left:data.location[i].x -= 1;break; + case CMovement.Direction.right:data.location[i].x += 1;break; } } } @@ -699,6 +605,132 @@ struct FixSnakeDirectionSystem } } +struct DrawAppleSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + @readonly CILocation[] location; + const (CApple)[] apple; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.location.length) + { + launcher.renderer.draw(snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0x80808080, 0); + } + } +} + +struct DrawSnakeSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + @readonly CILocation[] location; + const (CSnake)[] snake; + } + + static CMovement.Direction getDirection(ivec2 p1, ivec2 p2) + { + if(p1.x - p2.x == -1)return CMovement.direction.right; + else if(p1.x - p2.x == 1)return CMovement.direction.left; + else if(p1.y - p2.y == -1)return CMovement.direction.up; + else if(p1.y - p2.y == 1)return CMovement.direction.down; + else if(p1.x - p2.x > 1)return CMovement.direction.right; + else if(p1.x - p2.x < -1)return CMovement.direction.left; + else if(p1.y - p2.y > 1)return CMovement.direction.up; + else return CMovement.direction.down; + } + + static SnakePart snakePart(ivec2 p1, ivec2 p2, ivec2 p3) + { + CMovement.Direction direction = getDirection(p1, p2); + CMovement.Direction direction2 = getDirection(p1, p3); + uint case_ = direction*4 + direction2; + final switch(case_) + { + case 0:return SnakePart.horizontal; + case 1:return SnakePart.horizontal; + case 2:return SnakePart.turn_lu; + case 3:return SnakePart.turn_ru; + case 4:return SnakePart.horizontal; + case 5:return SnakePart.horizontal; + case 6:return SnakePart.turn_ld; + case 7:return SnakePart.turn_rd; + case 8:return SnakePart.turn_lu; + case 9:return SnakePart.turn_ld; + case 10:return SnakePart.vertical; + case 11:return SnakePart.vertical; + case 12:return SnakePart.turn_ru; + case 13:return SnakePart.turn_rd; + case 14:return SnakePart.vertical; + case 15:return SnakePart.vertical; + } + } + + static SnakePart snakeTail(ivec2 p1, ivec2 p2) + { + CMovement.Direction direction = getDirection(p1, p2); + final switch(direction) + { + case CMovement.Direction.up:return SnakePart.tail_up; + case CMovement.Direction.down:return SnakePart.tail_down; + case CMovement.Direction.left:return SnakePart.tail_left; + case CMovement.Direction.right:return SnakePart.tail_right; + } + } + + static void drawElement(ivec2 loc, SnakePart part) + { + final switch(cast(ubyte)part) + { + case SnakePart.tail_up:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,112,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.tail_down:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(0,112,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.tail_left:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(32,112,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.tail_right:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(0,144,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.turn_ld:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(64,128,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.turn_lu:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(32,144,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.turn_rd:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,144,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.turn_ru:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(64,112,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.vertical:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,128,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.horizontal:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(48,128,16,16)*px, 0, 0x80808080, 0);break; + } + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + const (CSnake)* snake = &data.snake[i]; + scope vec2 loc = cast(vec2)(data.location[i].location * 16); + final switch(snake.direction) + { + case CMovement.Direction.up:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(48,112,16,16)*px, 0, 0x80808080, 0);break; + case CMovement.Direction.down:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(48,144,16,16)*px, 0, 0x80808080, 0);break; + case CMovement.Direction.left:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,128,16,16)*px, 0, 0x80808080, 0);break; + case CMovement.Direction.right:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(32,128,16,16)*px, 0, 0x80808080, 0);break; + } + if(snake.parts.length >1) + { + foreach(j;1..snake.parts.length - 1)drawElement(snake.parts[j]*16, snakePart(snake.parts[j], snake.parts[j+1], snake.parts[j-1])); + drawElement(snake.parts[$-1]*16, snakePart(snake.parts[$-1], data.location[i], snake.parts[$-2])); + drawElement(snake.parts[0]*16, snakeTail(snake.parts[1], snake.parts[0])); + } + else if(snake.parts.length == 1) + { + drawElement(snake.parts[0]*16, snakeTail(data.location[i], snake.parts[0])); + } + + } + } +} + struct CleanSystem { mixin ECS.System!64; @@ -749,6 +781,8 @@ void snakeStart() launcher.manager.registerSystem!AnimationSystem(-1); launcher.manager.registerSystem!ParticleSystem(-1); launcher.manager.registerSystem!ParticleMovementSystem(-1); + launcher.manager.registerSystem!DrawAppleSystem(99); + launcher.manager.registerSystem!DrawSnakeSystem(101); launcher.manager.endRegister(); @@ -765,9 +799,9 @@ void snakeStart() { ushort[4] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id]; snake.snake_tmpl = launcher.manager.allocateTemplate(components); - CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation; - loc_comp.location = ivec2(2,2); - launcher.manager.addEntity(snake.snake_tmpl); + //CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation; + //*loc_comp = ivec2(2,2); + launcher.manager.addEntity(snake.snake_tmpl,[CILocation(ivec2(2,2)).ref_].staticArray); } { @@ -784,9 +818,9 @@ void snakeStart() snake.addApple(); } - launcher.gui_manager.addTemplate(snake.snake_tmpl, "Snake"); - launcher.gui_manager.addTemplate(snake.apple_tmpl, "Apple"); - launcher.gui_manager.addTemplate(snake.snake_destroy_particle, "Particle"); + launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.snake_tmpl), "Snake"); + launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.apple_tmpl), "Apple"); + launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.snake_destroy_particle), "Particle"); MoveSystem* move_system = launcher.manager.getSystem!MoveSystem(); move_system.setTemplates(); @@ -794,7 +828,7 @@ void snakeStart() /*foreach(i; 0..10) foreach(j; 0..10) { - loc_comp.location = vec2(i*32+64,j*32+64); + loc_compation = vec2(i*32+64,j*32+64); launcher.manager.addEntity(simple.tmpl); }*/ } @@ -815,15 +849,15 @@ void snakeTool(vec2 position, Tool tool, int size) CLocation* location = tmpl.getComponent!CLocation; if(location) { - position.x += (randomf - 0.5) * size; - position.y += (randomf - 0.5) * size; + position.x += (randomf() - 0.5) * size; + position.y += (randomf() - 0.5) * size; *location = position; } CILocation* ilocation = tmpl.getComponent!CILocation; if(ilocation) { - position.x += (randomf - 0.5) * size; - position.y += (randomf - 0.5) * size; + position.x += (randomf() - 0.5) * size; + position.y += (randomf() - 0.5) * size; ivec2 ipos; ipos.x = cast(int)(position.x / 16); ipos.y = cast(int)(position.y / 16); @@ -878,7 +912,7 @@ bool snakeLoop() launcher.manager.end(); - snake.drawMap(); + //snake.drawMap(); return true; } \ No newline at end of file diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index bddf53c..5615f0d 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -6,20 +6,29 @@ import bindbc.sdl; import cimgui.cimgui; -import ecs.attributes; -import ecs.core; -import ecs.entity; -import ecs.manager; -import ecs.std; +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +//import std.math : PI; + +enum PI = 3.141592653589793238462643383279502884197169399375105820; + +//import std.array : staticArray; + enum float px = 1.0/512.0; + extern(C): +enum ShootGridDependency = "ShootGridDependency"; + /*####################################################################################################################### ------------------------------------------------ Types ------------------------------------------------------------------ #######################################################################################################################*/ @@ -31,13 +40,31 @@ struct SpaceInvaders EntityTemplate* enemy_tmpl; EntityTemplate* ship_tmpl; EntityTemplate* laser_tmpl; + EntityTemplate*[5] bullet_tmpl; Texture texture; + ShootGrid* shoot_grid; + bool move_system = true; bool draw_system = true; const vec2 map_size = vec2(400,300); const float cell_size = 60; + + EntityID player_ship; + + ~this() @nogc nothrow + { + if(shoot_grid)Mallocator.dispose(shoot_grid); + if(enemy_tmpl)launcher.manager.freeTemplate(enemy_tmpl); + if(ship_tmpl)launcher.manager.freeTemplate(ship_tmpl); + if(laser_tmpl)launcher.manager.freeTemplate(laser_tmpl); + foreach (EntityTemplate* tmpl; bullet_tmpl) + { + if(tmpl)launcher.manager.freeTemplate(tmpl); + } + texture.destory(); + } } struct SceneGrid @@ -93,7 +120,7 @@ struct CLocation alias value this; - vec2 value; + vec2 value = vec2(0); } struct CScale @@ -106,11 +133,31 @@ struct CScale vec2 value = vec2(16,16); } +struct CColliderScale +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(16,16); +} + +struct CRotation +{ + mixin ECS.Component; + + ///use component as it value + alias value this; + + float value = 0; +} + struct CTexture { mixin ECS.Component; - Texture tex; + //Texture tex; + uint id; vec4 coords = vec4(0,0,0,1); } @@ -147,22 +194,50 @@ struct CGuild { mixin ECS.Component; - int guild; + byte guild; } -struct CLaser +struct CBullet { mixin ECS.Component; - float damage = 1.0f; + int damage = 1; } -struct CLaserWeapon +struct CWeapon { mixin ECS.Component; - ubyte level = 1; + static struct Level + { + float reload_time; + float dispersion; + int damage; + } + + __gshared Level[12] levels = [Level(4000,0),Level(4000,0.1), + Level(500,0),Level(350,0),Level(250,0.02),Level(175,0.03),Level(110,0.04), + Level(80,0.05),Level(50,0.08),Level(20,0.1),Level(10,0.12),Level(2,0.14)]; + + enum Type : ubyte + { + laser, + enemy_laser, + blaster, + canon, + plasma + } + float shoot_time = 0; + Type type; + ubyte level = 1; +} + +struct CWeaponLocation +{ + mixin ECS.Component; + + vec2 rel_pos = vec2(0,0); } struct CShootDirection @@ -179,6 +254,196 @@ struct CSideMove byte group = -1; } +struct CDepth +{ + mixin ECS.Component; + + alias depth this; + + short depth; +} + +struct CShootGrid +{ + mixin ECS.Component; +} + +struct CTargetParent +{ + mixin ECS.Component; + + EntityID parent; + vec2 rel_pos; +} + + +struct CHitPoints +{ + mixin ECS.Component; + + alias value this; + + int value = 3; +} + +struct CMaxHitPoints +{ + mixin ECS.Component; + + alias value this; + + int value = 3; +} + +struct CHitMark +{ + mixin ECS.Component; + + alias value this; + + ubyte value = 0; +} + +struct CUpgrade +{ + mixin ECS.Component; + + alias value this; + + enum Upgrade : ubyte + { + hit_points, + regeneration, + laser + } + + Upgrade value; +} + +struct CAnimation +{ + mixin ECS.Component; + + vec4[] frames; + float time = 0; + float speed = 1; +} + +struct CAnimationLooped +{ + mixin ECS.Component; +} + +struct CDamping +{ + mixin ECS.Component; + + alias value this; + + byte value = 0; +} + +struct CParticle +{ + mixin ECS.Component; + + float life = 0; +} + +struct CTarget +{ + mixin ECS.Component; + + EntityID target; +} + +struct CTargetPlayerShip +{ + mixin ECS.Component; +} + +struct CChildren +{ + mixin ECS.Component; + + EntityID[] childern; +} + +struct CBoss +{ + mixin ECS.Component; +} + +struct CParts +{ + mixin ECS.Component; + + ubyte count; +} + +struct CInit +{ + mixin ECS.Component; + + enum Type + { + space_ship, + tower, + boss + } + + Type type; +} + +struct CParticleEmitter +{ + mixin ECS.Component; + + vec2 range; + vec2 time_range; + ///due to multithreading there should be separate template for every thread. + ///It can be array of tempaltes or (like in this demo) simply index of template; + uint tmpl_id; + //EntityTemplate* tmpl; +} + +///Due to perfarmance reason emitter time and attributes are divided into seprate components. +///Beyon that both components are considerd to be used together. +struct CParticleEmitterTime +{ + mixin ECS.Component; + + float time = 0; +} + +///You can create separate component for every kind of spawned entities but it's not practial due to archetype fragmentation. +///Second approach can be commented code. It's gives good flexibility inchoosing entity, but it limits to one entity. +///Instead of entity it can be array of templates which is good solution, but if possibilities is known at time of game development it +///can be simply index/enum for type of spawn. Bad thing about this solution is problem witch merging multiple spawning types during +///gameplay, i.e. giving buff which cast firebols upon death. +struct CSpawnUponDeath +{ + mixin ECS.Component; + + enum Type + { + flashes_emitter, + } + + //EntityID parent; + //EntityTemplate* tmpl; + Type type; +} + +///This component can be replaced by "CSpawnUponDeath" but I want to gives possibility to add this component to every entity +///during gameplay. End application works exacly the same way for every demo so I can't use different way as adding component. +struct CShootWaveUponDeath +{ + mixin ECS.Component; + + CWeapon.Type bullet_type; +} + /*####################################################################################################################### ------------------------------------------------ Events ------------------------------------------------------------------ #######################################################################################################################*/ @@ -195,32 +460,593 @@ struct EChangeDirection Direction direction; } +struct EUpgrade +{ + mixin ECS.Event; +} + +struct EDeath +{ + mixin ECS.Event; +} + +struct EDamage +{ + mixin ECS.Event; + + this(uint damage) + { + this.damage = damage; + } + + uint damage = 0; +} + +struct EBulletHit +{ + mixin ECS.Event; + + this(EntityID id, uint damage) + { + this.id = id; + this.damage = damage; + } + + EntityID id; + uint damage; +} + +struct EDestroyedChild +{ + mixin ECS.Event; + + this(EntityID id) + { + this.id = id; + } + + EntityID id; +} + /*####################################################################################################################### ------------------------------------------------ Systems ------------------------------------------------------------------ #######################################################################################################################*/ -struct DrawSystem +struct ShootGrid +{ + + ~this() @nogc nothrow + { + if(nodes)Mallocator.dispose(nodes); + if(masks)Mallocator.dispose(masks); + } + + struct Node + { + alias entity this; + + EntityID entity; + } + + void create(ivec2 nodes_count, vec2 node_size) + { + this.size = nodes_count; + this.node_size = node_size; + inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y); + nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y); + masks = Mallocator.makeArray!ubyte(nodes.length); + } + + void mark(EntityID id, vec2 beg, vec2 end, byte mask) + { + ivec2 ibeg = cast(ivec2)(beg * inv_node_size); + ivec2 iend = cast(ivec2)((end * inv_node_size) + 0.5); + if(ibeg.x < 0)ibeg.x = 0; + if(ibeg.y < 0)ibeg.y = 0; + if(iend.x > size.x)iend.x = size.x; + if(iend.y > size.y)iend.y = size.y; + foreach(i; ibeg.y .. iend.y) + { + foreach(j; ibeg.x .. iend.x) + { + nodes[i * size.x + j] = id; + masks[i * size.x +j] = mask; + } + } + } + + void clear() + { + size_t size = nodes.length * EntityID.sizeof; + memset(nodes.ptr, 0, size); + memset(masks.ptr, 0, masks.length); + } + + bool test(out EntityID id, vec2 beg, vec2 end) + { + ivec2 ibeg = cast(ivec2)(beg * inv_node_size); + ivec2 iend = cast(ivec2)((end * inv_node_size) + 0.5); + if(ibeg.x < 0)ibeg.x = 0; + if(ibeg.y < 0)ibeg.y = 0; + if(iend.x > size.x)iend.x = size.x; + if(iend.y > size.y)iend.y = size.y; + foreach(i; ibeg.y .. iend.y) + { + foreach(j; ibeg.x .. iend.x) + { + if(nodes[i * size.x + j].id != 0) + { + id = nodes[i * size.x + j]; + return true; + } + } + } + return false; + } + + bool test(out EntityID id, vec2 pos, ubyte mask) + { + ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5); + if(ipos.x < 0)ipos.x = 0; + if(ipos.y < 0)ipos.y = 0; + if(ipos.x >= size.x)ipos.x = size.x - 1; + if(ipos.y >= size.y)ipos.y = size.y - 1; + size_t index = ipos.y * size.x + ipos.x; + if((masks[index] & mask) == 0)return false; + if(nodes[index].id != 0) + { + id = nodes[index]; + return true; + } + return false; + } + + vec2 inv_node_size; + ivec2 size; + vec2 node_size; + Node[] nodes; + ubyte[] masks; +} + +struct ShootGridCleaner { mixin ECS.System!1; struct EntitiesData { - uint length; - @readonly CTexture[] textures; - @readonly CLocation[] locations; - @readonly CScale[] scale; + } void onUpdate(EntitiesData data) { + if(space_invaders.shoot_grid)space_invaders.shoot_grid.clear(); + } +} + +struct ShootGridManager +{ + mixin ECS.System!128; + + mixin ECS.WritableDependencies!(ShootGridDependency); + + struct EntitiesData + { + uint length; + //uint thread_id; + const (Entity)[] entity; + @readonly CLocation[] locations; + @readonly CShootGrid[] grid_flag; + @readonly CGuild[] guild; + @optional @readonly CScale[] scale; + @optional @readonly CColliderScale[] collider_scale; + } + + ShootGrid* grid; + + void onCreate() + { + grid = space_invaders.shoot_grid; + } + + bool onBegin() + { + if(!grid)return false; + //grid.clear(); + return true; + } + + void onUpdate(EntitiesData data) + { + vec2[] scale; + if(data.collider_scale)scale = cast(vec2[])data.collider_scale; + else if(data.scale)scale = cast(vec2[])data.scale; + else return; foreach(i; 0..data.length) { - launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, 0, 0 , 0); - //draw(renderer, data.textures[i].tex, data.locations[i], vec2(32,32), vec4(0,0,1,1)); + vec2 half_scale = scale[i] * 0.5; + grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, cast(ubyte)(1 << data.guild[i].guild)); } } } +struct ParentOwnerSystem +{ + mixin ECS.System; + + struct EntitiesData + { + CChildren[] children; + } + + void onRemoveEntity(EntitiesData data) + { + //currently EntitiesData always has only one element + foreach(child; data.children[0].childern) + { + launcher.manager.removeEntity(child); + } + if(data.children[0].childern.length)Mallocator.dispose(data.children[0].childern); + } +} + +struct ShipWeaponSystem +{ + mixin ECS.System; + + struct EntitiesData + { + int length; + Entity[] entity; + CInit[] init; + //CShip[] ship; + //CChildren[] children; + } + + struct Ship + { + EntityTemplate* laser1_tmpl; + EntityTemplate* laser2_tmpl; + EntityTemplate* main_weapon_tmpl; + + void add(Entity* entity) + { + CChildren* children = entity.getComponent!CChildren; + if(children is null || children.childern.length != 0)return; + EntityID[3] weapons; + laser1_tmpl.getComponent!CTargetParent().parent = entity.id; + laser2_tmpl.getComponent!CTargetParent().parent = entity.id; + main_weapon_tmpl.getComponent!CTargetParent().parent = entity.id; + weapons[0] = launcher.manager.addEntity(laser1_tmpl).id; + weapons[1] = launcher.manager.addEntity(laser2_tmpl).id; + weapons[2] = launcher.manager.addEntity(main_weapon_tmpl).id; + children.childern = Mallocator.makeArray(weapons); + } + + void create() + { + laser1_tmpl = launcher.manager.allocateTemplate([CWeapon.component_id, CLocation.component_id, CShootDirection.component_id, CTargetParent.component_id, CGuild.component_id, CVelocity.component_id].staticArray); + main_weapon_tmpl = launcher.manager.allocateTemplate([CLocation.component_id, CShootDirection.component_id, CTargetParent.component_id, CGuild.component_id, CVelocity.component_id].staticArray); + *laser1_tmpl.getComponent!CWeapon = CWeapon(0,CWeapon.Type.laser,3); + laser1_tmpl.getComponent!CTargetParent().rel_pos = vec2(10,13); + main_weapon_tmpl.getComponent!CTargetParent().rel_pos = vec2(0,4); + laser2_tmpl = launcher.manager.allocateTemplate(laser1_tmpl); + laser2_tmpl.getComponent!CTargetParent().rel_pos = vec2(-10,13); + } + + ~this() + { + launcher.manager.freeTemplate(laser1_tmpl); + launcher.manager.freeTemplate(laser2_tmpl); + launcher.manager.freeTemplate(main_weapon_tmpl); + } + } + + struct Tower + { + EntityTemplate* weapon_tmpl; + EntityTemplate* top_tmpl; + + void add(Entity* entity) + { + CChildren* children = entity.getComponent!CChildren; + if(children is null || children.childern.length != 0)return; + CDepth* depth = entity.getComponent!CDepth; + EntityID[2] weapons; + weapon_tmpl.getComponent!CTargetParent().parent = entity.id; + if(depth)weapon_tmpl.getComponent!CDepth().depth = cast(short)(depth.depth - 1); + else weapon_tmpl.getComponent!CDepth().depth = -1; + top_tmpl.getComponent!CTargetParent().parent = entity.id; + if(depth)top_tmpl.getComponent!CDepth().depth = cast(short)(depth.depth - 2); + else top_tmpl.getComponent!CDepth().depth = -2; + + weapons[0] = launcher.manager.addEntity(weapon_tmpl).id; + weapons[1] = launcher.manager.addEntity(top_tmpl).id; + children.childern = Mallocator.makeArray(weapons); + } + + void create() + { + weapon_tmpl = launcher.manager.allocateTemplate( + [CWeapon.component_id, CLocation.component_id, CShootDirection.component_id, + CTargetParent.component_id, CGuild.component_id, CVelocity.component_id, + CAutoShoot.component_id, CTarget.component_id, CTargetPlayerShip.component_id, + CRotation.component_id, CScale.component_id, CTexture.component_id, + CDepth.component_id, CWeaponLocation.component_id].staticArray); + *weapon_tmpl.getComponent!CWeapon = CWeapon(0,CWeapon.Type.laser,3); + weapon_tmpl.getComponent!CTargetParent().rel_pos = vec2(0,0); + weapon_tmpl.getComponent!CGuild().guild = 1; + weapon_tmpl.getComponent!CScale().value = vec2(4,16); + //weapon_tmpl.getComponent!CWeapon().level = 1; + *weapon_tmpl.getComponent!CWeapon() = CWeapon(0,CWeapon.Type.canon,1); + weapon_tmpl.getComponent!CDepth().depth = -1; + weapon_tmpl.getComponent!CTexture().coords = vec4(136,96,4,16)*px; + weapon_tmpl.getComponent!CWeaponLocation().rel_pos = vec2(0,12); + + top_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CTargetParent.component_id, CScale.component_id, + CTexture.component_id, CDepth.component_id].staticArray); + top_tmpl.getComponent!CTargetParent().rel_pos = vec2(0,1); + top_tmpl.getComponent!CScale().value = vec2(10,11); + top_tmpl.getComponent!CDepth().depth = -2; + top_tmpl.getComponent!CTexture().coords = vec4(112,96,10,11)*px; + + } + + ~this() + { + launcher.manager.freeTemplate(weapon_tmpl); + launcher.manager.freeTemplate(top_tmpl); + } + } + + struct Boss + { + EntityTemplate* tower1_tmpl; + EntityTemplate* tower2_tmpl; + EntityTemplate* tower3_tmpl; + EntityTemplate* tower4_tmpl; + + void add(Entity* entity) + { + CChildren* children = entity.getComponent!CChildren; + if(children is null || children.childern.length != 0)return; + CParts* parts = entity.getComponent!CParts; + if(parts)parts.count = 4; + EntityID[4] towers; + tower1_tmpl.getComponent!CTargetParent().parent = entity.id; + tower2_tmpl.getComponent!CTargetParent().parent = entity.id; + tower3_tmpl.getComponent!CTargetParent().parent = entity.id; + tower4_tmpl.getComponent!CTargetParent().parent = entity.id; + towers[0] = launcher.manager.addEntity(tower1_tmpl).id; + towers[1] = launcher.manager.addEntity(tower2_tmpl).id; + towers[2] = launcher.manager.addEntity(tower3_tmpl).id; + towers[3] = launcher.manager.addEntity(tower4_tmpl).id; + children.childern = Mallocator.makeArray(towers); + } + + void create() + { + tower1_tmpl = launcher.manager.allocateTemplate( + [CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, + CTexture.component_id, CScale.component_id, CEnemy.component_id, + CShootGrid.component_id, CGuild.component_id, CInit.component_id, + CChildren.component_id, CDepth.component_id, CTargetParent.component_id, + CSpawnUponDeath.component_id, CShootWaveUponDeath.component_id].staticArray + ); + + CTexture* tex_comp = tower1_tmpl.getComponent!CTexture; + //tex_comp.tex = space_invaders.texture;//ship_tex; + tex_comp.coords = vec4(96*px,96*px,16*px,16*px); + CLocation* loc_comp = tower1_tmpl.getComponent!CLocation; + loc_comp.value = vec2(64,space_invaders.map_size.y - 16); + tower1_tmpl.getComponent!CGuild().guild = 1; + tower1_tmpl.getComponent!CInit().type = CInit.Type.tower; + tower1_tmpl.getComponent!CHitPoints().value = 10; + tower1_tmpl.getComponent!CDepth().depth = -2; + tower1_tmpl.getComponent!CShootWaveUponDeath().bullet_type = CWeapon.Type.canon; + tower1_tmpl.getComponent!CTargetParent().rel_pos = vec2(-33,2); + + tower2_tmpl = launcher.manager.allocateTemplate(tower1_tmpl); + tower2_tmpl.getComponent!CTargetParent().rel_pos = vec2(33,2); + + tower3_tmpl = launcher.manager.allocateTemplate(tower1_tmpl); + tower3_tmpl.getComponent!CDepth().depth = 0; + tower3_tmpl.getComponent!CTargetParent().rel_pos = vec2(-40,-15); + + tower4_tmpl = launcher.manager.allocateTemplate(tower1_tmpl); + tower4_tmpl.getComponent!CDepth().depth = 0; + tower4_tmpl.getComponent!CTargetParent().rel_pos = vec2(40,-15); + } + + ~this() + { + launcher.manager.freeTemplate(tower1_tmpl); + launcher.manager.freeTemplate(tower2_tmpl); + launcher.manager.freeTemplate(tower3_tmpl); + launcher.manager.freeTemplate(tower4_tmpl); + } + } + + Ship ship; + Tower tower; + Boss boss; + + void onCreate() + { + ship.create(); + tower.create(); + boss.create(); + /*ship.laser1_tmpl = launcher.manager.allocateTemplate([CWeapon.component_id, CLocation.component_id, CShootDirection.component_id, CTargetParent.component_id, CGuild.component_id, CVelocity.component_id].staticArray); + ship.main_weapon_tmpl = launcher.manager.allocateTemplate([CLocation.component_id, CShootDirection.component_id, CTargetParent.component_id, CGuild.component_id, CVelocity.component_id].staticArray); + *ship.laser1_tmpl.getComponent!CWeapon = CWeapon(3,0.0); + ship.laser1_tmpl.getComponent!CTargetParent().rel_pos = vec2(10,13); + ship.main_weapon_tmpl.getComponent!CTargetParent().rel_pos = vec2(0,4); + ship.laser2_tmpl = launcher.manager.allocateTemplate(ship.laser1_tmpl); + ship.laser2_tmpl.getComponent!CTargetParent().rel_pos = vec2(-10,13);*/ + } + + void onDestroy() + { + /*launcher.manager.freeTemplate(laser1_tmpl); + launcher.manager.freeTemplate(laser2_tmpl); + launcher.manager.freeTemplate(main_weapon_tmpl);*/ + } + + void onAddEntity(EntitiesData data) + { + foreach(i; 0..data.length) + { + final switch(data.init[i].type) + { + case CInit.Type.space_ship:ship.add(&data.entity[i]);break; + case CInit.Type.tower:tower.add(&data.entity[i]);break; + case CInit.Type.boss:boss.add(&data.entity[i]);break; + } + } + } +} + +struct MoveToParentTargetSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + int length; + CLocation[] location; + @optional CVelocity[] velocity; + @readonly CTargetParent[] target; + } + + void onUpdate(EntitiesData data) + { + if(data.velocity) + { + foreach(i;0..data.length) + { + Entity* target = launcher.manager.getEntity(data.target[i].parent); + if(target) + { + CLocation* target_loc = target.getComponent!CLocation; + if(target_loc != null) + { + data.location[i] = *target_loc + data.target[i].rel_pos; + } + CVelocity* target_vel = target.getComponent!CVelocity; + if(target_vel != null) + { + data.velocity[i] = *target_vel; + } + } + } + } + else + foreach(i;0..data.length) + { + Entity* target = launcher.manager.getEntity(data.target[i].parent); + if(target) + { + CLocation* target_loc = target.getComponent!CLocation; + if(target_loc != null) + { + data.location[i] = *target_loc + data.target[i].rel_pos; + } + } + } + } +} + +struct DrawSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + //uint thread_id; + uint job_id; + @readonly CTexture[] textures; + @readonly CLocation[] locations; + @readonly CScale[] scale; + @readonly @optional CRotation[] rotation; + @readonly @optional CDepth[] depth; + @readonly @optional CHitMark[] hit_mark; + } + + void onUpdate(EntitiesData data) + { + if(!data.depth) + { + if(data.hit_mark) + { + foreach(i; 0..data.length) + { + uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + short depth = cast(short)(data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color, 0, 0, 0, data.job_id); + } + } + else if(data.rotation) + { + foreach(i; 0..data.length) + { + short depth = cast(short)(data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, data.rotation[i], 0, 0, data.job_id); + } + } + else + { + foreach(i; 0..data.length) + { + short depth = cast(short)(data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, 0, 0, 0, data.job_id); + } + } + } + else + { + if(data.hit_mark) + { + if(data.rotation) + { + foreach(i; 0..data.length) + { + uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color, data.rotation[i], 0, 0, data.job_id); + } + } + else + { + foreach(i; 0..data.length) + { + uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color, 0, 0, 0, data.job_id); + } + } + } + else if(data.rotation) + { + foreach(i; 0..data.length) + { + short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, data.rotation[i], 0, 0, data.job_id); + } + } + else + { + foreach(i; 0..data.length) + { + short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, 0, 0, 0, data.job_id); + } + } + } + //if(data.thread_id == 0)launcher.renderer.pushData(); + } +} + struct CollisionSystem { mixin ECS.System; @@ -241,31 +1067,82 @@ struct LaserShootingSystem mixin ECS.System!32; bool shoot = false; - float[10] laser_shoot_times = [2000,1500,1000,700,500,300,100,50,10,1]; - CLocation* laser_location; - CVelocity* laser_velocity; - + __gshared vec4[] fire_frames = [vec4(96,64,8,16)*px,vec4(104,64,8,16)*px,vec4(112,64,8,16)*px,vec4(120,64,8,16)*px,vec4(128,64,8,16)*px, + vec4(136,64,8,16)*px,vec4(144,64,8,16)*px,vec4(152,64,8,16)*px,vec4(160,64,8,16)*px]; + + // __gshared vec4[] fire_frames = [vec4(0,160,8,16)*px,vec4(16,160,16,16)*px,vec4(32,160,16,16)*px,vec4(48,160,16,16)*px,vec4(64,160,16,16)*px, + // vec4(80,160,16,16)*px,vec4(96,160,16,16)*px,vec4(112,160,16,16)*px]; + struct EntitiesData { ///variable named "length" contain entites count uint length; - @readonly CShootDirection[] shoot_direction; - @readonly @optional CAutoShoot[] auto_shoot; + CWeapon[] laser; @readonly CLocation[] location; - CLaserWeapon[] laser; + @readonly CGuild[] guild; + + @optional @readonly CShootDirection[] shoot_direction; + @optional @readonly CWeaponLocation[] weapon_location; + @optional @readonly CAutoShoot[] auto_shoot; + @optional @readonly CVelocity[] velocity; + @optional @readonly CRotation[] rotation; } + + //EntityTemplate* laser_tmpl; + EntityTemplate* fire_tmpl; + + //EntityTemplate*[5] bullet_tmpl; ///Called inside "registerSystem" function - /*void onCreate() + void onCreate() { - laser_location = space_invaders.laser_tmpl.getComponent!CLocation; - }*/ + /*bullet_tmpl[0] = launcher.manager.allocateTemplate( + [CLocation.component_id, CTexture.component_id, CVelocity.component_id, + CScale.component_id, CBullet.component_id, CGuild.component_id].staticArray + ); + bullet_tmpl[0].getComponent!CTexture().coords = vec4(0,24,2,8)*px; + bullet_tmpl[0].getComponent!CScale().value = vec2(2,8); + + bullet_tmpl[1] = launcher.manager.allocateTemplate(bullet_tmpl[0]); + bullet_tmpl[2] = launcher.manager.allocateTemplate(bullet_tmpl[0]); + bullet_tmpl[2].getComponent!CTexture().coords = vec4(64,32,8,16)*px; + bullet_tmpl[2].getComponent!CScale().value = vec2(8,16); + bullet_tmpl[3] = launcher.manager.allocateTemplate(bullet_tmpl[0]); + bullet_tmpl[3].getComponent!CTexture().coords = vec4(56,32,2,2)*px; + bullet_tmpl[3].getComponent!CScale().value = vec2(2,2); + // bullet_tmpl[3].getComponent!CTexture().coords = vec4(48,32,8,8)*px; + // bullet_tmpl[3].getComponent!CScale().value = vec2(8,8); + bullet_tmpl[4] = launcher.manager.allocateTemplate(bullet_tmpl[0]);*/ + + + fire_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CTexture.component_id, CScale.component_id, + CAnimation.component_id, CParticle.component_id, CRotation.component_id, + CVelocity.component_id, CDamping.component_id].staticArray + ); + + fire_tmpl.getComponent!CTexture().coords = vec4(96,64,8,16)*px; + fire_tmpl.getComponent!CScale().value = vec2(8,16); + fire_tmpl.getComponent!(CParticle).life = 300; + *fire_tmpl.getComponent!(CAnimation) = CAnimation(fire_frames, 0, 3); + } + + void onDestroy() + { + //launcher.manager.freeTemplate(laser_tmpl); + /*foreach (EntityTemplate* tmpl; bullet_tmpl) + { + launcher.manager.freeTemplate(tmpl); + }*/ + launcher.manager.freeTemplate(fire_tmpl); + } bool onBegin() { - laser_location = space_invaders.laser_tmpl.getComponent!CLocation; + /*laser_location = space_invaders.laser_tmpl.getComponent!CLocation; laser_velocity = space_invaders.laser_tmpl.getComponent!CVelocity; + laser_guild = space_invaders.laser_tmpl.getComponent!CGuild;*/ if(launcher.getKeyState(SDL_SCANCODE_SPACE)) { shoot = true; @@ -281,14 +1158,69 @@ struct LaserShootingSystem { foreach(i;0..data.length) { - CLaserWeapon* laser = &data.laser[i]; + CWeapon* laser = &data.laser[i]; laser.shoot_time += launcher.delta_time; - while(laser.shoot_time > laser_shoot_times[laser.level - 1]) + while(laser.shoot_time > CWeapon.levels[laser.level - 1].reload_time) { - laser.shoot_time -= laser_shoot_times[laser.level - 1]; + CVelocity laser_velocity; + CGuild laser_guild; + CLocation laser_location; + CVelocity fire_velocity; + CLocation fire_location; + CRotation fire_rotation; + + laser.shoot_time -= CWeapon.levels[laser.level - 1].reload_time; laser_location.value = data.location[i]; - laser_velocity.value = vec2(randomf()*0.5-0.25,data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); - launcher.manager.addEntity(space_invaders.laser_tmpl); + + laser_velocity.value = vec2((randomf()*2-1) * CWeapon.levels[laser.level - 1].dispersion,1);//data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); + if(data.shoot_direction && data.shoot_direction[i].direction == Direction.down)laser_velocity.y = -1; + + laser_guild.guild = data.guild[i].guild; + + if(laser.level < 3)laser_velocity.value = laser_velocity.value * 0.4f; + + if(data.velocity) + { + fire_velocity.value = data.velocity[i]; + //laser_velocity.value += data.velocity[i] * 0.5; + } + else fire_velocity.value = vec2(0,0); + + fire_location.value = data.location[i]; + if(data.shoot_direction[i].direction == Direction.down) + { + fire_rotation.value = PI; + //fire_location.value.y -= 16; + } + else + { + fire_rotation.value = 0; + //fire_location.value.y += 24; + } + + if(data.rotation) + { + float sinn = sinf(data.rotation[i]); + float coss = cosf(data.rotation[i]); + float x = laser_velocity.y*sinn + laser_velocity.x*coss; + float y = laser_velocity.y*coss + laser_velocity.x*sinn; + laser_velocity.value = vec2(x,y); + fire_rotation.value = data.rotation[i]; + if(data.weapon_location) + { + vec2 rel_pos = vec2(data.weapon_location[i].rel_pos.y*sinn+data.weapon_location[i].rel_pos.x*coss, data.weapon_location[i].rel_pos.y*coss+data.weapon_location[i].rel_pos.x*sinn); + laser_location.value += rel_pos; + fire_location.value += rel_pos; + } + } + else if(data.weapon_location) + { + laser_location.value += data.weapon_location[i].rel_pos; + fire_location.value += data.weapon_location[i].rel_pos; + } + + launcher.manager.addEntity(space_invaders.bullet_tmpl[data.laser[i].type],[laser_velocity.ref_, laser_guild.ref_, laser_location.ref_].staticArray); + launcher.manager.addEntity(fire_tmpl,[fire_location.ref_, fire_rotation.ref_, fire_velocity.ref_].staticArray); } } } @@ -296,15 +1228,209 @@ struct LaserShootingSystem { foreach(i;0..data.length) { - CLaserWeapon* laser = &data.laser[i]; + CWeapon* laser = &data.laser[i]; laser.shoot_time += launcher.delta_time; - if(laser.shoot_time > laser_shoot_times[laser.level - 1])laser.shoot_time = laser_shoot_times[laser.level - 1]; + if(laser.shoot_time > CWeapon.levels[laser.level - 1].reload_time)laser.shoot_time = CWeapon.levels[laser.level - 1].reload_time; } } } } +struct DampingSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + @readonly CDamping[] damping; + CVelocity[] velocity; + } + + float[10] damp = 0; + + bool onBegin() + { + foreach(i;0..10) + { + damp[i] = powf((0.98 - cast(float)i * 0.02),launcher.delta_time*0.1); + } + + return true; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; + } + } +} + +struct LaserCollisionSystem +{ + mixin ECS.System!32; + + mixin ECS.ReadOnlyDependencies!(ShootGridDependency); + + struct EntitiesData + { + ///variable named "length" contain entites count + uint length; + const (Entity)[] entity; + @readonly CLocation[] location; + @readonly CBullet[] laser; + @readonly CGuild[] guild; + } + + void onUpdate(EntitiesData data) + { + EntityID id; + foreach(i; 0..data.length) + { + if(space_invaders.shoot_grid.test(id, data.location[i], cast(ubyte)(~(1 << data.guild[i].guild)))) + { + launcher.manager.sendEvent(id, EBulletHit(data.entity[i].id,1)); + //launcher.manager.removeEntity(data.entity[i].id); + } + } + } +} + +struct ParticleEmittingSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + //uint thread_id; + CParticleEmitterTime[] emit_time; + @readonly CLocation[] location; + @readonly CParticleEmitter[] emitter; + + @optional @readonly CVelocity[] velocity; + @optional @readonly CDepth[] depth; + } + + __gshared vec4[] flashes = [vec4(224,0,16,16)*px,vec4(240,0,16,16)*px,vec4(256,0,16,16)*px,vec4(272,0,16,16)*px,vec4(288,0,16,16)*px, + vec4(304,0,16,16)*px,vec4(320,0,16,16)*px]; + + EntityTemplate*[1] templates; + + void onCreate() + { + templates[0] = launcher.manager.allocateTemplate( + [CLocation.component_id, CTexture.component_id, CScale.component_id, + CAnimation.component_id, CParticle.component_id, CRotation.component_id, + CVelocity.component_id, CDamping.component_id, CDepth.component_id].staticArray); + *templates[0].getComponent!CAnimation() = CAnimation(flashes,0,2); + *templates[0].getComponent!CParticle() = CParticle(350); + //*templates[0].getComponent!CDepth() = CDepth(-3); + } + + void onDestroy() + { + foreach(tmpl; templates) + { + launcher.manager.freeTemplate(tmpl); + } + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.emit_time[i].time -= launcher.delta_time; + while(data.emit_time[i].time < 0) + { + CVelocity velocity; + CDepth depth; + + CParticleEmitter* emitter = &data.emitter[i]; + data.emit_time[i].time += emitter.time_range.x + randomf() * emitter.time_range.y; + + if(data.velocity) + { + velocity.value = data.velocity[i]; + } + + if(data.depth) + { + depth.depth = data.depth[i]; + } + + launcher.manager.addEntity(templates[0],[data.location[i].ref_,velocity.ref_,depth.ref_].staticArray); + } + } + } +} + +struct UpgradeCollisionSystem +{ + mixin ECS.System!32; + + mixin ECS.ReadOnlyDependencies!(ShootGridDependency); + + struct EntitiesData + { + ///variable named "length" contain entites count + uint length; + const (Entity)[] entity; + @readonly CLocation[] location; + @readonly CUpgrade[] upgrade; + } + + void onUpdate(EntitiesData data) + { + EntityID id; + foreach(i; 0..data.length) + { + if(space_invaders.shoot_grid.test(id, data.location[i], cast(ubyte)(0xFF))) + { + Entity* entity = launcher.manager.getEntity(id); + if(entity.hasComponent(CShip.component_id)) + { + launcher.manager.sendEvent(id, EUpgrade()); + launcher.manager.removeEntity(data.entity[i].id); + } + } + } + } +} + +struct UpgradeSystem +{ + mixin ECS.System; + + struct EntitiesData + { + const (Entity)[] entity; + //@readonly CShip[] ship; + } + + void handleEvent(Entity* entity, EUpgrade event) + { + CWeapon* laser = entity.getComponent!CWeapon; + if(laser) + { + if(laser.level < CWeapon.levels.length)laser.level++; + } + CShip* ship = entity.getComponent!CShip; + if(ship) + { + CChildren* children = entity.getComponent!CChildren; + foreach(child;children.childern) + { + launcher.manager.sendEvent(child,EUpgrade()); + } + } + } +} + struct ChangeDirectionSystem { mixin ECS.System!32; @@ -320,6 +1446,7 @@ struct ChangeDirectionSystem CVelocity[] velocity; const(CSideMove)[] side_move; + @optional const(CScale)[] scale; } void onCreate() @@ -330,19 +1457,6 @@ struct ChangeDirectionSystem } } - /*bool onBegin() - { - foreach(direction; groups_directions) - { - if(direction != cast(Direction)-1)//return true; - { - has_changes = true; - break; - } - } - return true; - }*/ - void onEnd() { if(has_changes) @@ -358,13 +1472,8 @@ struct ChangeDirectionSystem if(direction != cast(Direction)-1) { has_changes = true; - //direction = cast(Direction)-1; } } - /*foreach(ref direction; groups_directions) - { - direction = cast(Direction)-1; - }*/ } void onUpdate(EntitiesData data) @@ -409,6 +1518,34 @@ struct ChangeDirectionSystem } } } + else if(data.scale) + { + foreach(i;0..data.length) + { + if(data.locations[i].x - data.scale[i].x * 0.5 < 0) + { + if(data.side_move[i].group == -1) + { + if(data.velocity[i].x < 0)data.velocity[i].x = -data.velocity[i].x; + } + else + { + groups_directions[data.side_move[i].group] = Direction.right; + } + } + else if(data.locations[i].x + data.scale[i].x * 0.5 > space_invaders.map_size.x) + { + if(data.side_move[i].group == -1) + { + if(data.velocity[i].x > 0)data.velocity[i].x = -data.velocity[i].x; + } + else + { + groups_directions[data.side_move[i].group] = Direction.left; + } + } + } + } else { foreach(i;0..data.length) @@ -435,36 +1572,256 @@ struct ChangeDirectionSystem groups_directions[data.side_move[i].group] = Direction.left; } } - //if(data.locations[i].y < 0) data.locations[i].y = 0; - //else if(data.locations[i].y > space_invaders.map_size.y)data.locations[i].y = space_invaders.map_size.y; } } } +} - void handleEvent(Entity* entity, EChangeDirection event) +struct HitMarkingSystem +{ + mixin ECS.System!16; + + struct EntitiesData { - CSideMove* side_move = entity.getComponent!CSideMove; - if(side_move && side_move.group != -1) + uint length; + CHitMark[] mark; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) { - groups_directions[side_move.group] = event.direction; - return; + //if(data.mark[i] < 10)data.mark[i] = 0; + //else data.mark[i] -= 1; + data.mark[i] = cast(ubyte)(data.mark[i] * 0.9); } - //Entity* entity = launcher.manager.getEntity(event.entity_id); - CVelocity* velocity = entity.getComponent!CVelocity; - final switch(event.direction) + } +} + +struct HitPointsSystem +{ + mixin ECS.System; + + __gshared vec4[] upgrade_laser_frames = [vec4(96,80,16,16)*px,vec4(112,80,16,16)*px,vec4(128,80,16,16)*px,vec4(144,80,16,16)*px,vec4(128,80,16,16)*px,vec4(112,80,16,16)*px]; + __gshared vec4[] explosion_laser_frames = [vec4(80,128,16,16)*px,vec4(96,128,16,16)*px,vec4(112,128,16,16)*px,vec4(128,128,16,16)*px,vec4(144,128,16,16)*px,vec4(160,128,16,16)*px,vec4(176,128,16,16)*px,vec4(192,128,16,16)*px,vec4(208,128,16,16)*px]; + + EntityTemplate* upgrade_tmpl; + EntityTemplate* explosion_tmpl; + + struct EntitiesData + { + CHitPoints[] hp; + } + + void onCreate() + { + upgrade_tmpl = launcher.manager.allocateTemplate( + [CVelocity.component_id, CLocation.component_id, CTexture.component_id, + CScale.component_id, CUpgrade.component_id, CAnimation.component_id, + CAnimationLooped.component_id].staticArray); + //tex_comp.tex = space_invaders.texture;//ship_tex; + upgrade_tmpl.getComponent!CTexture().coords = vec4(0*px,32*px,16*px,16*px); + *upgrade_tmpl.getComponent!CAnimation = CAnimation(upgrade_laser_frames, 0, 1); + upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.1); + + explosion_tmpl = launcher.manager.allocateTemplate( + [CDepth.component_id, CParticle.component_id, CLocation.component_id, + CTexture.component_id, CScale.component_id, CAnimation.component_id].staticArray); + //explosion_tmpl.getComponent!(CTexture).tex = space_invaders.texture; + *explosion_tmpl.getComponent!CAnimation = CAnimation(explosion_laser_frames, 0, 1.333); + explosion_tmpl.getComponent!(CParticle).life = 600; + *explosion_tmpl.getComponent!CDepth = -1; + } + + void onDestroy() + { + launcher.manager.freeTemplate(upgrade_tmpl); + launcher.manager.freeTemplate(explosion_tmpl); + } + + /*void handleEvent(Entity* entity, EDamage event) + { + CHitPoints* hp = entity.getComponent!CHitPoints; + if(*hp <= 0)return; + *hp -= event.damage; + if(*hp <= 0) { - case Direction.up: - if(velocity.value.y > 0)velocity.value.y = -velocity.value.y; - break; - case Direction.down: - if(velocity.value.y < 0)velocity.value.y = -velocity.value.y; - break; - case Direction.left: - if(velocity.value.x > 0)velocity.value.x = -velocity.value.x; - break; - case Direction.right: - if(velocity.value.x < 0)velocity.value.x = -velocity.value.x; - break; + launcher.manager.sendEvent(entity.id, EDeath()); + //launcher.manager.removeEntity(entity.id); + } + CHitMark* hit_mark = entity.getComponent!CHitMark; + if(hit_mark)hit_mark.value = 127; + }*/ + + void handleEvent(Entity* entity, EBulletHit event) + { + CHitPoints* hp = entity.getComponent!CHitPoints; + if(*hp <= 0)return; + launcher.manager.removeEntity(event.id); + *hp -= event.damage; + if(*hp <= 0) + { + launcher.manager.sendEvent(entity.id, EDeath()); + //launcher.manager.removeEntity(entity.id); + } + CHitMark* hit_mark = entity.getComponent!CHitMark; + if(hit_mark)hit_mark.value = 127; + } + + void handleEvent(Entity* entity, EDeath event) + { + CEnemy* enemy = entity.getComponent!CEnemy; + if(enemy) + { + CLocation* location = entity.getComponent!CLocation; + if(location) + { + if(randomRange(0, 1000) < 5) + { + launcher.manager.addEntity(upgrade_tmpl,[location.ref_].staticArray); + } + launcher.manager.addEntity(explosion_tmpl,[location.ref_].staticArray); + } + } + launcher.manager.removeEntity(entity.id); + } +} + +struct ChildDestroySystem +{ + mixin ECS.System; + + struct EntitiesData + { + CTargetParent[] parent; + } + + void handleEvent(Entity* entity, EDeath event) + { + CTargetParent* parent = entity.getComponent!CTargetParent; + if(parent) + { + launcher.manager.sendEvent(parent.parent, EDestroyedChild(entity.id)); + } + } +} + +struct ShootWaveSystem +{ + mixin ECS.System; + + struct EntitiesData + { + CLocation[] location; + CShootWaveUponDeath[] shoot_wave; + } + + vec2[] dirs; + + void onCreate() + { + enum count = 24; + dirs = Mallocator.makeArray!vec2(count); + float step = 2 * PI / cast(float)count; + foreach(i;0..count) + { + float angle = step * i; + dirs[i] = vec2(sinf(angle),cosf(angle)) * 0.2; + } + } + + void onDestroy() + { + Mallocator.dispose(dirs); + } + + void handleEvent(Entity* entity, EDeath event) + { + + CShootWaveUponDeath* wave = entity.getComponent!CShootWaveUponDeath; + CLocation* location = entity.getComponent!CLocation; + CGuild* guild = entity.getComponent!CGuild; + + //LaserShootingSystem.bullet_tmpl + EntityTemplate* tmpl = space_invaders.bullet_tmpl[wave.bullet_type]; + foreach(dir;dirs) + { + if(guild)launcher.manager.addEntity(tmpl,[location.ref_,guild.ref_,CVelocity(dir).ref_].staticArray); + else launcher.manager.addEntity(tmpl,[location.ref_,CVelocity(dir).ref_].staticArray); + } + //launcher.manager.addEntity(tmpl);//,[location.ref_].staticArray); + + //launcher.manager.addEntity(space_invaders.bullet_tmpl[0]); + } +} + +struct PartsDestroySystem +{ + mixin ECS.System; + + struct EntitiesData + { + CInit[] init; + CChildren[] children; + CParts[] parts; + } + + EntityTemplate* flashes_emitter; + + void onCreate() + { + flashes_emitter = launcher.manager.allocateTemplate( + [ + CVelocity.component_id, CLocation.component_id, CParticleEmitter.component_id, + CParticleEmitterTime.component_id, CTargetParent.component_id, CDepth.component_id + ].staticArray); + *flashes_emitter.getComponent!CParticleEmitter() = CParticleEmitter(vec2(0,0), vec2(800,1600), 0); + } + + void onDestroy() + { + launcher.manager.freeTemplate(flashes_emitter); + } + + void handleEvent(Entity* entity, EDestroyedChild event) + { + CParts* parts = entity.getComponent!CParts; + parts.count--; + + CInit* init = entity.getComponent!CInit; + if(init.type == CInit.Type.boss) + { + CChildren* children = entity.getComponent!CChildren; + foreach(ref EntityID child; children.childern) + { + if(child == event.id) + { + Entity* child_entity = launcher.manager.getEntity(child); + if(child_entity) + { + CLocation location; + CTargetParent* target_parent = child_entity.getComponent!CTargetParent; + CDepth* target_depth = child_entity.getComponent!CDepth; + CLocation* target_location = child_entity.getComponent!CLocation; + //CVelocity* velocity = child_entity.getComponent!CTargetParent; + + if(target_location)location = *target_location; + + *flashes_emitter.getComponent!CTargetParent() = *target_parent; + if(target_depth)child = launcher.manager.addEntity(flashes_emitter, [target_depth.ref_, location.ref_].staticArray).id; + else child = launcher.manager.addEntity(flashes_emitter, [location.ref_].staticArray).id; + } + break; + } + } + } + + if(parts.count == 0) + { + if(init.type == CInit.Type.boss) + { + launcher.manager.addComponents(entity.id, CHitPoints(100), CShootGrid()); + } } } } @@ -481,7 +1838,10 @@ struct ClampPositionSystem //components are treated as required by default CLocation[] locations; - @optional const (CLaser)[] laser; + @optional @readonly CColliderScale[] collider_scale; + @optional @readonly CScale[] scale; + @optional const (CBullet)[] laser; + @optional const (CUpgrade)[] upgrade; //@optional CVelocity[] velocity; //@optional const (CSideMove)[] side_move; } @@ -490,7 +1850,7 @@ struct ClampPositionSystem void onUpdate(EntitiesData data) { - if(data.laser) + if(data.laser || data.upgrade) { foreach(i;0..data.length) { @@ -532,6 +1892,28 @@ struct ClampPositionSystem else if(data.locations[i].y > space_invaders.map_size.y)data.locations[i].y = space_invaders.map_size.y; } }*/ + else if(data.collider_scale) + { + foreach(i;0..data.length) + { + vec2 hscale = data.collider_scale[i] * 0.5; + if(data.locations[i].x - hscale.x < 0)data.locations[i].x = hscale.x; + else if(data.locations[i].x + hscale.x > space_invaders.map_size.x)data.locations[i].x = space_invaders.map_size.x - hscale.x; + if(data.locations[i].y - hscale.y < 0)data.locations[i].y = hscale.y; + else if(data.locations[i].y + hscale.y > space_invaders.map_size.y)data.locations[i].y = space_invaders.map_size.y - hscale.y; + } + } + else if(data.scale) + { + foreach(i;0..data.length) + { + vec2 hscale = data.scale[i] * 0.5; + if(data.locations[i].x - hscale.x < 0)data.locations[i].x = hscale.x; + else if(data.locations[i].x + hscale.x > space_invaders.map_size.x)data.locations[i].x = space_invaders.map_size.x - hscale.x; + if(data.locations[i].y - hscale.y < 0)data.locations[i].y = hscale.y; + else if(data.locations[i].y + hscale.y > space_invaders.map_size.y)data.locations[i].y = space_invaders.map_size.y - hscale.y; + } + } else { foreach(i;0..data.length) @@ -556,10 +1938,10 @@ struct MovementSystem const (CVelocity)[] velocity; //components are treated as required by default CLocation[] locations; - //@optional const (CLaser)[] laser; + //@optional const (CBullet)[] laser; const (Entity)[] entities; - @optional CSideMove[] side_move; + //@optional CSideMove[] side_move; } void onUpdate(EntitiesData data) @@ -572,6 +1954,238 @@ struct MovementSystem } } +struct AnimationSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + CAnimation[] animation; + CTexture[] texture; + @optional @readonly CAnimationLooped[] looped; + } + + void onUpdate(EntitiesData data) + { + float dt = launcher.delta_time * 0.01; + if(data.looped) + { + foreach(i;0..data.length) + { + data.animation[i].time += dt * data.animation[i].speed; + while(cast(uint)data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length; + if(cast(uint)(data.animation[i].time) >= data.animation[i].frames.length)assert(0); + uint index = cast(uint)(data.animation[i].time); + if(index < data.animation[i].frames.length)data.texture[i].coords = data.animation[i].frames[index]; + } + } + else + { + foreach(i;0..data.length) + { + data.animation[i].time += dt * data.animation[i].speed; + if(cast(uint)data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time = data.animation[i].frames.length - 0.9; + uint index = cast(uint)(data.animation[i].time); + if(index < data.animation[i].frames.length)data.texture[i].coords = data.animation[i].frames[index]; + } + } + + } +} + +struct ParticleSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + @readonly Entity[] entitiy; + CParticle[] particle; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.particle[i].life -= launcher.delta_time; + if(data.particle[i].life < 0)launcher.manager.removeEntity(data.entitiy[i].id); + } + } +} + +struct RotateToTargetSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + int length; + @readonly CTarget[] target; + @readonly CLocation[] location; + CRotation[] rotation; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + Entity* target = launcher.manager.getEntity(data.target[i].target); + if(target) + { + CLocation* target_loc = target.getComponent!CLocation; + if(target_loc) + { + vec2 rel_pos = target_loc.value - data.location[i]; + float length = sqrtf(rel_pos.x*rel_pos.x + rel_pos.y*rel_pos.y); + if(rel_pos.x > 0)data.rotation[i] = acosf(rel_pos.y/length); + else data.rotation[i] = 2 * PI - acosf(rel_pos.y/length); + + } + } + //CLocation* target_loc = + //vec2 rel_pos = d + //data.rotation = 0; + } + } +} + +struct ShipTargetSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + int length; + @readonly CTargetPlayerShip[] target_player; + CTarget[] target; + } + + EntityID player_ship; + + void iterateShips(CShipIterator.EntitiesData data) + { + player_ship = data.entity[0].id; + } + + void onAddEntity(EntitiesData data) + { + foreach(i;0..data.length) + { + data.target[i].target = player_ship; + } + } + + bool onBegin() + { + Entity* ship = launcher.manager.getEntity(player_ship); + if(ship is null) + { + launcher.manager.callEntitiesFunction!CShipIterator(&iterateShips); + ship = launcher.manager.getEntity(player_ship); + if(ship is null)return false; + return true; + } + return false; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.target[i].target = player_ship; + } + } +} + +struct CShipIterator +{ + mixin ECS.System!1; + + struct EntitiesData + { + @readonly Entity[] entity; + @readonly CShip[] ship; + } + + bool onBegin() + { + return false; + } + + void onUpdate(EntitiesData data) + { + + } +} + +/*struct SpawnUponDeathSystem +{ + mixin ECS.System; + + struct EntitiesData + { + @readonly CSpawnUponDeath[] spawn; + @optional CTargetParent[] parent; + } + + EntityTemplate* flashes_emitter; + + void onCreate() + { + flashes_emitter = launcher.manager.allocateTemplate( + [ + CVelocity.component_id, CLocation.component_id, CParticleEmitter.component_id, + CParticleEmitterTime.component_id, CTargetParent.component_id + ].staticArray); + *flashes_emitter.getComponent!CParticleEmitter() = CParticleEmitter(vec2(0,0), vec2(400,400), 0); + } + + void onDestroy() + { + launcher.manager.freeTemplate(flashes_emitter); + } + + void onRemoveEntity(EntitiesData data) + { + //CSpawnUponDeath[] spawn = + switch(data.spawn[0].type) + { + case CSpawnUponDeath.Type.flashes_emitter: + if(data.parent) + { + /*Entity* parent_entity = launcher.manager.getEntity(data.parent[0].parent); + CChildren* children = entity.getComponent!CChildren; + foreach(ref EntityID child; children.childern) + { + if(child == event.id) + { + Entity* child_entity = launcher.manager.getEntity(child); + if(child_entity) + { + *flashes_emitter.getComponent!CTargetParent = data.parent[0]; + launcher.manager.addEntity(flashes_emitter); + //child = launcher.manager.addEntity(flashes_emitter); + } + break; + } + } + } + break; + default:break; + } + } + + //void handleEvent(Entity* entity, ) +}//*/ + +extern(C) float sqrtf(float x) @nogc nothrow @system; +extern(C) float acosf(float x) @nogc nothrow @system; +extern(C) float sinf(float x) @nogc nothrow @system; +extern(C) float cosf(float x) @nogc nothrow @system; +extern(C) float powf(float x, float y) @nogc nothrow @system; + /** *System is responsible for movement of objects with CInput component. *In this example every entity has same speed when using movement system. @@ -588,7 +2202,8 @@ struct InputMovementSystem //read only components can be marked with @readonly attribute or with const expression instead const (CInput)[] input; //components are treated as required by default - CLocation[] locations; + //CLocation[] locations; + CVelocity[] velocity; CTexture[] textures; } @@ -601,26 +2216,28 @@ struct InputMovementSystem move_vector = vec2(0,0); if(launcher.getKeyState(SDL_SCANCODE_W)) { - move_vector = vec2(0,1); - return true; + move_vector += vec2(0,1); } else if(launcher.getKeyState(SDL_SCANCODE_S)) { - move_vector = vec2(0,-1); - return true; + move_vector += vec2(0,-1); } - else if(launcher.getKeyState(SDL_SCANCODE_A)) + if(launcher.getKeyState(SDL_SCANCODE_A)) { - move_vector = vec2(-1,0); - return true; + move_vector += vec2(-1,0); } else if(launcher.getKeyState(SDL_SCANCODE_D)) { - move_vector = vec2(1,0); + move_vector += vec2(1,0); + } + + if(move_vector.x != 0 ||move_vector.y != 0) + { + move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y); return true; } //don't call system update because no key pressed - return true; + return false; } /** @@ -631,22 +2248,33 @@ struct InputMovementSystem */ void onUpdate(EntitiesData data) { - if(move_vector.x == 0) + /*if(move_vector.x == 0) { foreach(i; 0..data.length) { data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); } - return; - } + //return; + }*/ //move every entity using movement vector + //if(move_vector.x != 0 || move_vector.y != 0) foreach(i; 0..data.length) { - data.locations[i].x += move_vector.x * launcher.delta_time * 0.25; - data.locations[i].y += move_vector.y * launcher.delta_time * 0.25; - if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px); - else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); + data.velocity[i] += move_vector * launcher.delta_time * 0.005; + if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5; + else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5; + if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5; + else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5; + //data.locations[i].x += move_vector.x * launcher.delta_time * 0.25; + //data.locations[i].y += move_vector.y * launcher.delta_time * 0.25; + //if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px); + //else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); } + /*else + foreach(i; 0..data.length) + { + data.velocity[i] = vec2(0,0); + }*/ } } @@ -663,8 +2291,13 @@ void spaceInvadersStart() space_invaders.texture.create(); space_invaders.texture.load("assets/textures/atlas.png"); + space_invaders.shoot_grid = Mallocator.make!ShootGrid; + space_invaders.shoot_grid.create(ivec2(80,60), vec2(5,5)); + launcher.manager.beginRegister(); + launcher.manager.registerDependency(ShootGridDependency); + launcher.manager.registerComponent!CLocation; launcher.manager.registerComponent!CTexture; launcher.manager.registerComponent!CInput; @@ -673,21 +2306,72 @@ void spaceInvadersStart() launcher.manager.registerComponent!CScale; launcher.manager.registerComponent!CShootDirection; launcher.manager.registerComponent!CAutoShoot; - launcher.manager.registerComponent!CLaserWeapon; + launcher.manager.registerComponent!CWeapon; launcher.manager.registerComponent!CVelocity; - launcher.manager.registerComponent!CLaser; + launcher.manager.registerComponent!CBullet; launcher.manager.registerComponent!CSideMove; + launcher.manager.registerComponent!CDepth; + launcher.manager.registerComponent!CShootGrid; + launcher.manager.registerComponent!CGuild; + launcher.manager.registerComponent!CHitPoints; + launcher.manager.registerComponent!CHitMark; + launcher.manager.registerComponent!CUpgrade; + launcher.manager.registerComponent!CParticle; + launcher.manager.registerComponent!CMaxHitPoints; + launcher.manager.registerComponent!CAnimation; + launcher.manager.registerComponent!CRotation; + launcher.manager.registerComponent!CAnimationLooped; + launcher.manager.registerComponent!CDamping; + launcher.manager.registerComponent!CTargetParent; + launcher.manager.registerComponent!CTarget; + launcher.manager.registerComponent!CTargetPlayerShip; + launcher.manager.registerComponent!CChildren; + launcher.manager.registerComponent!CWeaponLocation; + launcher.manager.registerComponent!CInit; + launcher.manager.registerComponent!CBoss; + launcher.manager.registerComponent!CParts; + launcher.manager.registerComponent!CColliderScale; + launcher.manager.registerComponent!CParticleEmitter; + launcher.manager.registerComponent!CParticleEmitterTime; + launcher.manager.registerComponent!CSpawnUponDeath; + launcher.manager.registerComponent!CShootWaveUponDeath; launcher.manager.registerEvent!EChangeDirection; + launcher.manager.registerEvent!EDamage; + launcher.manager.registerEvent!EUpgrade; + launcher.manager.registerEvent!EDeath; + launcher.manager.registerEvent!EDestroyedChild; + launcher.manager.registerEvent!EBulletHit; //launcher.manager.registerSystem!MoveSystem(0); launcher.manager.registerSystem!DrawSystem(100); launcher.manager.registerSystem!InputMovementSystem(-100); + launcher.manager.registerSystem!MovementSystem(-99); + launcher.manager.registerSystem!ClampPositionSystem(-90); launcher.manager.registerSystem!LaserShootingSystem(0); - launcher.manager.registerSystem!MovementSystem(0); - launcher.manager.registerSystem!ClampPositionSystem(1); launcher.manager.registerSystem!ChangeDirectionSystem(0); - + launcher.manager.registerSystem!LaserCollisionSystem(-70); + launcher.manager.registerSystem!ShootGridManager(-80); + launcher.manager.registerSystem!ShootGridCleaner(-101); + launcher.manager.registerSystem!HitPointsSystem(0); + launcher.manager.registerSystem!HitMarkingSystem(-100); + launcher.manager.registerSystem!UpgradeCollisionSystem(-70); + launcher.manager.registerSystem!UpgradeSystem(-100); + launcher.manager.registerSystem!ParticleSystem(-100); + launcher.manager.registerSystem!AnimationSystem(-100); + launcher.manager.registerSystem!DampingSystem(-101); + launcher.manager.registerSystem!MoveToParentTargetSystem(-98); + launcher.manager.registerSystem!ParentOwnerSystem(-101); + launcher.manager.registerSystem!ShipWeaponSystem(-100); + launcher.manager.registerSystem!ParticleEmittingSystem(-95); + launcher.manager.registerSystem!RotateToTargetSystem(-100); + launcher.manager.registerSystem!ShipTargetSystem(-110); + launcher.manager.registerSystem!CShipIterator(-100); + launcher.manager.registerSystem!PartsDestroySystem(-80); + launcher.manager.registerSystem!ChildDestroySystem(-110); + launcher.manager.registerSystem!ShootWaveSystem(-100); + //launcher.manager.registerSystem!SpawnUponDeathSystem(-110); + launcher.manager.endRegister(); launcher.gui_manager.addSystem(DrawSystem.system_id,"Draw System"); @@ -696,102 +2380,194 @@ void spaceInvadersStart() launcher.gui_manager.addSystem(MovementSystem.system_id,"Movement System"); launcher.gui_manager.addSystem(ClampPositionSystem.system_id,"Clamp Position System"); launcher.gui_manager.addSystem(ChangeDirectionSystem.system_id,"Change Direction System"); + launcher.gui_manager.addSystem(LaserCollisionSystem.system_id,"Laser Collision System"); + launcher.gui_manager.addSystem(ShootGridManager.system_id,"Shoot Grid Manager"); + launcher.gui_manager.addSystem(ShootGridCleaner.system_id,"Shoot Grid Cleaner"); + launcher.gui_manager.addSystem(HitPointsSystem.system_id,"Hit Points System"); + launcher.gui_manager.addSystem(HitMarkingSystem.system_id,"Hit Marking System"); + launcher.gui_manager.addSystem(UpgradeCollisionSystem.system_id,"Upgrade Collision System"); + launcher.gui_manager.addSystem(UpgradeSystem.system_id,"Upgrade System"); + launcher.gui_manager.addSystem(ParticleSystem.system_id,"Particle System"); + launcher.gui_manager.addSystem(AnimationSystem.system_id,"Animation System"); + launcher.gui_manager.addSystem(DampingSystem.system_id,"Damping System"); + launcher.gui_manager.addSystem(MoveToParentTargetSystem.system_id,"Move To Target System"); + launcher.gui_manager.addSystem(ParentOwnerSystem.system_id,"Parent Owner System"); + launcher.gui_manager.addSystem(ShipWeaponSystem.system_id,"Ship Weapon System"); + launcher.gui_manager.addSystem(ParticleEmittingSystem.system_id,"Particle Emitting System"); + launcher.gui_manager.addSystem(RotateToTargetSystem.system_id,"Rotate To Target System"); + launcher.gui_manager.addSystem(ShipTargetSystem.system_id,"Ship Target System"); + launcher.gui_manager.addSystem(PartsDestroySystem.system_id,"Parts Destroy System"); + launcher.gui_manager.addSystem(ChildDestroySystem.system_id,"Child Destroy System"); + launcher.gui_manager.addSystem(ShootWaveSystem.system_id,"Shoot Wave System"); + //launcher.gui_manager.addSystem(SpawnUponDeathSystem.system_id,"Child Destroy System"); //launcher.manager.getSystem(CleanSystem.system_id).disable(); { - ushort[7] components = [CLocation.component_id, CTexture.component_id, CInput.component_id, CShip.component_id, CScale.component_id, CLaserWeapon.component_id, CShootDirection.component_id]; - space_invaders.ship_tmpl = launcher.manager.allocateTemplate(components); + space_invaders.ship_tmpl = launcher.manager.allocateTemplate( + [CVelocity.component_id, CHitMark.component_id, CHitPoints.component_id, + CLocation.component_id, CTexture.component_id, CInput.component_id, + CShip.component_id, CScale.component_id, CColliderScale.component_id, + CShootDirection.component_id, CShootGrid.component_id, CGuild.component_id, + CDamping.component_id, CChildren.component_id, CInit.component_id].staticArray + ); + space_invaders.ship_tmpl.getComponent!CTexture().coords = vec4(0,80,48,32)*px; + space_invaders.ship_tmpl.getComponent!CScale().value = vec2(48,32); + space_invaders.ship_tmpl.getComponent!CHitPoints().value = 1000; + space_invaders.ship_tmpl.getComponent!CDamping().value = 7; + space_invaders.ship_tmpl.getComponent!CInit().type = CInit.Type.space_ship; + space_invaders.ship_tmpl.getComponent!CColliderScale().value = vec2(26,24); - CScale* scale_comp = space_invaders.ship_tmpl.getComponent!CScale; - scale_comp.value = vec2(48,32); - CTexture* tex_comp = space_invaders.ship_tmpl.getComponent!CTexture; - tex_comp.tex = space_invaders.texture;//ship_tex; - tex_comp.coords = vec4(0*px,80*px,48*px,32*px); - CLocation* loc_comp = space_invaders.ship_tmpl.getComponent!CLocation; - loc_comp.value = vec2(64,64); - CLaserWeapon* weapon = space_invaders.ship_tmpl.getComponent!CLaserWeapon; - weapon.level = 10; - - launcher.manager.addEntity(space_invaders.ship_tmpl); + launcher.manager.addEntity(space_invaders.ship_tmpl,[CLocation(vec2(64,64)).ref_].staticArray); } { - ushort[5] components = [CLocation.component_id, CTexture.component_id, CVelocity.component_id, CScale.component_id, CLaser.component_id]; + ushort[6] components = [CLocation.component_id, CTexture.component_id, CVelocity.component_id, CScale.component_id, CBullet.component_id, CGuild.component_id]; space_invaders.laser_tmpl = launcher.manager.allocateTemplate(components); - CTexture* tex_comp = space_invaders.laser_tmpl.getComponent!CTexture; - tex_comp.tex = space_invaders.texture;//laser_tex; - tex_comp.coords = vec4(0*px,24*px,2*px,8*px); - CScale* scale_comp = space_invaders.laser_tmpl.getComponent!CScale; - scale_comp.value = vec2(2,8); - CVelocity* vel_comp = space_invaders.laser_tmpl.getComponent!CVelocity; - vel_comp.value = vec2(0,1); + space_invaders.laser_tmpl.getComponent!CTexture().coords = vec4(0,24,2,8)*px; + space_invaders.laser_tmpl.getComponent!CScale().value = vec2(2,8); + space_invaders.laser_tmpl.getComponent!CVelocity().value = vec2(0,1); } EntityTemplate* enemy_tmpl; EntityTemplate* grouped_tmpl; + EntityTemplate* tower_tmpl; + EntityTemplate* boss_tmpl; + //EntityTemplate* tower_weapon_tmpl; EntityID enemy_id; EntityID grouped_id; { - ushort[8] components = [CVelocity.component_id, CAutoShoot.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CLaserWeapon.component_id, CEnemy.component_id, CShootDirection.component_id];//, CVelocity.component_id]; - space_invaders.enemy_tmpl = launcher.manager.allocateTemplate(components); + boss_tmpl = launcher.manager.allocateTemplate( + [CHitMark.component_id, CParts.component_id, CLocation.component_id, + CTexture.component_id, CScale.component_id, CEnemy.component_id, + CBoss.component_id, CGuild.component_id, CInit.component_id, + CChildren.component_id, CSideMove.component_id, CVelocity.component_id, + CDepth.component_id].staticArray + ); - CTexture* tex_comp = space_invaders.enemy_tmpl.getComponent!CTexture; - tex_comp.tex = space_invaders.texture;//ship_tex; - tex_comp.coords = vec4(32*px,32*px,16*px,16*px); - CLocation* loc_comp = space_invaders.enemy_tmpl.getComponent!CLocation; - loc_comp.value = vec2(64,space_invaders.map_size.y - 16); - CShootDirection* shoot_dir_comp = space_invaders.enemy_tmpl.getComponent!CShootDirection; - shoot_dir_comp.direction = Direction.down; - CVelocity* vel_comp = space_invaders.enemy_tmpl.getComponent!CVelocity; - vel_comp.value = vec2(0.1,0); + //CTexture* tex_comp = boss_tmpl.getComponent!CTexture; + //tex_comp.tex = space_invaders.texture;//ship_tex; + //tex_comp.coords = vec4(128*px,0*px,96*px,48*px); + //CLocation* loc_comp = boss_tmpl.getComponent!CLocation; + //loc_comp.value = vec2(64,space_invaders.map_size.y - 16); + boss_tmpl.getComponent!CTexture().coords = vec4(128,0,96,48)*px; + boss_tmpl.getComponent!CGuild().guild = 1; + boss_tmpl.getComponent!CInit().type = CInit.Type.boss; + boss_tmpl.getComponent!CScale().value = vec2(96,48); + boss_tmpl.getComponent!CDepth().depth = -1; + boss_tmpl.getComponent!CParts().count = 4; + boss_tmpl.getComponent!CVelocity().value = vec2(0.05,0); + } + + { + tower_tmpl = launcher.manager.allocateTemplate( + [CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, + CTexture.component_id, CScale.component_id, CEnemy.component_id, + CShootGrid.component_id, CGuild.component_id, CInit.component_id, + CChildren.component_id].staticArray + ); + + tower_tmpl.getComponent!CTexture().coords = vec4(96,96,16,16)*px; + tower_tmpl.getComponent!CGuild().guild = 1; + tower_tmpl.getComponent!CInit().type = CInit.Type.tower; + tower_tmpl.getComponent!CHitPoints().value = 10; + } + + { + space_invaders.enemy_tmpl = launcher.manager.allocateTemplate( + [CWeaponLocation.component_id, CHitMark.component_id, CHitPoints.component_id, + CVelocity.component_id, CAutoShoot.component_id, CLocation.component_id, + CTexture.component_id, CScale.component_id, CWeapon.component_id, + CEnemy.component_id, CShootDirection.component_id, CShootGrid.component_id, + CGuild.component_id].staticArray + ); + + space_invaders.enemy_tmpl.getComponent!CTexture().coords = vec4(32,32,16,16)*px; + space_invaders.enemy_tmpl.getComponent!CShootDirection().direction = Direction.down; + space_invaders.enemy_tmpl.getComponent!CVelocity().value = vec2(0.1,0); + space_invaders.enemy_tmpl.getComponent!CGuild().guild = 1; + space_invaders.enemy_tmpl.getComponent!CWeaponLocation().rel_pos = vec2(0,-15); Entity* current_entity; - current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl); + current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl,[CLocation(vec2(32,space_invaders.map_size.y - 16)).ref_].staticArray); launcher.manager.addComponents(current_entity.id,CSideMove(0)); - loc_comp.value = vec2(128,space_invaders.map_size.y - 16); - current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl); + //loc_comp.value = vec2(128,space_invaders.map_size.y - 16); + current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl,[CLocation(vec2(128,space_invaders.map_size.y - 16)).ref_].staticArray); launcher.manager.addComponents(current_entity.id,CSideMove(-1)); enemy_id = current_entity.id; //enemy_tmpl = launcher.manager.allocateTemplate(current_entity.id); - loc_comp.value = vec2(256,space_invaders.map_size.y - 16); - launcher.manager.addEntity(space_invaders.enemy_tmpl); + //loc_comp.value = vec2(256,space_invaders.map_size.y - 16); + launcher.manager.addEntity(space_invaders.enemy_tmpl,[CLocation(vec2(256,space_invaders.map_size.y - 16)).ref_].staticArray); - loc_comp.value = vec2(0,space_invaders.map_size.y - 16); - current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl); + //loc_comp.value = vec2(0,space_invaders.map_size.y - 16); + current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl,[CLocation(vec2(0,space_invaders.map_size.y - 16)).ref_].staticArray); launcher.manager.addComponents(current_entity.id,CSideMove(0)); grouped_id = current_entity.id; //grouped_tmpl = launcher.manager.allocateTemplate(current_entity.id); } + + EntityTemplate* upgrade_tmpl; + + { + upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CUpgrade.component_id, CAnimationLooped.component_id, CAnimation.component_id].staticArray); + upgrade_tmpl.getComponent!CTexture().coords = vec4(0,32,16,16)*px; + upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.1); + *upgrade_tmpl.getComponent!CAnimation = CAnimation(HitPointsSystem.upgrade_laser_frames, 0, 0.75); + } + launcher.manager.commit(); enemy_tmpl = launcher.manager.allocateTemplate(enemy_id); grouped_tmpl = launcher.manager.allocateTemplate(grouped_id); - launcher.gui_manager.addTemplate(space_invaders.ship_tmpl,"Ship"); + space_invaders.bullet_tmpl[0] = launcher.manager.allocateTemplate( + [CLocation.component_id, CTexture.component_id, CVelocity.component_id, + CScale.component_id, CBullet.component_id, CGuild.component_id].staticArray + ); + space_invaders.bullet_tmpl[0].getComponent!CTexture().coords = vec4(0,24,2,8)*px; + space_invaders.bullet_tmpl[0].getComponent!CScale().value = vec2(2,8); + + space_invaders.bullet_tmpl[1] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); + space_invaders.bullet_tmpl[2] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); + space_invaders.bullet_tmpl[2].getComponent!CTexture().coords = vec4(64,32,8,16)*px; + space_invaders.bullet_tmpl[2].getComponent!CScale().value = vec2(8,16); + space_invaders.bullet_tmpl[3] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); + space_invaders.bullet_tmpl[3].getComponent!CTexture().coords = vec4(56,32,2,2)*px; + space_invaders.bullet_tmpl[3].getComponent!CScale().value = vec2(2,2); + // bullet_tmpl[3].getComponent!CTexture().coords = vec4(48,32,8,8)*px; + // bullet_tmpl[3].getComponent!CScale().value = vec2(8,8); + space_invaders.bullet_tmpl[4] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); + launcher.gui_manager.addTemplate(enemy_tmpl,"Enemy"); launcher.gui_manager.addTemplate(grouped_tmpl,"Grouped enemy"); - launcher.gui_manager.addTemplate(space_invaders.laser_tmpl,"Laser"); - + launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(space_invaders.ship_tmpl),"Ship"); + launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(space_invaders.laser_tmpl),"Laser"); + launcher.gui_manager.addTemplate(upgrade_tmpl,"Upgrade"); + launcher.gui_manager.addTemplate(tower_tmpl,"Tower"); + launcher.gui_manager.addTemplate(boss_tmpl,"Boss"); + launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[3]),"Cannon bullet"); + //launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[4]),"Laser"); + //launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[5]),"Laser"); } void spaceInvadersEnd() { - - launcher.manager.getSystem(DrawSystem.system_id).disable(); + /*launcher.manager.getSystem(DrawSystem.system_id).disable(); launcher.manager.getSystem(InputMovementSystem.system_id).disable(); launcher.manager.getSystem(LaserShootingSystem.system_id).disable(); launcher.manager.getSystem(MovementSystem.system_id).disable(); launcher.manager.getSystem(ClampPositionSystem.system_id).disable(); + launcher.manager.getSystem(ShootGridCleaner.system_id).disable();*/ - launcher.manager.freeTemplate(space_invaders.enemy_tmpl); + //launcher.manager.freeTemplate(space_invaders.enemy_tmpl); Mallocator.dispose(space_invaders); + space_invaders = null; } void spaceInvadersTool(vec2 position, Tool tool, int size) @@ -806,8 +2582,15 @@ void spaceInvadersTool(vec2 position, Tool tool, int size) { position.x += (randomf - 0.5) * size; position.y += (randomf - 0.5) * size; + if(position.y < 16)position.y = 16; + else if(position.y > 299)position.y = 299; *location = position; } + CWeapon* laser_weapon = tmpl.getComponent!CWeapon; + if(laser_weapon) + { + laser_weapon.shoot_time = randomf * CWeapon.levels[laser_weapon.level - 1].reload_time; + } launcher.manager.addEntity(tmpl); } break; diff --git a/demos/source/game_core/job_updater.d b/demos/source/game_core/job_updater.d index 46cd609..47d2423 100644 --- a/demos/source/game_core/job_updater.d +++ b/demos/source/game_core/job_updater.d @@ -1,13 +1,13 @@ module game_core.job_updater; -import ecs.std; -import ecs.vector; -import ecs.atomic; +import bubel.ecs.std; +import bubel.ecs.vector; +import bubel.ecs.atomic; import ecs_utils.utils; //import core.time; -import ecs.manager; +import bubel.ecs.manager; import mmutils.thread_pool; version(LDC) @@ -63,6 +63,7 @@ struct ECSJobUpdater JobData[1024] jobs; JobCaller[1024] callers; uint count = 0; + string name; void dependantOn(Group* dependency) { @@ -89,7 +90,7 @@ struct ECSJobUpdater void add(JobCaller caller) { callers[count] = caller; - jobs[count] = JobData(&callers[count].callJob,"hmm"); + jobs[count] = JobData(&callers[count].callJob,name); count++; } } @@ -216,6 +217,8 @@ struct ECSJobUpdater return; } + jobs[group.id].name = cast(string)group.caller.system.name; + foreach(ref job;group.jobs) { uint index = 0; @@ -233,7 +236,7 @@ struct ECSJobUpdater foreach(dep;group.dependencies) { - if(jobs[dep.id].count && dep.caller.system.execute && dep.caller.system.enabled)jobs[group.id].dependantOn(&jobs[dep.id]); + if(jobs[dep.id].count && dep.caller.system.willExecute && dep.caller.system.enabled)jobs[group.id].dependantOn(&jobs[dep.id]); else deps--; } diff --git a/demos/source/gui/manager.d b/demos/source/gui/manager.d index 8a46a92..4d452ad 100644 --- a/demos/source/gui/manager.d +++ b/demos/source/gui/manager.d @@ -4,10 +4,10 @@ import app; import cimgui.cimgui; -import ecs.std; -import ecs.system; -import ecs.vector; -import ecs.entity; +import bubel.ecs.std; +import bubel.ecs.system; +import bubel.ecs.vector; +import bubel.ecs.entity; import gui.system; import gui.template_; @@ -30,6 +30,7 @@ struct GUIManager systems.clear(); templates.clear(); + selected_tempalte = 0; } EntityTemplate* getSelectedTemplate() diff --git a/demos/source/gui/system.d b/demos/source/gui/system.d index bbee409..112bed3 100644 --- a/demos/source/gui/system.d +++ b/demos/source/gui/system.d @@ -1,6 +1,6 @@ module gui.system; -import ecs.system; +import bubel.ecs.system; struct SystemGUI { diff --git a/demos/source/gui/template_.d b/demos/source/gui/template_.d index 2aa1833..4d3de11 100644 --- a/demos/source/gui/template_.d +++ b/demos/source/gui/template_.d @@ -1,6 +1,6 @@ module gui.template_; -import ecs.entity; +import bubel.ecs.entity; struct TemplateGUI { diff --git a/demos/utils/source/ecs_utils/gfx/buffer.d b/demos/utils/source/ecs_utils/gfx/buffer.d index 2c25324..b11cb85 100644 --- a/demos/utils/source/ecs_utils/gfx/buffer.d +++ b/demos/utils/source/ecs_utils/gfx/buffer.d @@ -1,6 +1,6 @@ module ecs_utils.gfx.buffer; -import ecs.std; +import bubel.ecs.std; import glad.gl.gl; import glad.gl.gles2; diff --git a/demos/utils/source/ecs_utils/gfx/config.d b/demos/utils/source/ecs_utils/gfx/config.d index de5528a..0683647 100644 --- a/demos/utils/source/ecs_utils/gfx/config.d +++ b/demos/utils/source/ecs_utils/gfx/config.d @@ -2,7 +2,7 @@ module ecs_utils.gfx.config; import bindbc.sdl; -import ecs.std; +import bubel.ecs.std; import ecs_utils.gfx.material; import ecs_utils.gfx.mesh; @@ -18,7 +18,7 @@ enum LayerType sorted } -import ecs.vector; +import bubel.ecs.vector; static struct GfxConfig { diff --git a/demos/utils/source/ecs_utils/gfx/material.d b/demos/utils/source/ecs_utils/gfx/material.d index b697992..fbd88b9 100644 --- a/demos/utils/source/ecs_utils/gfx/material.d +++ b/demos/utils/source/ecs_utils/gfx/material.d @@ -2,7 +2,7 @@ module ecs_utils.gfx.material; import bindbc.sdl; -import ecs.std; +import bubel.ecs.std; import ecs_utils.gfx.shader; diff --git a/demos/utils/source/ecs_utils/gfx/mesh.d b/demos/utils/source/ecs_utils/gfx/mesh.d index 346226f..20d5855 100644 --- a/demos/utils/source/ecs_utils/gfx/mesh.d +++ b/demos/utils/source/ecs_utils/gfx/mesh.d @@ -2,7 +2,7 @@ module ecs_utils.gfx.mesh; import bindbc.sdl; -import ecs.std; +import bubel.ecs.std; import ecs_utils.gfx.buffer; diff --git a/demos/utils/source/ecs_utils/gfx/renderer.d b/demos/utils/source/ecs_utils/gfx/renderer.d index 2a04c57..fcd6a88 100644 --- a/demos/utils/source/ecs_utils/gfx/renderer.d +++ b/demos/utils/source/ecs_utils/gfx/renderer.d @@ -2,14 +2,17 @@ module ecs_utils.gfx.renderer; import bindbc.sdl; -import ecs.std; +import bubel.ecs.std; //import ecs_utils.core : Backend; import ecs_utils.gfx.buffer; import ecs_utils.gfx.texture; import ecs_utils.math.vector; -import glad.gl.gl; +import bubel.ecs.block_allocator; +import bubel.ecs.vector; +version(WebAssembly)import glad.gl.gles2; +else import glad.gl.gl; version = ver1; /*version(ver5)version = vv2; @@ -41,9 +44,10 @@ enum RenderTechnique struct Renderer { //static SDL_Renderer* main_sdl_renderer; + BlockAllocator allocator; enum MaxObjects = 1024 * 64 * 4; - enum BufferUsage = GL_STATIC_DRAW; + enum BufferUsage = GL_DYNAMIC_DRAW; //SDL_Window* sdl_window; //SDL_Renderer* sdl_renderer; @@ -53,8 +57,136 @@ struct Renderer vec2 view_pos = vec2(-1,-1); vec2 view_size = vec2(1,1); + enum block_size = 2^^16; + enum batch_size = block_size/68;//963;//16_384; //uint[2] time_queries; + struct VertexBlock + { + enum max_items = batch_size;//963; + byte[] batch_vertices; + ushort[] batch_indices; + void* memory; + uint items = 0; + } + + Mutex* get_block_mutex; + Mutex* block_stack_mutex; + + VertexBlock getBlock() + { + VertexBlock block; + get_block_mutex.lock(); + block.memory = allocator.getBlock(); + get_block_mutex.unlock(); + block.batch_vertices = (cast(byte*)block.memory)[0 .. VertexBlock.max_items * 4 * 14]; + block.batch_indices = (cast(ushort*)block.memory)[VertexBlock.max_items * 4 * 7 .. VertexBlock.max_items * (4 * 7 + 6)]; + return block; + } + + Vector!VertexBlock blocks; + uint current_block = 0; + uint render_blocks = 0; + + void pushBlock(VertexBlock block) + { + block_stack_mutex.lock(); + prepared_items += block.items; + blocks.add(block); + render_blocks++; + block_stack_mutex.unlock(); + } + + bool isRemainingBlocks() + { + if(render_blocks <= current_block)return false; + return true; + } + + VertexBlock fetchBlock() + { + block_stack_mutex.lock(); + VertexBlock block = blocks[current_block]; + current_block++; + block_stack_mutex.unlock(); + return block; + } + + void freeBlocks() + { + /*block_stack_mutex.lock(); + render_blocks = 0; + current_block = 0; + foreach(VertexBlock block; blocks) + { + allocator.freeBlock(block.memory); + } + blocks.clear; + prepared_items=0; + draw_list.clear(); + block_stack_mutex.unlock();*/ + foreach(ref Thread thread; threads) + { + foreach(VertexBlock block; thread.blocks) + { + allocator.freeBlock(block.memory); + } + thread.blocks.clear(); + } + render_blocks = 0; + current_block = 0; + prepared_items = 0; + draw_list.clear(); + } + + void pushData() + { + foreach(ref Thread thread; threads) + { + foreach(VertexBlock block; thread.blocks) + { + uint items = block.items; + if(items + item_id >= MaxObjects)items = MaxObjects - item_id; + batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr); + batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr); + draw_list.add(DrawCall(item_id,items)); + item_id += items; + } + //thread.blocks.clear(); + } + //if(!isRemainingBlocks())return; + /* while(isRemainingBlocks()) + { + VertexBlock block = fetchBlock(); + uint items = block.items; + if(items + item_id >= MaxObjects)items = MaxObjects - item_id; + batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr); + batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr); + draw_list.add(DrawCall(item_id,items)); + item_id += items; + }*/ + } + + void pushThreadsBlocks() + { + foreach(i, ref Thread thread; threads) + { + //pushBlock(thread.block); + thread.blocks.add(thread.block); + thread.block = getBlock(); + } + } + + struct Thread + { + //Vector!VertexBlock block; + RenderData[] render_list; + VertexBlock block; + Vector!VertexBlock blocks; + } + Thread[] threads; + + Buffer[2] ubos; int block_alignment = 1; int block_max_size = 16384; @@ -71,7 +203,7 @@ struct Renderer Buffer[2] batch_vbo; Buffer[2] batch_ibo; - float[] batch_vertices; + ubyte[] batch_vertices; ushort[] batch_indices; Buffer indirect_buffer; @@ -90,8 +222,17 @@ struct Renderer uint mesh_id; } + struct DrawCall + { + uint start; + uint count; + } + + Vector!DrawCall draw_list; + RenderData[] render_list; uint item_id; + uint prepared_items; uint[] multi_count; uint[] multi_offset; @@ -109,6 +250,18 @@ struct Renderer { //this.technique = __ecs_used_technique; __initialize(this); + + get_block_mutex = Mallocator.make!Mutex(); + block_stack_mutex = Mallocator.make!Mutex(); + get_block_mutex.initialize(); + block_stack_mutex.initialize(); + + + threads = Mallocator.makeArray!Thread(32); + foreach(ref Thread thread;threads) + { + thread.block = getBlock(); + } } private static void __initialize_gl(ref Renderer this_) @@ -141,16 +294,16 @@ struct Renderer case Technique.vbo_batch: batch_vbo[0].create(); batch_ibo[0].create(); - batch_vbo[0].bufferData(Buffer.BindTarget.array,16,4*MaxObjects,BufferUsage,null); + batch_vbo[0].bufferData(Buffer.BindTarget.array,14,4*MaxObjects,BufferUsage,null); batch_ibo[0].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null); batch_vbo[1].create(); batch_ibo[1].create(); - batch_vbo[1].bufferData(Buffer.BindTarget.array,16,4*MaxObjects,BufferUsage,null); + batch_vbo[1].bufferData(Buffer.BindTarget.array,14,4*MaxObjects,BufferUsage,null); batch_ibo[1].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null); - batch_vertices = Mallocator.makeArray!float(16*MaxObjects); - batch_indices = Mallocator.makeArray!ushort(6*MaxObjects); + //batch_vertices = Mallocator.makeArray!ubyte(14*4*MaxObjects); + //batch_indices = Mallocator.makeArray!ushort(6*MaxObjects); break; case Technique.instanced_attrib_divisor: goto case(Technique.uniform_buffer_indexed); @@ -253,6 +406,8 @@ struct Renderer SDL_Log("Uniform block alignment: %u",block_alignment); SDL_Log("Uniform block max size: %u",block_max_size); SDL_Log("Data offset: %u",data_offset); + + allocator = BlockAllocator(block_size, 32); } } @@ -261,12 +416,13 @@ struct Renderer } - void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, float angle = 0, uint material_id = 0, uint mesh_id = 0) + void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, short depth = 0, uint color = uint.max, float angle = 0, uint material_id = 0, uint mesh_id = 0, uint thread_id = 0) { - __draw(this,tex,pos,size,coords,angle,material_id,mesh_id); + if(prepared_items >= MaxObjects)return; + __draw(this,tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id); } - private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id) + private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) { /*with(this_) { @@ -286,7 +442,7 @@ struct Renderer }*/ } - private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id) + private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) { //import core.stdc.string; with(this_) @@ -330,19 +486,20 @@ struct Renderer data_index += data_offset; item_id++; + prepared_items++; } } - private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id) + private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id = 0) { import ecs_utils.gfx.config; + short[3] mem = [depth, *cast(short*)&color, *(cast(short*)&color + 1)]; //import core.stdc.string; with(this_) { - if(item_id >= MaxObjects)return; + //if(item_id >= MaxObjects)return; //pos += view_pos; - size.x *= view_size.x; - size.y *= view_size.y; + pos.x = pos.x * view_size.x + view_pos.x; pos.y = pos.y * view_size.y + view_pos.y;//*/ @@ -355,67 +512,134 @@ struct Renderer memcpy(ptr+16,pos.data.ptr,8); memcpy(ptr+32,coords.data.ptr,16);*/ + short[] verts = cast(short[])threads[thread_id].block.batch_vertices; + uint item_id = threads[thread_id].block.items; + if(angle == 0) { - batch_vertices[item_id*16] = GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x; - batch_vertices[item_id*16+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y; - batch_vertices[item_id*16+4] = GfxConfig.meshes[mesh_id].vertices[4] * size.x + pos.x; - batch_vertices[item_id*16+5] = GfxConfig.meshes[mesh_id].vertices[5] * size.y + pos.y; - batch_vertices[item_id*16+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x + pos.x; - batch_vertices[item_id*16+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y + pos.y; - batch_vertices[item_id*16+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x + pos.x; - batch_vertices[item_id*16+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y + pos.y; + size.x *= view_size.x; + size.y *= view_size.y; + + verts[item_id*28] = cast(short)((GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x) * 8191); + verts[item_id*28+1] = cast(short)((GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y) * 8191); + verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767); + verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+4,mem.ptr,6); + + + verts[item_id*28+7] = cast(short)((GfxConfig.meshes[mesh_id].vertices[4] * size.x + pos.x) * 8191); + verts[item_id*28+8] = cast(short)((GfxConfig.meshes[mesh_id].vertices[5] * size.y + pos.y) * 8191); + verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767); + verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+11,mem.ptr,6); + + + verts[item_id*28+14] = cast(short)((GfxConfig.meshes[mesh_id].vertices[8] * size.x + pos.x) * 8191); + verts[item_id*28+15] = cast(short)((GfxConfig.meshes[mesh_id].vertices[9] * size.y + pos.y) * 8191); + verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767); + verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+18,mem.ptr,6); + + + verts[item_id*28+21] = cast(short)((GfxConfig.meshes[mesh_id].vertices[12] * size.x + pos.x) * 8191); + verts[item_id*28+22] = cast(short)((GfxConfig.meshes[mesh_id].vertices[13] * size.y + pos.y) * 8191); + verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767); + verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+25,mem.ptr,6); } else { //import core.stdc.math; - float sinn = sinf(angle); - float coss = cosf(angle); + float sinx = sinf(angle) * size.x * view_size.y; + float cosx = cosf(angle) * size.x * view_size.x; + float siny = sinf(angle) * size.y * view_size.x; + float cosy = cosf(angle) * size.y * view_size.y; - /*batch_vertices[item_id*16] = GfxConfig.meshes[mesh_id].vertices[0] * size.x; - batch_vertices[item_id*16+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y; - batch_vertices[item_id*16+4] = GfxConfig.meshes[mesh_id].vertices[4] * size.x; - batch_vertices[item_id*16+5] = GfxConfig.meshes[mesh_id].vertices[5] * size.y; - batch_vertices[item_id*16+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x; - batch_vertices[item_id*16+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y; - batch_vertices[item_id*16+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x; - batch_vertices[item_id*16+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y;*/ + /*batch_vertices[item_id*28] = GfxConfig.meshes[mesh_id].vertices[0] * size.x; + batch_vertices[item_id*28+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y; + batch_vertices[item_id*28+4] = GfxConfig.meshes[mesh_id].vertices[4] * size.x; + batch_vertices[item_id*28+5] = GfxConfig.meshes[mesh_id].vertices[5] * size.y; + batch_vertices[item_id*28+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x; + batch_vertices[item_id*28+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y; + batch_vertices[item_id*28+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x; + batch_vertices[item_id*28+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y;*/ - batch_vertices[item_id*16] = (GfxConfig.meshes[mesh_id].vertices[0] * coss + GfxConfig.meshes[mesh_id].vertices[1] * sinn) * size.x + pos.x; - batch_vertices[item_id*16+1] = (GfxConfig.meshes[mesh_id].vertices[1] * coss - GfxConfig.meshes[mesh_id].vertices[0] * sinn) * size.y + pos.y; - batch_vertices[item_id*16+4] = (GfxConfig.meshes[mesh_id].vertices[4] * coss + GfxConfig.meshes[mesh_id].vertices[5] * sinn) * size.x + pos.x; - batch_vertices[item_id*16+5] = (GfxConfig.meshes[mesh_id].vertices[5] * coss - GfxConfig.meshes[mesh_id].vertices[4] * sinn) * size.y + pos.y; - batch_vertices[item_id*16+8] = (GfxConfig.meshes[mesh_id].vertices[8] * coss + GfxConfig.meshes[mesh_id].vertices[9] * sinn) * size.x + pos.x; - batch_vertices[item_id*16+9] = (GfxConfig.meshes[mesh_id].vertices[9] * coss - GfxConfig.meshes[mesh_id].vertices[8] * sinn) * size.y + pos.y; - batch_vertices[item_id*16+12] = (GfxConfig.meshes[mesh_id].vertices[12] * coss + GfxConfig.meshes[mesh_id].vertices[13] * sinn) * size.x + pos.x; - batch_vertices[item_id*16+13] = (GfxConfig.meshes[mesh_id].vertices[13] * coss - GfxConfig.meshes[mesh_id].vertices[12] * sinn) * size.y + pos.y; + verts[item_id*28] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[0] * cosx + GfxConfig.meshes[mesh_id].vertices[1] * siny) + pos.x) * 8191); + verts[item_id*28+1] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[1] * cosy - GfxConfig.meshes[mesh_id].vertices[0] * sinx) + pos.y) * 8191); + verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767); + verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+4,mem.ptr,6); + + + verts[item_id*28+7] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[4] * cosx + GfxConfig.meshes[mesh_id].vertices[5] * siny) + pos.x) * 8191); + verts[item_id*28+8] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[5] * cosy - GfxConfig.meshes[mesh_id].vertices[4] * sinx) + pos.y) * 8191); + verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767); + verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+11,mem.ptr,6); + + + verts[item_id*28+14] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[8] * cosx + GfxConfig.meshes[mesh_id].vertices[9] * siny) + pos.x) * 8191); + verts[item_id*28+15] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[9] * cosy - GfxConfig.meshes[mesh_id].vertices[8] * sinx) + pos.y) * 8191); + verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767); + verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+18,mem.ptr,6); + + + verts[item_id*28+21] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[12] * cosx + GfxConfig.meshes[mesh_id].vertices[13] * siny) + pos.x) * 8191); + verts[item_id*28+22] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[13] * cosy - GfxConfig.meshes[mesh_id].vertices[12] * sinx) + pos.y) * 8191); + verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767); + verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+25,mem.ptr,6); } - batch_vertices[item_id*16+2] = GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x; - batch_vertices[item_id*16+3] = GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y; - batch_vertices[item_id*16+6] = GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x; - batch_vertices[item_id*16+7] = GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y; - batch_vertices[item_id*16+10] = GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x; - batch_vertices[item_id*16+11] = GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y; - batch_vertices[item_id*16+14] = GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x; - batch_vertices[item_id*16+15] = GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y; + /*verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767); + verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767); + verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767); + verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767); + verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767); + verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767); + verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767); + verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);*/ - uint ind_id = item_id % 16_384; + /*verts[item_id*28+4] = depth; + verts[item_id*28+11] = depth; + verts[item_id*28+18] = depth; + verts[item_id*28+25] = depth; - batch_indices[item_id*6] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[0] + ind_id*4); - batch_indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id*4); - batch_indices[item_id*6+2] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[2] + ind_id*4); - batch_indices[item_id*6+3] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[3] + ind_id*4); - batch_indices[item_id*6+4] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[4] + ind_id*4); - batch_indices[item_id*6+5] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[5] + ind_id*4); + *cast(uint*)&verts[item_id*28+5] = color; + *cast(uint*)&verts[item_id*28+12] = color; + *cast(uint*)&verts[item_id*28+19] = color; + *cast(uint*)&verts[item_id*28+26] = color; + + memcpy(verts.ptr+item_id*28+4,mem.ptr,6); + memcpy(verts.ptr+item_id*28+11,mem.ptr,6); + memcpy(verts.ptr+item_id*28+18,mem.ptr,6); + memcpy(verts.ptr+item_id*28+25,mem.ptr,6);*/ + + uint ind_id = (item_id % batch_size)*4; + + ushort[] indices = threads[thread_id].block.batch_indices; + + indices[item_id*6] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[0] + ind_id); + indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id); + indices[item_id*6+2] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[2] + ind_id); + indices[item_id*6+3] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[3] + ind_id); + indices[item_id*6+4] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[4] + ind_id); + indices[item_id*6+5] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[5] + ind_id); //render_list[item_id] = RenderData(tex,material_id,mesh_id); - render_list[item_id].texture = tex; - render_list[item_id].material_id = material_id; - render_list[item_id].mesh_id = mesh_id; + //render_list[item_id].texture = tex; + //render_list[item_id].material_id = material_id; + //render_list[item_id].mesh_id = mesh_id; //data_index += 1;//data_offset; - item_id++; + threads[thread_id].block.items++; + if(threads[thread_id].block.items >= VertexBlock.max_items) + { + //pushBlock(threads[thread_id].block); + threads[thread_id].blocks.add(threads[thread_id].block); + threads[thread_id].block = getBlock(); + } } } @@ -433,9 +657,22 @@ struct Renderer { glClearColor(0,0,0,0); glViewport(0,0,this_.resolution.x,this_.resolution.y); - glClear(GL_COLOR_BUFFER_BIT);// | GL_DEPTH_BUFFER_BIT); - glDisable(GL_DEPTH_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + //glDisable(GL_DEPTH_TEST); + glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); + glDepthFunc(GL_LESS); + + version(WebAssembly) + { + glDepthRangef(0,1); + } + else + { + glDepthRange(0,1); + } + //glDepthRange(0,1); + //glClearDepth(1); } void present() @@ -450,6 +687,10 @@ struct Renderer private static void __present_gl(ref Renderer this_) { + + this_.pushThreadsBlocks(); + this_.pushData(); + glViewport(0,0,this_.resolution.x,this_.resolution.y); //glEnable(GL_ALPHA_TEST); //glAlphaFunc(GL_GREATER, 0.01); @@ -471,14 +712,17 @@ struct Renderer break; case Technique.vbo_batch: //if(data_index){ - batch_vbo[0].bufferSubData(Buffer.BindTarget.array,item_id*4*16,0,batch_vertices.ptr); - batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,item_id*6*2,0,batch_indices.ptr); + //batch_vbo[0].bufferSubData(Buffer.BindTarget.array,item_id*4*14,0,batch_vertices.ptr); + //batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,item_id*6*2,0,batch_indices.ptr); batch_vbo[0].bind(Buffer.BindTarget.array); batch_ibo[0].bind(Buffer.BindTarget.element_array); - glVertexAttribPointer(0,2,GL_FLOAT,false,16,null); - glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)8);//} + //glVertexAttribPointer(0,2,GL_SHORT,true,14,null); + //glVertexAttribPointer(1,2,GL_SHORT,true,14,cast(void*)4);//} + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + //glVertexAttribPointer(2,1,GL_SHORT,true,14,cast(void*)6);//} break; case Technique.instanced_attrib_divisor: ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr); @@ -575,8 +819,8 @@ struct Renderer //glBeginQuery(GL_TIME_ELAPSED, time_queries[0]); if(technique == Technique.vbo_batch) { - uint items = item_id/16_384+1; - foreach(i; 0..items) + //uint items = item_id/batch_size+1; + foreach(i; 0..draw_list.length) { if(material_id != render_list[i].material_id) { @@ -591,16 +835,20 @@ struct Renderer render_list[i].texture.bind(); } - uint instance_count = 16_384; - if((i+1)*16_384 > item_id) + /*uint instance_count = batch_size; + if((i+1)*batch_size > item_id) { - instance_count = item_id%16_384; - } + instance_count = item_id%batch_size; + }*/ - glVertexAttribPointer(0,2,GL_FLOAT,false,16,cast(void*)(i*16_384*4*16)); - glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)(i*16_384*4*16+8)); + // glVertexAttribPointer(0,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16)); + // glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16+8)); + glVertexAttribPointer(0,2,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14)); + glVertexAttribPointer(1,2,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14+4)); + glVertexAttribPointer(2,1,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14+8)); + glVertexAttribPointer(3,4,GL_UNSIGNED_BYTE,true,14,cast(void*)(draw_list[i].start*4*14+10)); - glDrawElements(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2)); + glDrawElements(GL_TRIANGLES,draw_list[i].count*6,GL_UNSIGNED_SHORT,cast(void*)(draw_list[i].start*6*2)); //glDrawElementsBaseVertex(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2),i*16_384*4); } @@ -783,6 +1031,9 @@ struct Renderer } glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + this_.freeBlocks(); /*glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/ @@ -803,7 +1054,7 @@ struct Renderer view_pos = (pos - size * 0.5) * view_size; } - __gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id) __draw; + __gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) __draw; __gshared void function(ref Renderer this_) __present; __gshared void function(ref Renderer this_) __clear; __gshared void function(ref Renderer this_) __initialize; diff --git a/demos/utils/source/ecs_utils/gfx/shader.d b/demos/utils/source/ecs_utils/gfx/shader.d index 7749013..5e24d9a 100644 --- a/demos/utils/source/ecs_utils/gfx/shader.d +++ b/demos/utils/source/ecs_utils/gfx/shader.d @@ -2,7 +2,7 @@ module ecs_utils.gfx.shader; import bindbc.sdl; -import ecs.std; +import bubel.ecs.std; import glad.gl.gl; diff --git a/demos/utils/source/ecs_utils/gfx/texture.d b/demos/utils/source/ecs_utils/gfx/texture.d index bb4c62a..d4e2089 100644 --- a/demos/utils/source/ecs_utils/gfx/texture.d +++ b/demos/utils/source/ecs_utils/gfx/texture.d @@ -2,7 +2,7 @@ module ecs_utils.gfx.texture; import bindbc.sdl; -import ecs.std; +import bubel.ecs.std; import ecs_utils.math.vector; @@ -54,6 +54,8 @@ struct Texture data.data = Mallocator.makeArray!ubyte(surf.w*surf.h*surf.format.BytesPerPixel); data.data[0..$] = (cast(ubyte*)surf.pixels)[0..data.data.length]; + SDL_FreeSurface(surf); + glGenTextures(1, &data.gl_handle); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,data.gl_handle); @@ -78,11 +80,12 @@ struct Texture glBindTexture(GL_TEXTURE_2D, data.gl_handle); } - void destory() + void destory() @nogc nothrow { if(data) { glDeleteTextures(1, &data.gl_handle); + if(data.data)Mallocator.dispose(data.data); Mallocator.dispose(data); data = null; } diff --git a/demos/utils/source/ecs_utils/gfx/vertex.d b/demos/utils/source/ecs_utils/gfx/vertex.d index 1d11fdd..b63803c 100644 --- a/demos/utils/source/ecs_utils/gfx/vertex.d +++ b/demos/utils/source/ecs_utils/gfx/vertex.d @@ -1,6 +1,6 @@ module ecs_utils.gfx.vertex; -import ecs.std; +import bubel.ecs.std; struct Vertex { diff --git a/demos/utils/source/ecs_utils/math/vector.d b/demos/utils/source/ecs_utils/math/vector.d index 4b9f3af..a8f14f8 100644 --- a/demos/utils/source/ecs_utils/math/vector.d +++ b/demos/utils/source/ecs_utils/math/vector.d @@ -2,6 +2,18 @@ module ecs_utils.math.vector; struct vec2 { + this(float v) @nogc nothrow + { + x = v; + y = v; + } + + this(float x, float y) @nogc nothrow + { + this.x = x; + this.y = y; + } + union { struct @@ -75,6 +87,22 @@ struct vec4 float[4] data; } + this(float v) @nogc nothrow + { + x = v; + y = v; + z = v; + w = v; + } + + this(float x, float y, float z, float w) @nogc nothrow + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + vec4 opBinary(string op)(float v) { static if (op == "+") return vec4(x + v, y + v, z + v, w + v); @@ -96,6 +124,27 @@ struct ivec2 } int[2] data; } + + this(int v) @nogc nothrow + { + x = v; + y = v; + } + + this(int x, int y) @nogc nothrow + { + this.x = x; + this.y = y; + } + + ivec2 opBinary(string op, T)(T v) + { + static if (op == "+") return ivec2(x + v, y + v); + else static if (op == "-") return ivec2(x - v, y - v); + else static if (op == "*") return ivec2(x * v, y * v); + else static if (op == "/") return ivec2(x / v, y / v); + else static assert(0, "Operator "~op~" not implemented"); + } vec2 opCast() { @@ -116,4 +165,20 @@ struct ivec4 } int[4] data; } + + this(int v) @nogc nothrow + { + x = v; + y = v; + z = v; + w = v; + } + + this(int x, int y, int z, int w) @nogc nothrow + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } } \ No newline at end of file diff --git a/demos/utils/source/ecs_utils/utils.d b/demos/utils/source/ecs_utils/utils.d index 4c143fd..8ce5cd1 100644 --- a/demos/utils/source/ecs_utils/utils.d +++ b/demos/utils/source/ecs_utils/utils.d @@ -21,7 +21,19 @@ float randomRangef(float min, float max) return rand()%4096; }*/ -extern(C) int printf(scope const char* format, ...) @nogc nothrow @system; +version(GNU) +{ + public import core.stdc.stdio : printf; + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else +{ + extern(C) int printf(scope const char* format, ...) @nogc nothrow @system; + public import std.array : staticArray; +} extern(C) int rand(); version(D_BetterC) diff --git a/dub.json b/dub.json index c23a4e6..01cf85c 100755 --- a/dub.json +++ b/dub.json @@ -74,6 +74,9 @@ "dflags": [ "-betterC", "-defaultlib=" + ], + "dflags-gdc": [ + "-fno-druntime" ] }, { @@ -88,9 +91,7 @@ ], "dflags-gdc": [ "-fno-druntime", - "-fvisibility=hidden" - ], - "lflags-gdc": [ + "-fvisibility=hidden", "-lpthread" ] }, @@ -105,9 +106,7 @@ "-betterC" ], "dflags-gdc": [ - "-fno-druntime" - ], - "lflags-gdc": [ + "-fno-druntime", "-lpthread" ] }, @@ -118,6 +117,9 @@ "-betterC", "-unittest" ], + "dflags-gdc": [ + "-fno-druntime" + ], "sourcePaths": ["source/","tests/"], "mainSourceFile":"tests/runner.d", "excludedSourceFiles":[ diff --git a/meson.build b/meson.build index 3317d5c..c31b93a 100644 --- a/meson.build +++ b/meson.build @@ -1,21 +1,21 @@ project('DECS', 'd') src = [ - 'source/ecs/atomic.d', - 'source/ecs/attributes.d', - 'source/ecs/block_allocator.d', - 'source/ecs/core.d', - 'source/ecs/entity.d', - 'source/ecs/events.d', - 'source/ecs/hash_map.d', - 'source/ecs/id_manager.d', - 'source/ecs/manager.d', - 'source/ecs/package.d', - 'source/ecs/simple_vector.d', - 'source/ecs/std.d', - 'source/ecs/system.d', - 'source/ecs/traits.d', - 'source/ecs/vector.d' + 'source/bubel/ecs/atomic.d', + 'source/bubel/ecs/attributes.d', + 'source/bubel/ecs/block_allocator.d', + 'source/bubel/ecs/core.d', + 'source/bubel/ecs/entity.d', + 'source/bubel/ecs/events.d', + 'source/bubel/ecs/hash_map.d', + 'source/bubel/ecs/id_manager.d', + 'source/bubel/ecs/manager.d', + 'source/bubel/ecs/package.d', + 'source/bubel/ecs/simple_vector.d', + 'source/bubel/ecs/std.d', + 'source/bubel/ecs/system.d', + 'source/bubel/ecs/traits.d', + 'source/bubel/ecs/vector.d' ] tests_src = [ diff --git a/source/bubel/ecs/atomic.d b/source/bubel/ecs/atomic.d new file mode 100644 index 0000000..8ea43df --- /dev/null +++ b/source/bubel/ecs/atomic.d @@ -0,0 +1,132 @@ +/************************************************************************************************************************ +It's internal code. Can be used for atomics if emscripten backend will be used. + +This module contain atomic operations which include support for emscripten atomics functions. +Emscripten functions are contained in API similar to druntime. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ +module bubel.ecs.atomic; + +version (Emscripten) version = ECSEmscripten; + +version (ECSEmscripten) +{ + import std.traits; + + enum MemoryOrder + { + acq, + acq_rel, + raw, + rel, + seq + } + + extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure; + extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure; + extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure; + + public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) + { + static if (op == "+=") + { + static if (is(T == byte) || is(T == ubyte)) + return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else static if (is(T == short) || is(T == ushort)) + return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else static if (is(T == int) || is(T == uint)) + return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else + static assert(0); + } + else static if (op == "-=") + { + static if (is(T == byte) || is(T == ubyte)) + return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else static if (is(T == short) || is(T == ushort)) + return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else static if (is(T == int) || is(T == uint)) + return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else + static assert(0); + } + } + + public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, + V newval) + { + alias UT = Unqual!T; + static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte)) + emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval); + else static if (is(UT == short) || is(UT == ushort)) + emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval); + else static if (is(UT == int) || is(UT == uint)) + emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval); + else + static assert(0); + } + + public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( + ref const T val) + { + alias UT = Unqual!T; + static if (is(UT == bool)) + return emscripten_atomic_load_u8(cast(const void*)&val) != 0; + else static if (is(UT == byte) || is(UT == ubyte)) + return emscripten_atomic_load_u8(cast(const void*)&val); + else static if (is(UT == short) || is(UT == ushort)) + return emscripten_atomic_load_u16(cast(const void*)&val); + else static if (is(UT == int) || is(UT == uint)) + return emscripten_atomic_load_u32(cast(const void*)&val); + else + static assert(0); + } + + public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, + MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) + { + alias UT = Unqual!T; + static if (is(UT == bool)) + return emscripten_atomic_cas_u8(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == byte) || is(UT == ubyte)) + return emscripten_atomic_cas_u8(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == short) || is(UT == ushort)) + return emscripten_atomic_cas_u16(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == int) || is(UT == uint)) + return emscripten_atomic_cas_u32(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else + static assert(0); + } +} +else +{ + public import core.atomic; +} diff --git a/source/ecs/attributes.d b/source/bubel/ecs/attributes.d similarity index 94% rename from source/ecs/attributes.d rename to source/bubel/ecs/attributes.d index 20d0f60..d094aad 100644 --- a/source/ecs/attributes.d +++ b/source/bubel/ecs/attributes.d @@ -20,9 +20,9 @@ Struct EntitiesData Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ -module ecs.attributes; +module bubel.ecs.attributes; ///Used to mark optional components for system. enum optional = "optional"; ///Used to mark readonly components for system. "const" can be used insted. -enum readonly = "readonly"; \ No newline at end of file +enum readonly = "readonly"; diff --git a/source/ecs/block_allocator.d b/source/bubel/ecs/block_allocator.d similarity index 82% rename from source/ecs/block_allocator.d rename to source/bubel/ecs/block_allocator.d index d9f08ca..d3070c4 100644 --- a/source/ecs/block_allocator.d +++ b/source/bubel/ecs/block_allocator.d @@ -6,10 +6,10 @@ Module contain memory allocator. Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ -module ecs.block_allocator; +module bubel.ecs.block_allocator; -import ecs.manager; -import ecs.std; +import bubel.ecs.manager; +import bubel.ecs.std; /************************************************************************************************************************ Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated. @@ -35,7 +35,7 @@ struct BlockAllocator */ void freeBlock(void* block) nothrow @nogc { - *cast(void**)block = next_block; + *cast(void**) block = next_block; next_block = block; } @@ -44,9 +44,9 @@ struct BlockAllocator */ void freeMemory() nothrow @nogc { - while(pointers) + while (pointers) { - foreach(i;0..pointers.numberof) + foreach (i; 0 .. pointers.numberof) { Mallocator.alignDispose(pointers.blocks[i]); } @@ -60,11 +60,14 @@ private: void allocBlock() nothrow @nogc { - next_block = cast(void*) Mallocator.alignAlloc( - block_size * blocks_in_allocation, block_size); + next_block = cast(void*) Mallocator.alignAlloc(block_size * blocks_in_allocation, + block_size); + if (next_block is null) + assert(0); - if(pointers is null)pointers = Mallocator.make!BlockPointers; - if(pointers.numberof >= 32) + if (pointers is null) + pointers = Mallocator.make!BlockPointers; + if (pointers.numberof >= 32) { BlockPointers* new_pointers = Mallocator.make!BlockPointers; new_pointers.next_pointers = pointers; @@ -77,8 +80,7 @@ private: void** pointer = cast(void**)(next_block + i * block_size); *pointer = next_block + (i + 1) * block_size; } - void** pointer = cast(void**)( - next_block + (blocks_in_allocation - 1) * block_size); + void** pointer = cast(void**)(next_block + (blocks_in_allocation - 1) * block_size); *pointer = null; } diff --git a/source/ecs/core.d b/source/bubel/ecs/core.d similarity index 74% rename from source/ecs/core.d rename to source/bubel/ecs/core.d index c346c9f..c032d21 100644 --- a/source/ecs/core.d +++ b/source/bubel/ecs/core.d @@ -49,10 +49,10 @@ Struct System1 Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ -module ecs.core; +module bubel.ecs.core; -public import ecs.manager; -public import ecs.entity; +public import bubel.ecs.manager; +public import bubel.ecs.entity; /************************************************************************************************************************ Main struct used as namespace for templates. @@ -74,15 +74,19 @@ static struct ECS mixin template Component() { __gshared ushort component_id = ushort.max; + + ComponentRef ref_() @nogc nothrow return + { + return ComponentRef(&this, component_id); + } } /************************************************************************************************************************ Mark structure as Event. Should be added on top of structure (before any data). */ - mixin template Event() + mixin template Event() { __gshared ushort event_id = ushort.max; - EntityID entity_id; } /************************************************************************************************************************ @@ -92,4 +96,20 @@ static struct ECS { alias ExcludedComponents = T; } -} \ No newline at end of file + + /************************************************************************************************************************ + Make list of readonly ependencies. This template get strings as arguments. Should be added inside System structure. + */ + mixin template ReadOnlyDependencies(T...) + { + alias ReadOnlyDependencies = T; + } + + /************************************************************************************************************************ + Make list of writable ependencies. This template get strings as arguments. Should be added inside System structure. + */ + mixin template WritableDependencies(T...) + { + alias WritableDependencies = T; + } +} diff --git a/source/ecs/entity.d b/source/bubel/ecs/entity.d similarity index 66% rename from source/ecs/entity.d rename to source/bubel/ecs/entity.d index 0bd2dea..6a64d50 100644 --- a/source/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -4,10 +4,10 @@ Entity module. Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ -module ecs.entity; +module bubel.ecs.entity; -import ecs.system; -import ecs.manager; +import bubel.ecs.system; +import bubel.ecs.manager; /************************************************************************************************************************ Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference! @@ -39,11 +39,44 @@ struct Entity if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) return null; - static if (EntityID.sizeof == 8) - uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) >> 3); - else - uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) / EntityID.sizeof()); - return cast(T*)(cast(void*)block + info.deltas[T.component_id] + ind * T.sizeof); + return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof); + } + + bool hasComponent(ushort component_id) + { + EntityManager.EntitiesBlock* block = gEM.getMetaData(&this); + EntityManager.EntityInfo* info = block.type_info; + if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false; + return true; + } + + EntityMeta getMeta() + { + EntityMeta meta; + meta.block = gEM.getMetaData(&this); + meta.index = meta.block.entityIndex(&this); + return meta; + } +} + +struct EntityMeta +{ + EntityManager.EntitiesBlock* block; + ushort index; + + T* getComponent(T)() const + { + const (EntityManager.EntityInfo)* info = block.type_info; + if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) + return null; + return cast(T*)(cast(void*)block + block.type_info.deltas[T.component_id] + index * T.sizeof); + } + + bool hasComponent(ushort component_id) + { + EntityManager.EntityInfo* info = block.type_info; + if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false; + return true; } } @@ -74,3 +107,14 @@ export struct EntityTemplate return cast(T*)(entity_data.ptr + info.tmpl_deltas[T.component_id]); } } + +/************************************************************************************************************************ +ComponentRef contain component ID and pointer to it. It used to add component data to entity. +*/ +export struct ComponentRef +{ + ///pointer to component + void* ptr; + ///component index + ushort component_id; +} diff --git a/source/ecs/events.d b/source/bubel/ecs/events.d similarity index 67% rename from source/ecs/events.d rename to source/bubel/ecs/events.d index ac7e185..64e0c79 100644 --- a/source/ecs/events.d +++ b/source/bubel/ecs/events.d @@ -1,9 +1,9 @@ -module ecs.events; +module bubel.ecs.events; -import ecs.block_allocator; -import ecs.entity; -import ecs.manager; -import ecs.std; +import bubel.ecs.block_allocator; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; import std.algorithm.comparison : max; @@ -20,7 +20,7 @@ package struct EventManager void destroy() nothrow @nogc { - if(event_block_alloc_mutex) + if (event_block_alloc_mutex) { event_block_alloc_mutex.destroy(); Mallocator.dispose(event_block_alloc_mutex); @@ -30,55 +30,60 @@ package struct EventManager export void sendEvent(Ev)(EntityID id, Ev event, uint thread_id = 0) nothrow @nogc { - uint block_id = current_index+thread_id; + uint block_id = current_index + thread_id; EventData* data = &events[Ev.event_id]; EventBlock* block = data.blocks[block_id]; //EntityManager.EventInfo* info = &manager.events[Ev.event_id]; - event.entity_id = id; + //event.entity_id = id; - if(block is null) + if (block is null) { event_block_alloc_mutex.lock(); - scope (exit) - event_block_alloc_mutex.unlock(); - block = cast(EventBlock*) allocator.getBlock(); + event_block_alloc_mutex.unlock(); + *block = EventBlock(); data.first_blocks[block_id] = block; data.blocks[block_id] = block; } - if(block.count >= data.max_events) + if (block.count >= data.max_events) { event_block_alloc_mutex.lock(); - scope (exit) - event_block_alloc_mutex.unlock(); - EventBlock* new_block = cast(EventBlock*) allocator.getBlock(); + event_block_alloc_mutex.unlock(); + *new_block = EventBlock(); block.next = new_block; block = new_block; data.blocks[block_id] = block; } - Ev* event_array = cast(Ev*)(cast(void*)block + data.data_offset); - event_array[block.count] = event; + uint size = Ev.sizeof + EntityID.sizeof; + void* ptr = cast(void*) block + data.data_offset + block.count * size; + *cast(EntityID*)ptr = id; + *cast(Ev*)(ptr + EntityID.sizeof) = event; + //Ev* event_array = cast(Ev*)(cast(void*) block + data.data_offset); + //event_array[block.count] = event; block.count++; } void swapCurrent() nothrow @nogc { - uint threads_count = cast(uint)manager.threads.length; - if(current_index == 0)current_index = threads_count; - else current_index = 0; + uint threads_count = cast(uint) manager.threads.length; + if (current_index == 0) + current_index = threads_count; + else + current_index = 0; - foreach(ref event;events) + foreach (ref event; events) { - foreach(ref first_block; event.first_blocks[current_index .. current_index + threads_count]) + foreach (ref first_block; event.first_blocks[current_index + .. current_index + threads_count]) { EventBlock* block = first_block; - while(block) + while (block) { EventBlock* to_dispose = block; block = block.next; @@ -86,7 +91,7 @@ package struct EventManager } first_block = null; } - foreach(ref block; event.blocks[current_index .. current_index + threads_count]) + foreach (ref block; event.blocks[current_index .. current_index + threads_count]) { block = null; } @@ -96,12 +101,12 @@ package struct EventManager void clearEvents() nothrow @nogc { //uint threads_count = cast(uint)manager.threads.length; - foreach(ref event;events) + foreach (ref event; events) { - foreach(ref first_block; event.first_blocks) + foreach (ref first_block; event.first_blocks) { EventBlock* block = first_block; - while(block) + while (block) { EventBlock* to_dispose = block; block = block.next; @@ -109,7 +114,7 @@ package struct EventManager } first_block = null; } - foreach(ref block; event.blocks) + foreach (ref block; event.blocks) { block = null; } @@ -120,23 +125,25 @@ package struct EventManager { disposeData(); events = Mallocator.makeArray!EventData(manager.events.length); - foreach(i,ref event;events) + foreach (i, ref event; events) { - event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2); - event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2); - event.data_offset = EventBlock.sizeof;//manager.events[i]. + event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2); + event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2); + event.data_offset = EventBlock.sizeof; //manager.events[i]. manager.alignNum(event.data_offset, manager.events[i].alignment); - event.max_events = cast(ushort)((events_block_size - event.data_offset) / manager.events[i].size); + uint size = manager.events[i].size + EntityID.sizeof; + event.max_events = cast(ushort)( + (events_block_size - event.data_offset) / size); } } private void disposeData() nothrow @nogc { clearEvents(); - if(events) + if (events) { - foreach(ref event;events) + foreach (ref event; events) { Mallocator.dispose(event.blocks); Mallocator.dispose(event.first_blocks); @@ -168,7 +175,7 @@ package struct EventManager ushort max_events; EventBlock*[] blocks; EventBlock*[] first_blocks; - + //EventBlock*[] current_blocks; } diff --git a/source/ecs/hash_map.d b/source/bubel/ecs/hash_map.d similarity index 61% rename from source/ecs/hash_map.d rename to source/bubel/ecs/hash_map.d index 5a8b253..764374c 100755 --- a/source/ecs/hash_map.d +++ b/source/bubel/ecs/hash_map.d @@ -1,9 +1,9 @@ -module ecs.hash_map; +module bubel.ecs.hash_map; import std.traits; -import ecs.vector; -import ecs.traits; +import bubel.ecs.vector; +import bubel.ecs.traits; enum doNotInline = "version(DigitalMars)pragma(inline,false);version(LDC)pragma(LDC_never_inline);"; @@ -11,36 +11,55 @@ private enum HASH_EMPTY = 0; private enum HASH_DELETED = 0x1; private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1; -export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc { - static if (isIntegral!(T)) { +export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc +{ + static if (isIntegral!(T)) + { return hashInt(t); - } else { - return 0;//hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts + } + else + { + static if(isArray!T)return hashInt(hash((cast(byte*)t.ptr)[0 .. t.length * ForeachType!(T).sizeof])); + else return hashInt(hash((cast(byte*)&t)[0 .. T.sizeof])); //hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts } } +ulong hash(byte[] array) nothrow @nogc +{ + ulong hash = 0; + + foreach(c;array) + hash = c + (hash << 6) + (hash << 16) - hash; + + return hash; +} + // Can turn bad hash function to good one -export ulong hashInt(ulong x) nothrow @nogc @safe { +export ulong hashInt(ulong x) nothrow @nogc @safe +{ x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb; x = x ^ (x >> 31); return x; } -struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { +struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) +{ alias Key = KeyPar; alias Value = ValuePar; - nothrow: +nothrow: enum rehashFactor = 0.75; enum size_t getIndexEmptyValue = size_t.max; - static struct KeyVal { + static struct KeyVal + { Key key; Value value; } - static struct Bucket { + static struct Bucket + { ulong hash; KeyVal keyValue; } @@ -49,58 +68,74 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { size_t length; // Used to compute loadFactor size_t markerdDeleted; - export void clear() { + export void clear() + { elements.clear(); length = 0; markerdDeleted = 0; } - export void reset() { + export void reset() + { elements.reset(); length = 0; markerdDeleted = 0; } - export bool isIn(ref Key el) { + export bool isIn(ref Key el) + { return getIndex(el) != getIndexEmptyValue; } - export bool isIn(Key el) { + export bool isIn(Key el) + { return getIndex(el) != getIndexEmptyValue; } - export Value* getPtr()(auto ref Key k) { + export Value* getPtr()(auto ref Key k) + { size_t index = getIndex(k); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { return null; - } else { + } + else + { return &elements[index].keyValue.value; } } - export ref Value get()(auto ref Key k) { + export ref Value get()(auto ref Key k) + { size_t index = getIndex(k); assert(index != getIndexEmptyValue); return elements[index].keyValue.value; } deprecated("Use get with second parameter.") export auto ref Value getDefault()( - auto ref Key k, auto ref Value defaultValue) { + auto ref Key k, auto ref Value defaultValue) + { return get(k, defaultValue); } - export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) { + export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) + { size_t index = getIndex(k); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { return defaultValue; - } else { + } + else + { return elements[index].keyValue.value; } } - export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) { + export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) + { size_t index = getIndex(k); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { add(k, defaultValue); } index = getIndex(k); @@ -109,9 +144,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - export bool tryRemove(Key el) { + export bool tryRemove(Key el) + { size_t index = getIndex(el); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { return false; } length--; @@ -120,28 +157,34 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return true; } - export void remove(Key el) { + export void remove(Key el) + { bool ok = tryRemove(el); assert(ok); } - export ref Value opIndex()(auto ref Key key) { + export ref Value opIndex()(auto ref Key key) + { return get(key); } - export void opIndexAssign()(auto ref Value value, auto ref Key key) { + export void opIndexAssign()(auto ref Value value, auto ref Key key) + { add(key, value); } - export void add()(auto ref Key key, auto ref Value value) { + export void add()(auto ref Key key, auto ref Value value) + { size_t index = getIndex(key); - if (index != getIndexEmptyValue) { + if (index != getIndexEmptyValue) + { elements[index].keyValue.value = value; return; } if (getLoadFactor(length + 1) > rehashFactor - || getLoadFactor(length + markerdDeleted) > rehashFactor) { + || getLoadFactor(length + markerdDeleted) > rehashFactor) + { rehash(); } length++; @@ -150,10 +193,13 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { immutable size_t rotateMask = elements.length - 1; index = hash & rotateMask; // Starting point - while (true) { + while (true) + { Bucket* gr = &elements[index]; - if ((gr.hash & HASH_FILLED_MARK) == 0) { - if (gr.hash == HASH_DELETED) { + if ((gr.hash & HASH_FILLED_MARK) == 0) + { + if (gr.hash == HASH_DELETED) + { markerdDeleted--; } gr.hash = hash; @@ -171,15 +217,18 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { //int numA; //int numB; - export size_t getIndex(Key el) { + export size_t getIndex(Key el) + { return getIndex(el); } - export size_t getIndex(ref Key el) { + export size_t getIndex(ref Key el) + { mixin(doNotInline); immutable size_t groupsLength = elements.length; - if (groupsLength == 0) { + if (groupsLength == 0) + { return getIndexEmptyValue; } @@ -188,13 +237,16 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { size_t index = hash & rotateMask; // Starting point //numA++; - while (true) { + while (true) + { //numB++; Bucket* gr = &elements[index]; - if (gr.hash == hash && gr.keyValue.key == el) { + if (gr.hash == hash && gr.keyValue.key == el) + { return index; } - if (gr.hash == HASH_EMPTY) { + if (gr.hash == HASH_EMPTY) + { return getIndexEmptyValue; } @@ -204,21 +256,26 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - export float getLoadFactor(size_t forElementsNum) { - if (elements.length == 0) { + export float getLoadFactor(size_t forElementsNum) + { + if (elements.length == 0) + { return 1; } return cast(float) forElementsNum / (elements.length); } - export void rehash()() { + export void rehash()() + { mixin(doNotInline); // Get all elements Vector!KeyVal allElements; allElements.reserve(elements.length); - foreach (ref Bucket el; elements) { - if ((el.hash & HASH_FILLED_MARK) == 0) { + foreach (ref Bucket el; elements) + { + if ((el.hash & HASH_FILLED_MARK) == 0) + { el.hash = HASH_EMPTY; continue; } @@ -227,12 +284,14 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - if (getLoadFactor(length + 1) > rehashFactor) { // Reallocate + if (getLoadFactor(length + 1) > rehashFactor) + { // Reallocate elements.length = (elements.length ? elements.length : 4) << 1; // Power of two, initially 8 elements } // Insert elements - foreach (i, ref el; allElements) { + foreach (i, ref el; allElements) + { add(el.key, el.value); } length = allElements.length; @@ -240,19 +299,29 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } // foreach support - export int opApply(DG)(scope DG dg) { + export int opApply(DG)(scope DG dg) + { int result; - foreach (ref Bucket gr; elements) { - if ((gr.hash & HASH_FILLED_MARK) == 0) { + foreach (ref Bucket gr; elements) + { + if ((gr.hash & HASH_FILLED_MARK) == 0) + { continue; } - static if (isForeachDelegateWithTypes!(DG, Key)) { + static if (isForeachDelegateWithTypes!(DG, Key)) + { result = dg(gr.keyValue.key); - } else static if (isForeachDelegateWithTypes!(DG, Value)) { + } + else static if (isForeachDelegateWithTypes!(DG, Value)) + { result = dg(gr.keyValue.value); - } else static if (isForeachDelegateWithTypes!(DG, Key, Value)) { + } + else static if (isForeachDelegateWithTypes!(DG, Key, Value)) + { result = dg(gr.keyValue.key, gr.keyValue.value); - } else { + } + else + { static assert(0); } if (result) @@ -263,9 +332,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - export int byKey(scope int delegate(Key k) nothrow dg) { + export int byKey(scope int delegate(Key k) nothrow dg) + { int result; - foreach (ref Key k; this) { + foreach (ref Key k; this) + { result = dg(k); if (result) break; @@ -273,9 +344,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - export int byValue(scope int delegate(ref Value k) nothrow dg) { + export int byValue(scope int delegate(ref Value k) nothrow dg) + { int result; - foreach (ref Value v; this) { + foreach (ref Value v; this) + { result = dg(v); if (result) break; @@ -283,13 +356,15 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) { + export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) + { int result; - foreach (ref Key k, ref Value v; this) { + foreach (ref Key k, ref Value v; this) + { result = dg(k, v); if (result) break; } return result; } -} \ No newline at end of file +} diff --git a/source/ecs/id_manager.d b/source/bubel/ecs/id_manager.d similarity index 75% rename from source/ecs/id_manager.d rename to source/bubel/ecs/id_manager.d index 1ab8465..aaeef30 100644 --- a/source/ecs/id_manager.d +++ b/source/bubel/ecs/id_manager.d @@ -1,10 +1,10 @@ -module ecs.id_manager; +module bubel.ecs.id_manager; -import ecs.entity; -import ecs.std; -import ecs.vector; +import bubel.ecs.entity; +import bubel.ecs.std; +import bubel.ecs.vector; -import ecs.atomic; +import bubel.ecs.atomic; import core.stdc.string : memcpy; /************************************************************************************************************************ @@ -17,12 +17,8 @@ struct IDManager */ pragma(inline, false) EntityID getNewID() nothrow @nogc { - //uint current = m_next_id; - //uint next;// = m_ids_array[m_next_id].next_id; -//begin: - //if (current == uint.max)//> m_last_id) int current = m_stack_top.atomicOp!"-="(1) + 1; - if(current < 0) + if (current < 0) { uint add_id = m_last_id.atomicOp!"+="(1) - 1; @@ -33,7 +29,7 @@ struct IDManager if (block_id >= m_blocks_count) { add_mutex.lock(); - if(block_id >= m_blocks_count) + if (block_id >= m_blocks_count) { m_blocks[m_blocks_count].alloc(); m_blocks_count++; @@ -48,29 +44,11 @@ struct IDManager return id; } - - //current += 1; uint index = m_free_stack[current]; EntityID id; id.id = index; id.counter = m_ids_array[index].counter; - //current = m_ids_array[index].next_id; return id; - - /*next = m_ids_array[current].next_id; - if(cas(&m_next_id,current,next)) - { - EntityID id; - id.id = current; - id.counter = m_ids_array[current].counter; - m_next_id = m_ids_array[current].next_id; - return id; - } - else - { - current = next; - goto begin; - }*/ } /************************************************************************************************************************ @@ -78,13 +56,12 @@ struct IDManager */ void releaseID(EntityID id) nothrow @nogc { + optimize(); Data* data = &m_ids_array[id.id]; if (data.counter != id.counter) return; data.counter++; - //data.next_id = m_next_id; data.entity = null; - ///m_next_id = id.id; m_stack_top.atomicOp!"+="(1); m_free_stack[m_stack_top] = id.id; @@ -136,9 +113,11 @@ struct IDManager */ export bool isExist(EntityID id) nothrow @nogc { - if(id.id >= m_ids_array.length)return false; + if (id.id >= m_ids_array.length) + return false; Data* data = &m_ids_array[id.id]; - if(data.entity is null)return false; + if (data.entity is null) + return false; return data.counter == id.counter; } @@ -150,7 +129,8 @@ struct IDManager m_ids_array = Mallocator.makeArray!Data(65536); m_free_stack = Mallocator.makeArray!uint(65536); m_blocks = Mallocator.makeArray!Block(64); - foreach(ref block;m_blocks)block = Block(); + foreach (ref block; m_blocks) + block = Block(); m_blocks_count = 1; m_blocks[0].alloc(); @@ -166,20 +146,23 @@ struct IDManager */ void deinitialize() @trusted @nogc nothrow { - if(m_ids_array)Mallocator.dispose(m_ids_array); - if(m_free_stack)Mallocator.dispose(m_free_stack); - if(m_blocks) + if (m_ids_array) + Mallocator.dispose(m_ids_array); + if (m_free_stack) + Mallocator.dispose(m_free_stack); + if (m_blocks) { - foreach(ref block;m_blocks) + foreach (ref block; m_blocks) { - if(block.data)block.free(); + if (block.data) + block.free(); } Mallocator.dispose(m_blocks); } - if(add_mutex) + if (add_mutex) { add_mutex.destroy(); - Mallocator.dispose(add_mutex);//cast(void*)add_mutex); //workaround for compiler bug + Mallocator.dispose(add_mutex); //cast(void*)add_mutex); //workaround for compiler bug add_mutex = null; } } @@ -189,27 +172,31 @@ struct IDManager */ void optimize() nothrow @nogc { - if(m_stack_top < -1)m_stack_top = -1; - if(m_last_id > m_ids_array.length) + if (m_stack_top < -1) + m_stack_top = -1; + if (m_last_id > m_ids_array.length) { - uint begin = cast(uint)m_ids_array.length; + uint begin = cast(uint) m_ids_array.length; Data[] new_array = Mallocator.makeArray!Data(begin + (m_blocks_count << 16)); memcpy(new_array.ptr, m_ids_array.ptr, m_ids_array.length * Data.sizeof); Mallocator.dispose(m_ids_array); m_ids_array = new_array; uint[] new_stack = Mallocator.makeArray!uint(m_ids_array.length); - memcpy(new_stack.ptr,m_free_stack.ptr,m_free_stack.length * uint.sizeof); + memcpy(new_stack.ptr, m_free_stack.ptr, m_free_stack.length * uint.sizeof); Mallocator.dispose(m_free_stack); m_free_stack = new_stack; - foreach(block;m_blocks[0..m_blocks_count-1]) + foreach (block; m_blocks[0 .. m_blocks_count - 1]) { - memcpy(cast(void*)m_ids_array.ptr + begin * Data.sizeof, block.data.ptr, 65536 * Data.sizeof); + memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof, + block.data.ptr, 65536 * Data.sizeof); begin += 65536; } - memcpy(cast(void*)m_ids_array.ptr + begin * Data.sizeof, m_blocks[m_blocks_count-1].data.ptr, (m_last_id - begin) * Data.sizeof); - foreach(ref block;m_blocks[1..m_blocks_count])block.free(); + memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof, + m_blocks[m_blocks_count - 1].data.ptr, (m_last_id - begin) * Data.sizeof); + foreach (ref block; m_blocks[1 .. m_blocks_count]) + block.free(); m_blocks_count = 1; } } @@ -241,15 +228,12 @@ struct IDManager private: Mutex* add_mutex; - //shared uint m_next_id = 0; - - //shared uint m_last_id = 0; + Data[] m_ids_array = null; uint m_blocks_count = 0; Block[] m_blocks; - //shared int m_stack_top = -1; uint[] m_free_stack = null; - + align(64) shared uint m_last_id = 0; align(64) shared int m_stack_top = -1; } diff --git a/source/ecs/manager.d b/source/bubel/ecs/manager.d similarity index 79% rename from source/ecs/manager.d rename to source/bubel/ecs/manager.d index aca7f53..0982531 100644 --- a/source/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -4,7 +4,7 @@ Most important module. Almost every function is called from EntityManager. Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ -module ecs.manager; +module bubel.ecs.manager; import std.algorithm : max; import std.conv : to; @@ -14,21 +14,21 @@ import std.traits; //import core.stdc.stdlib : qsort; //import core.stdc.string; -import ecs.system; //not ordered as forward reference bug workaround -import ecs.block_allocator; -import ecs.entity; -import ecs.events; -import ecs.hash_map; -import ecs.id_manager; -import ecs.simple_vector; -import ecs.std; -import ecs.traits; -import ecs.vector; -import ecs.atomic; +import bubel.ecs.system; //not ordered as forward reference bug workaround +import bubel.ecs.block_allocator; +import bubel.ecs.entity; +import bubel.ecs.events; +import bubel.ecs.hash_map; +import bubel.ecs.id_manager; +import bubel.ecs.simple_vector; +import bubel.ecs.std; +import bubel.ecs.traits; +import bubel.ecs.vector; +import bubel.ecs.atomic; export alias gEM = EntityManager.instance; export alias gEntityManager = EntityManager.instance; -alias SerializeVector = ecs.vector.Vector!ubyte; +alias SerializeVector = bubel.ecs.vector.Vector!ubyte; /************************************************************************************************************************ Entity manager is responsible for everything. @@ -104,8 +104,8 @@ export struct EntityManager Mallocator.dispose(system.jobs); if (system.m_read_only_components) Mallocator.dispose(system.m_read_only_components); - if (system.m_modified_components) - Mallocator.dispose(system.m_modified_components); + if (system.m_writable_components) + Mallocator.dispose(system.m_writable_components); if (system.m_components) Mallocator.dispose(system.m_components); if (system.m_excluded_components) @@ -126,7 +126,7 @@ export struct EntityManager //if(info.components)Mallocator.dispose(info.components); Mallocator.dispose(info); - } //*/ + } foreach (UpdatePass* pass; passes) { @@ -402,7 +402,7 @@ export struct EntityManager Sys* data_system = cast(Sys*) data.system_pointer; Type* event = cast(Type*) data.event; - data_system.handleEvent(gEM.getEntity(event.entity_id), *event); + data_system.handleEvent(data.entity, *event); } void setEventCallers(Sys)(ref System system) @@ -452,6 +452,8 @@ export struct EntityManager uint excluded = 1; uint optional = 1; uint req = 1; + uint readonly_dep = 1; + uint writable_dep = 1; } static ComponentsCounts getComponentsCounts()() @@ -467,53 +469,55 @@ export struct EntityManager foreach (member; __traits(allMembers, Sys.EntitiesData)) { alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); - if (member == "length" || member == "thread_id" + if (member == "length" || member == "thread_id" || member == "job_id" || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) { - continue; + //continue; } - - string name; - static if (isArray!MemberType) - { // Workaround. This code is never called with: not an array type, but compiler prints an error - name = Unqual!(ForeachType!MemberType).stringof; - } - - bool is_optional; - bool is_read_only; - - if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) + else { - is_read_only = true; - } - - foreach (att; __traits(getAttributes, __traits(getMember, - Sys.EntitiesData, member))) - { - if (att == "optional") - { - is_optional = true; + string name; + static if (isArray!MemberType) + { // Workaround. This code is never called with: not an array type, but compiler prints an error + name = Unqual!(ForeachType!MemberType).stringof; } - if (att == "readonly") + + bool is_optional; + bool is_read_only; + + if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) { is_read_only = true; } - } - if (is_read_only) - { - components_counts.readonly++; - } - else - { - components_counts.mutable++; - } - if (is_optional) - { - components_counts.optional++; - } - else - { - components_counts.req++; + + foreach (att; __traits(getAttributes, __traits(getMember, + Sys.EntitiesData, member))) + { + if (att == "optional") + { + is_optional = true; + } + if (att == "readonly") + { + is_read_only = true; + } + } + if (is_read_only) + { + components_counts.readonly++; + } + else + { + components_counts.mutable++; + } + if (is_optional) + { + components_counts.optional++; + } + else + { + components_counts.req++; + } } } @@ -532,7 +536,22 @@ export struct EntityManager { components_counts.excluded++; } + } + } + static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) + { + foreach (str; Sys.ReadOnlyDependencies) + { + components_counts.readonly_dep++; + } + } + + static if (__traits(hasMember, Sys, "WritableDependencies")) + { + foreach (str; Sys.WritableDependencies) + { + components_counts.writable_dep++; } } @@ -568,6 +587,16 @@ export struct EntityManager return m_req[0 .. m_req_counter]; } + CompInfo[] readonlyDeps() + { + return m_readonly_dep[0 .. m_readonly_dep_counter]; + } + + CompInfo[] writableDeps() + { + return m_writable_dep[0 .. m_writable_dep_counter]; + } + void addReadonly(CompInfo info) { m_readonly[m_readonly_counter++] = info; @@ -593,17 +622,31 @@ export struct EntityManager m_req[m_req_counter++] = info; } + void addReadonlyDep(CompInfo info) + { + m_readonly_dep[m_readonly_dep_counter++] = info; + } + + void addWritableDep(CompInfo info) + { + m_writable_dep[m_writable_dep_counter++] = info; + } + CompInfo[counts.readonly] m_readonly; CompInfo[counts.mutable] m_mutable; CompInfo[counts.excluded] m_excluded; CompInfo[counts.optional] m_optional; CompInfo[counts.req] m_req; + CompInfo[counts.readonly_dep] m_readonly_dep; + CompInfo[counts.writable_dep] m_writable_dep; uint m_readonly_counter; uint m_mutable_counter; uint m_excluded_counter; uint m_optional_counter; uint m_req_counter; + uint m_readonly_dep_counter; + uint m_writable_dep_counter; string entites_array; } @@ -615,7 +658,9 @@ export struct EntityManager size_t opt = components_info.optional.length; size_t excluded = components_info.excluded.length; size_t read_only = components_info.readonly.length; - size_t modified = components_info.mutable.length; + size_t writable = components_info.mutable.length; + size_t read_only_deps = components_info.readonlyDeps.length; + size_t writable_deps = components_info.writableDeps.length; if (req > 0) system.m_components = Mallocator.makeArray!ushort(req); @@ -625,8 +670,12 @@ export struct EntityManager system.m_excluded_components = Mallocator.makeArray!ushort(excluded); if (read_only > 0) system.m_read_only_components = Mallocator.makeArray!ushort(read_only); - if (modified > 0) - system.m_modified_components = Mallocator.makeArray!ushort(modified); + if (writable > 0) + system.m_writable_components = Mallocator.makeArray!ushort(writable); + if (read_only_deps > 0) + system.m_readonly_dependencies = Mallocator.makeArray!ushort(read_only_deps); + if (writable_deps > 0) + system.m_writable_dependencies = Mallocator.makeArray!ushort(writable_deps); } @@ -643,55 +692,57 @@ export struct EntityManager foreach (member; __traits(allMembers, Sys.EntitiesData)) { alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); - if (member == "length" || member == "thread_id" + if (member == "length" || member == "thread_id" || member == "job_id" || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) { if (is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) components_info.entites_array = member; - continue; + //continue; } - - string name; - static if (isArray!MemberType) - { // Workaround. This code is never called with: not an array type, but compiler prints an error - name = Unqual!(ForeachType!MemberType).stringof; - } - - bool is_optional; - bool is_read_only; - - if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) + else { - is_read_only = true; - } - - foreach (att; __traits(getAttributes, __traits(getMember, - Sys.EntitiesData, member))) - { - if (att == "optional") - { - is_optional = true; + string name; + static if (isArray!MemberType) + { // Workaround. This code is never called with: not an array type, but compiler prints an error + name = Unqual!(ForeachType!MemberType).stringof; } - if (att == "readonly") + + bool is_optional; + bool is_read_only; + + if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) { is_read_only = true; } - } - if (is_read_only) - { - components_info.addReadonly(CompInfo(member, name)); - } - else - { - components_info.addMutable(CompInfo(member, name)); - } - if (is_optional) - { - components_info.addOptional(CompInfo(member, name)); - } - else - { - components_info.addReq(CompInfo(member, name)); + + foreach (att; __traits(getAttributes, __traits(getMember, + Sys.EntitiesData, member))) + { + if (att == "optional") + { + is_optional = true; + } + if (att == "readonly") + { + is_read_only = true; + } + } + if (is_read_only) + { + components_info.addReadonly(CompInfo(member, name)); + } + else + { + components_info.addMutable(CompInfo(member, name)); + } + if (is_optional) + { + components_info.addOptional(CompInfo(member, name)); + } + else + { + components_info.addReq(CompInfo(member, name)); + } } } @@ -714,6 +765,22 @@ export struct EntityManager } } + static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) + { + foreach (str; Sys.ReadOnlyDependencies) + { + components_info.addReadonlyDep(CompInfo(str, str)); + } + } + + static if (__traits(hasMember, Sys, "WritableDependencies")) + { + foreach (str; Sys.WritableDependencies) + { + components_info.addWritableDep(CompInfo(str, str)); + } + } + return components_info; } @@ -742,6 +809,13 @@ export struct EntityManager static assert(MemberType.sizeof > 1, "EntitiesData 'thread_id' member can't be byte or ubyte."); } + else static if (member == "job_id") + { + static assert(isIntegral!(MemberType), + "EntitiesData 'job_id' member must be integral type."); + static assert(MemberType.sizeof > 1, + "EntitiesData 'job_id' member can't be byte or ubyte."); + } else static if (!(isArray!(MemberType))) static assert(0, "EntitiesData members should be arrays of elements!"); } @@ -807,12 +881,11 @@ export struct EntityManager else assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); - system.m_modified_components[iii] = comp; + system.m_writable_components[iii] = comp; } - } - static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info, + static void fillInputData()(ref Sys.EntitiesData input_data, EntityInfo* info, EntitiesBlock* block, uint offset, uint entities_count, System* system) { //enum ComponentsIndices components_info = getComponentsInfo(); @@ -943,6 +1016,11 @@ export struct EntityManager .thread_id; } + static if (hasMember!(Sys.EntitiesData, "job_id")) + { + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; + } + //s.onUpdate(input_data); (cast(typeof(&__traits(getOverloads, s, "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)(input_data); @@ -971,6 +1049,11 @@ export struct EntityManager input_data.thread_id = cast(typeof(input_data.thread_id)) data.thread_id; } + static if (hasMember!(Sys.EntitiesData, "job_id")) + { + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; + } + (cast(typeof(&__traits(getOverloads, s, "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)(input_data); } @@ -1059,20 +1142,54 @@ export struct EntityManager genCompList(system, components_map); + foreach (iii, comp_info; components_info.readonlyDeps) + { + ushort comp = external_dependencies_map.get(cast(const(char)[]) comp_info.type, + ushort.max); + version (D_BetterC) + assert(comp != ushort.max, + "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency."); + else + assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); + system.m_readonly_dependencies[iii] = comp; + } + + foreach (iii, comp_info; components_info.writableDeps) + { + ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); + version (D_BetterC) + assert(comp != ushort.max, + "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency."); + else + assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); + system.m_writable_dependencies[iii] = comp; + } + ushort sys_id = systems_map.get(cast(char[]) Sys.stringof, ushort.max); if (sys_id < systems.length) { - system.enable(); + systems[sys_id].disable(); + if (systems[sys_id].m_destroy) + (cast(void function(void*)) systems[sys_id].m_destroy)( + systems[sys_id].m_system_pointer); if (system.m_create) (cast(void function(void*)) system.m_create)(system.m_system_pointer); + system.enable(); + system.m_id = sys_id; + system.m_name = systems[sys_id].m_name; systems[sys_id] = system; } else { system.m_name = Mallocator.makeArray(cast(char[]) Sys.stringof); + systems_map.add(system.m_name, cast(ushort) systems.length); system.m_id = cast(ushort)(systems.length); @@ -1118,6 +1235,11 @@ export struct EntityManager return cast(ushort)(passes.length - 1); } + export void registerDependency(const(char)[] name) + { + return external_dependencies_map.add(name, cast(ushort) external_dependencies_map.length); + } + /************************************************************************************************************************ Register component into EntityManager. */ @@ -1153,7 +1275,10 @@ export struct EntityManager info.create_callback = &callCreate; } - info.size = Comp.sizeof; + static if (Comp.sizeof == 1 && Fields!(Comp).length == 0) + info.size = 0; + else + info.size = Comp.sizeof; info.alignment = Comp.alignof; //8; info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof); *cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); @@ -1219,7 +1344,9 @@ export struct EntityManager "Can't call function with system which hasn't EntitesData structure."); static assert(__traits(hasMember, Sys, "onUpdate"), "Can't call function with system which hasn't onUpdate function callback."); - static assert(is(SetFunctionAttributes!(T,functionLinkage!(s.onUpdate), functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), "Function must match system update function."); + static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate), + functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), + "Function must match system update function."); static assert(__traits(hasMember, Sys, "system_id"), "Sys must be system type."); System* system = getSystem(Sys.system_id); @@ -1253,7 +1380,7 @@ export struct EntityManager foreach (caller; passes[pass].system_callers) { System* sys = &systems[caller.system_id]; - if (sys.enabled && sys.execute) + if (sys.enabled && sys.willExecute) { if (sys.m_empty) { @@ -1293,7 +1420,7 @@ export struct EntityManager foreach (caller; passes[pass].system_callers) { System* sys = &systems[caller.system_id]; - if (sys.enabled && sys.execute) + if (sys.enabled && sys.willExecute) { uint job_id = 0; void nextJob() @@ -1304,6 +1431,7 @@ export struct EntityManager memcpy(callers.ptr, &tmp_datas[0], CallData.sizeof * tmp_datas.length); tmp_datas.clear(); sys.jobs[job_id].callers = callers; + sys.jobs[job_id].id = job_id; job_id++; } @@ -1490,6 +1618,8 @@ export struct EntityManager //fill components with default data foreach (comp; info.components) { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], components[comp].init_data.ptr, components[comp].size); } @@ -1499,6 +1629,8 @@ export struct EntityManager ushort index = block.entityIndex(entity); foreach (comp; info.components) { + if (components[comp].size == 0) + continue; memcpy(cast(void*) temp.entity_data.ptr + info.tmpl_deltas[comp], cast(void*) block + info.deltas[comp] + components[comp].size * index, components[comp].size); @@ -1547,6 +1679,8 @@ export struct EntityManager //fill components with default data foreach (comp; info.components) { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], components[comp].init_data.ptr, components[comp].size); } @@ -1614,14 +1748,19 @@ export struct EntityManager //fill components with default data and copy from base template foreach (comp; info.components) { - if (comp < base_tmpl.info.deltas.length && base_tmpl.info.deltas[comp] != ushort.max) //copy data from base component - { + if (comp < base_tmpl.info.tmpl_deltas.length + && base_tmpl.info.tmpl_deltas[comp] != ushort.max) //copy data from base component + { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp], components[comp].size); } else //fill with default data { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], components[comp].init_data.ptr, components[comp].size); } @@ -1630,6 +1769,20 @@ export struct EntityManager return temp; } + /************************************************************************************************************************ + Allocate EntityTemplate copy. + + Params: + copy_tmpl = template which should be copied + */ + export EntityTemplate* allocateTemplate(EntityTemplate* copy_tmpl) + { + EntityTemplate* tmpl = Mallocator.make!EntityTemplate; + tmpl.info = copy_tmpl.info; + tmpl.entity_data = Mallocator.makeArray(copy_tmpl.entity_data); + return tmpl; + } + /************************************************************************************************************************ Returns entity type info. @@ -1697,8 +1850,8 @@ export struct EntityManager } info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length); - info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length, - info); + //info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length); + info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); foreach (comp; info.components) { @@ -1758,11 +1911,11 @@ export struct EntityManager } add_len++; //move elements after new listener - if(add_len < tmp_add.length) - for (int k = add_len; k > j; k--) - { - tmp_add[k] = tmp_add[k - 1]; - } + if (add_len < tmp_add.length) + for (int k = add_len; k > j; k--) + { + tmp_add[k] = tmp_add[k - 1]; + } //assign listener tmp_add[j] = cast(ushort) i; } @@ -1778,11 +1931,11 @@ export struct EntityManager } rem_len++; //move elements after new listener - if(rem_len < tmp_add.length) - for (int k = rem_len; k > j; k--) - { - tmp_rem[k] = tmp_rem[k - 1]; - } + if (rem_len < tmp_add.length) + for (int k = rem_len; k > j; k--) + { + tmp_rem[k] = tmp_rem[k - 1]; + } //assign listener tmp_rem[j] = cast(ushort) i; } @@ -1798,11 +1951,11 @@ export struct EntityManager } ch_len++; //move elements after new listener - if(ch_len < tmp_add.length) - for (int k = ch_len; k > j; k--) - { - tmp_ch[k] = tmp_ch[k - 1]; - } + if (ch_len < tmp_add.length) + for (int k = ch_len; k > j; k--) + { + tmp_ch[k] = tmp_ch[k - 1]; + } //assign listener tmp_ch[j] = cast(ushort) i; } @@ -1958,10 +2111,10 @@ export struct EntityManager { ThreadData* data = &threads[threadID]; uint num = cast(uint) del_ids.length; - data.change_entities_list.add(0); - data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); - data.change_entities_list.add((cast(ubyte*)&num)[0 .. uint.sizeof]); - data.change_entities_list.add((cast(ubyte*) del_ids.ptr)[0 .. num * 2]); + data.changeEntitiesList.add(0); + data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); + data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); + data.changeEntitiesList.add((cast(ubyte*) del_ids.ptr)[0 .. num * 2]); } private void __removeComponents(EntityID entity_id, ushort[] del_ids) @@ -2001,6 +2154,8 @@ export struct EntityManager return;*/ EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); + updateEntityInfoBlocks(new_info); + assert(new_block.added_count == 0); void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; @@ -2024,10 +2179,16 @@ export struct EntityManager foreach (comp; new_info.components) { uint comp_size = components[comp].size; + if (comp_size == 0) + continue; memcpy(cast(void*) new_block + new_info.deltas[comp] + new_block.entities_count * comp_size, cast(void*) block + info.deltas[comp] + ind * comp_size, comp_size); } + new_block.entities_count++; + if (new_block != new_info.update_block) + new_info.update_block = new_block; + if (new_info.add_listeners) { foreach (listener; new_info.add_listeners) @@ -2035,7 +2196,7 @@ export struct EntityManager if (!info.systems[listener]) { callAddEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count, new_block.entities_count + 1); + new_block.entities_count - 1, new_block.entities_count); } } } @@ -2047,13 +2208,11 @@ export struct EntityManager if (info.systems[listener]) { callChangeEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count, new_block.entities_count + 1, del_ids); + new_block.entities_count - 1, new_block.entities_count, del_ids); } } } - new_block.entities_count++; - removeEntityNoID(entity, block); } @@ -2136,12 +2295,14 @@ export struct EntityManager //EntityInfo* new_info = getEntityInfo(ids[0 .. len]); EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); + updateEntityInfoBlocks(new_info); + assert(new_block.added_count == 0); void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; Entity* new_entity = cast(Entity*) start; new_entity.id = entity.id; - id_manager.update(*new_entity); //new_entity.updateID(); + id_manager.update(*new_entity); uint j = 0; uint k = 0; @@ -2160,28 +2321,37 @@ export struct EntityManager foreach (id; new_info.components) //ids[0 .. len]) { - void* dst = cast(void*) new_block + new_info.deltas[id] + ( - new_block.entities_count) * components[id].size; uint size = components[id].size; + void* dst = void; + if (size != 0) + dst = cast(void*) new_block + new_info.deltas[id] + (new_block.entities_count) + * size; if (k >= new_ids.length) { - memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); + if (size != 0) + memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); j++; } else if (j >= info.components.length || id == new_ids[k]) { - memcpy(dst, data_pointers[k], size); + if (size != 0) + memcpy(dst, data_pointers[k], size); k++; } else { assert(id != new_ids[0]); - memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); + if (size != 0) + memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); j++; } } + new_block.entities_count++; + if (new_block != new_info.update_block) + new_info.update_block = new_block; + if (new_info.add_listeners) { foreach (listener; new_info.add_listeners) @@ -2189,7 +2359,7 @@ export struct EntityManager if (!info.systems[listener]) { callAddEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count, new_block.entities_count + 1); + new_block.entities_count - 1, new_block.entities_count); } } } @@ -2201,12 +2371,11 @@ export struct EntityManager if (info.systems[listener]) { callChangeEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count, new_block.entities_count + 1, new_ids); + new_block.entities_count - 1, new_block.entities_count, new_ids); } } } - new_block.entities_count++; removeEntityNoID(entity, block); } @@ -2220,12 +2389,7 @@ export struct EntityManager void addComponents(Components...)(const EntityID entity_id, Components comps) nothrow @nogc { const uint num = Components.length; - //Entity* entity = id_manager.getEntityPointer(entity_id); - //EntitiesBlock* block = getMetaData(entity); - //EntityInfo* info = block.type_info; - /*ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * (info.components.length + num)))[0 - .. info.components.length + num];*/ - ushort[num] new_ids; + /*ushort[num] new_ids; static foreach (i, comp; Components) { @@ -2233,16 +2397,48 @@ export struct EntityManager } ThreadData* data = &threads[threadID]; - data.change_entities_list.add(cast(ubyte) 1u); - data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); - data.change_entities_list.add((cast(ubyte*)&num)[0 .. uint.sizeof]); - data.change_entities_list.add(cast(ubyte[]) new_ids); + data.changeEntitiesList.add(cast(ubyte) 1u); + data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); + data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); + data.changeEntitiesList.add(cast(ubyte[]) new_ids); static foreach (i, comp; comps) { - data.change_entities_list.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); - } + data.changeEntitiesList.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); + }*/ //__addComponents(entity_id, new_ids, pointers); + + ComponentRef[num] _comps; + static foreach (i, comp; comps) + { + _comps[i] = comp.ref_; + } + addComponents(entity_id, _comps); + + } + + export void addComponents(const EntityID entity_id, ComponentRef[] comps) nothrow @nogc + { + uint num = cast(uint) comps.length; + ThreadData* data = &threads[threadID]; + data.changeEntitiesList.add(cast(ubyte) 1u); + data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); + data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); + foreach (ref_; comps) + { + data.changeEntitiesList.add((cast(ubyte*)&ref_.component_id)[0 .. ushort.sizeof]); + } + foreach (ref_; comps) + { + if (components[ref_.component_id].size != 0) + data.changeEntitiesList.add( + (cast(ubyte*) ref_.ptr)[0 .. components[ref_.component_id].size]); + } + /*data.changeEntitiesList.add(cast(ubyte[]) new_ids); + static foreach (i, comp; comps) + { + data.changeEntitiesList.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); + }*/ } /************************************************************************************************************************ @@ -2251,7 +2447,7 @@ export struct EntityManager Params: template_ = pointer entity template allocated by EntityManager. */ - export void freeTemplate(EntityTemplate* template_) + export void freeTemplate(EntityTemplate* template_) @nogc nothrow { Mallocator.dispose(template_.entity_data); Mallocator.dispose(template_); @@ -2288,25 +2484,24 @@ export struct EntityManager foreach (i, comp; info.components) { - memcpy(cast(void*) new_block + info.deltas[comp] + components[comp].size * new_id, - cast(void*) block + info.deltas[comp] + components[comp].size * index, - components[comp].size); + ushort size = components[comp].size; + if (size != 0) + memcpy(cast(void*) new_block + info.deltas[comp] + size * new_id, + cast(void*) block + info.deltas[comp] + size * index, size); if (components[comp].create_callback) { - components[comp].create_callback(cast( - void*) block + info.deltas[comp] + new_id * components[comp].size); + components[comp].create_callback( + cast(void*) block + info.deltas[comp] + new_id * size); } } - if (new_index == 1) - threads[threadID].blocks_to_update.add(new_block); + if (new_index == 1 && info.update_block == block) + threads[threadID].infosToUpdate.add(info); Entity* new_entity = cast(Entity*) start; - //add_mutex.lock_nothrow(); new_entity.id = id_manager.getNewID(); - //add_mutex.unlock_nothrow(); - id_manager.update(*new_entity); //new_entity.updateID(); + id_manager.update(*new_entity); return new_entity; } @@ -2319,6 +2514,56 @@ export struct EntityManager tmpl = pointer entity template allocated by EntityManager. */ export Entity* addEntity(EntityTemplate* tmpl) + { + /*EntityInfo* info = tmpl.info; + + ushort index = 0; + EntitiesBlock* block; + do + { + block = findBlockWithFreeSpaceMT(info); + index = block.added_count.atomicOp!"+="(1); + } + while (block.entities_count + index > info.max_entities); + + uint id = (block.entities_count + index - 1); //block.added_count); + + void* data_begin = block.dataBegin(); + void* start = data_begin + EntityID.sizeof * id; + + foreach (comp; info.components) + { + memcpy(cast(void*) block + info.deltas[comp] + components[comp].size * id, + tmpl.entity_data.ptr + info.tmpl_deltas[comp], components[comp].size); + + if (components[comp].create_callback) + { + components[comp].create_callback( + cast(void*) block + info.deltas[comp] + id * components[comp].size); + } + + } + + if (index == 1) + threads[threadID].infosToUpdate.add(block); + + Entity* entity = cast(Entity*) start; + entity.id = id_manager.getNewID(); + id_manager.update(*entity); //entity.updateID(); + + return entity;*/ + return addEntity(tmpl, null); + } + + /************************************************************************************************************************ + Add entity to system. Returen pointer is valid only before one from commit(), begin() or end() will be called. To save entity to further + use you should save ID instead of pointer. + + Params: + tmpl = pointer entity template allocated by EntityManager. + replacement = list of components references to used. Memory form list replace data from template inside new entity. Should be used only for data which vary between most entities (like 3D position etc.) + */ + export Entity* addEntity(EntityTemplate* tmpl, ComponentRef[] replacement) { EntityInfo* info = tmpl.info; @@ -2336,27 +2581,43 @@ export struct EntityManager void* data_begin = block.dataBegin(); void* start = data_begin + EntityID.sizeof * id; + foreach (comp; info.components) + { + uint size = components[comp].size; + if (size != 0) + memcpy(cast(void*) block + info.deltas[comp] + size * id, + tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); + } + + foreach (comp; replacement) + { + if (comp.component_id < info.deltas.length) + { + ushort delta = info.deltas[comp.component_id]; + if (delta != 0) + { + uint size = components[comp.component_id].size; + if (size != 0) + memcpy(cast(void*) block + delta + size * id, comp.ptr, size); + } + } + } + foreach (i, comp; info.components) { - memcpy(cast(void*) block + info.deltas[comp] + components[comp].size * id, - tmpl.entity_data.ptr + info.tmpl_deltas[comp], components[comp].size); - if (components[comp].create_callback) { components[comp].create_callback( cast(void*) block + info.deltas[comp] + id * components[comp].size); } - } - if (index == 1) - threads[threadID].blocks_to_update.add(block); + if (index == 1 && info.update_block == block) + threads[threadID].infosToUpdate.add(info); Entity* entity = cast(Entity*) start; - //add_mutex.lock_nothrow(); entity.id = id_manager.getNewID(); - //add_mutex.unlock_nothrow(); - id_manager.update(*entity); //entity.updateID(); + id_manager.update(*entity); return entity; } @@ -2375,6 +2636,7 @@ export struct EntityManager block.id = 0; info.first_block = block; info.last_block = block; + info.update_block = block; } else if (block.entities_count >= info.max_entities) { @@ -2385,6 +2647,12 @@ export struct EntityManager new_block.id = cast(ushort)(block.id + 1); block = new_block; info.last_block = block; + ///make sure that update_block point to unfilled block + if (info.update_block.entities_count == info.max_entities) + { + assert(!info.update_block.added_count); + info.update_block = block; + } } return block; } @@ -2410,8 +2678,9 @@ export struct EntityManager block.id = 0; info.first_block = block; info.last_block = block; + info.update_block = block; } - else if (block.entities_count + block.added_count > info.max_entities) + else if (block.entities_count + block.added_count >= info.max_entities) { EntitiesBlock* last_block = info.last_block; @@ -2429,6 +2698,12 @@ export struct EntityManager new_block.id = cast(ushort)(block.id + 1); block = new_block; info.last_block = block; + ///make sure that update_block point to unfilled block + if (info.update_block.entities_count == info.max_entities) + { + assert(!info.update_block.added_count); + info.update_block = block; + } } return block; } @@ -2441,15 +2716,17 @@ export struct EntityManager */ export void removeEntity(EntityID id) { - threads[threadID].entities_to_remove.add(id); + threads[threadID].entitesToRemove.add(id); } private void __removeEntity(EntityID id) nothrow @nogc { //get entity and block meta data pointers Entity* entity = id_manager.getEntityPointer(id); + if (entity is null) return; //return if entity doesn't exist + EntitiesBlock* block = getMetaData(entity); EntityInfo* info = block.type_info; @@ -2470,6 +2747,11 @@ export struct EntityManager { EntityInfo* info = block.type_info; + updateEntityInfoBlocks(info); + + assert(info.last_block.added_count == 0); + assert(info.last_block.entities_count > 0); + info.last_block.entities_count--; uint pos = block.entityIndex(entity); @@ -2490,21 +2772,24 @@ export struct EntityManager { foreach (comp; info.components) { + uint size = components[comp].size; + if (size == 0) + continue; void* src = cast(void*) info.last_block + info.deltas[comp]; void* dst = cast(void*) block + info.deltas[comp]; - uint size = components[comp].size; memcpy(dst + pos * size, src + info.last_block.entities_count * size, size); } block = info.last_block; entity.id = *cast(EntityID*)(block.dataBegin() + block.entities_count * EntityID.sizeof); - id_manager.update(*entity); //entity.updateID(); + id_manager.update(*entity); } block = info.last_block; if (block.entities_count == 0) { + assert(info.update_block is block); info.last_block = block.prev_block; if (info.first_block is block) { @@ -2513,7 +2798,9 @@ export struct EntityManager if (block.prev_block) { block.prev_block.next_block = null; - block.prev_block.added_count = 0; + info.update_block = block.prev_block; + assert(block.prev_block.added_count == 0); + //block.prev_block.added_count.atomicStore(cast(ushort)0); } allocator.freeBlock(block); } @@ -2530,47 +2817,52 @@ export struct EntityManager return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(m_page_size - 1))); } - private void changeEntities() + private bool changeEntities() { - foreach (ref thread; threads) + bool has_work = false; + //foreach (ref ThreadData thread; threads)thread.swapToChange(); + foreach (ref ThreadData thread; threads) { uint index = 0; - uint len = cast(uint) thread.change_entities_list.length; + uint len = cast(uint) thread.changeEntitiesListPrev.length; + if (len) + has_work = true; void*[32] pointers; // = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; while (index < len) { - if (!thread.change_entities_list[index++]) + if (!thread.changeEntitiesListPrev[index++]) { - EntityID id = *cast(EntityID*)&thread.change_entities_list[index]; + EntityID id = *cast(EntityID*)&thread.changeEntitiesListPrev[index]; index += EntityID.sizeof; - uint num = *cast(uint*)&thread.change_entities_list[index]; + uint num = *cast(uint*)&thread.changeEntitiesListPrev[index]; index += uint.sizeof; ushort[] ids; // = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; - ids = (cast(ushort*)&thread.change_entities_list[index])[0 .. num]; + ids = (cast(ushort*)&thread.changeEntitiesListPrev[index])[0 .. num]; index += ushort.sizeof * num; __removeComponents(id, ids); } else { - EntityID id = *cast(EntityID*)&thread.change_entities_list[index]; + EntityID id = *cast(EntityID*)&thread.changeEntitiesListPrev[index]; index += EntityID.sizeof; - uint num = *cast(uint*)&thread.change_entities_list[index]; + uint num = *cast(uint*)&thread.changeEntitiesListPrev[index]; index += uint.sizeof; ushort[] ids; // = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; - ids = (cast(ushort*)&thread.change_entities_list[index])[0 .. num]; + ids = (cast(ushort*)&thread.changeEntitiesListPrev[index])[0 .. num]; index += ushort.sizeof * num; //void*[] pointers = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; foreach (i; 0 .. num) { - pointers[i] = &thread.change_entities_list[index]; + pointers[i] = &thread.changeEntitiesListPrev[index]; index += components[ids[i]].size; } - + __addComponents(id, ids, pointers[0 .. num]); } } - thread.change_entities_list.clear(); + thread.changeEntitiesListPrev.clear(); } + return has_work; } private void callAddEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow @@ -2582,7 +2874,7 @@ export struct EntityManager } } - private void callAddEntityListener(System* system, EntityInfo* info, + private static void callAddEntityListener(System* system, EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow { ListenerCallData data; @@ -2603,8 +2895,8 @@ export struct EntityManager } } - private void callRemoveEntityListener(System* system, EntityInfo* info, - EntitiesBlock* block, int begin, int end) @nogc nothrow + private static void callRemoveEntityListener(System* system, + EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow { ListenerCallData data; data.system = system; @@ -2651,108 +2943,173 @@ export struct EntityManager (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_change_entity)(data); } - private void updateBlocks() + private void updateEntityInfoBlocks(EntityInfo* info) nothrow @nogc { - foreach (ref thread; threads) + while (info.last_block.added_count) { - foreach (block; thread.blocks_to_update) + EntitiesBlock* block = info.update_block; + assert(block !is null); + if (block.entities_count == info.max_entities) { - EntityInfo* info = block.type_info; - ushort entities_count = block.entities_count; - block.entities_count += block.added_count; - if (block.entities_count > block.type_info.max_entities) - { - block.entities_count = block.type_info.max_entities; - } - block.added_count.atomicStore(cast(ushort) 0); - - if (info.add_listeners) - { - callAddEntityListeners(info, block, entities_count, block.entities_count); - } - + assert(!block.added_count); + block = block.next_block; } - thread.blocks_to_update.clear(); + assert(!block.prev_block || !block.prev_block.added_count); + info.update_block = info.last_block; + + while (block) + { + assert(block.added_count.atomicLoad() > 0); + updateBlock(block); + block = block.next_block; + } + } + assert(info.last_block is info.update_block); + } + + private void updateBlock(EntitiesBlock* block) @nogc nothrow + { + //if(block.added_count == 0)return; + assert(block.added_count != 0); + EntityInfo* info = block.type_info; + ushort entities_count = block.entities_count; + block.entities_count += block.added_count; + if (block.entities_count > block.type_info.max_entities) + { + block.entities_count = block.type_info.max_entities; + } + block.added_count.atomicStore(cast(ushort) 0); + + if (info.add_listeners) + { + callAddEntityListeners(info, block, entities_count, block.entities_count); } } - private void removeEntities() nothrow @nogc + private bool updateBlocks() { - foreach (i, ref thread; threads) + bool has_work = false; + foreach (ref ThreadData thread; threads) { - foreach (id; thread.entities_to_remove) + if (thread.infosToUpdatePrev.length) + has_work = true; + foreach (info; thread.infosToUpdatePrev) + { + updateEntityInfoBlocks(info); + } + thread.infosToUpdatePrev.clear(); + } + return has_work; + } + + private bool removeEntities() nothrow @nogc + { + bool has_work = false; + //foreach (ref ThreadData thread; threads)thread.swapToRemove(); + foreach (ref ThreadData thread; threads) + { + if (thread.entitiesToRemovePrev.length) + has_work = true; + foreach (id; thread.entitiesToRemovePrev) { __removeEntity(id); } - thread.entities_to_remove.clear(); + thread.entitiesToRemovePrev.clear(); } + return has_work; } - private void updateEvents() nothrow @nogc + private bool updateEvents() nothrow @nogc { - bool empty = true; - while (1) + bool has_work = false; + // bool empty = true; + //while (1) + //{ + //event_manager.swapCurrent(); + uint current_index; + if (event_manager.current_index == 0) + current_index = cast(uint) threads.length; + else + current_index = 0; + foreach (i, event; event_manager.events) { - event_manager.swapCurrent(); - uint current_index; - if (event_manager.current_index == 0) - current_index = cast(uint) threads.length; - else - current_index = 0; - foreach (i, event; event_manager.events) + foreach (first_block; event.first_blocks[current_index .. current_index + threads + .length]) { - foreach (first_block; event.first_blocks[current_index - .. current_index + threads.length]) + EventManager.EventBlock* block = first_block; + if (block) + has_work = true; + // { + // has_work = true; + // //empty = false; + // } + while (block) { - EventManager.EventBlock* block = first_block; - if (block) - empty = false; - while (block) + EventCallData call_data; + void* event_pointer = cast(void*) block + event.data_offset; + foreach (j; 0 .. block.count) { - EventCallData call_data; - void* event_pointer = cast(void*) block + event.data_offset; - foreach (j; 0 .. block.count) + call_data.event = event_pointer + EntityID.sizeof; + EntityID entity_id = *cast(EntityID*)(event_pointer); + Entity* entity = id_manager.getEntityPointer(entity_id); + if (entity) { - call_data.event = event_pointer; - EntityID entity_id = *cast(EntityID*) event_pointer; - Entity* entity = id_manager.getEntityPointer(entity_id); - if (entity) - { - call_data.block = getMetaData(entity); - call_data.id = call_data.block.entityIndex(entity); + call_data.block = getMetaData(entity); + call_data.id = call_data.block.entityIndex(entity); + call_data.entity = entity; - foreach (caller; events[i].callers) - { - if ( - call_data.block.type_info.systems[caller.system.m_id] - == false) - continue; - call_data.system_pointer = caller.system.m_system_pointer; - (cast(void function( - ref EventCallData) nothrow @nogc) caller.callback)( - call_data); - } + foreach (caller; events[i].callers) + { + if (call_data.block.type_info.systems[caller.system.m_id] == false + || !caller.system.enabled || !caller.system.willExecute) + continue; + call_data.system_pointer = caller.system.m_system_pointer; + (cast(void function(ref EventCallData) nothrow @nogc) caller + .callback)(call_data); } - if(events[i].destroy_callback)events[i].destroy_callback(event_pointer); - event_pointer += events[i].size; } - block = block.next; + if (events[i].destroy_callback) + events[i].destroy_callback(event_pointer); + event_pointer += events[i].size + EntityID.sizeof; } + block = block.next; } } - if (empty) - break; - empty = true; + } + // if (empty) + // break; + // empty = true; + //} + return has_work; + } + + private void swapData() nothrow @nogc + { + event_manager.swapCurrent(); + foreach (ref ThreadData thread; threads) + { + thread.swapData(); } } export void commit() { - id_manager.optimize(); - updateBlocks(); - changeEntities(); - updateEvents(); - removeEntities(); + bool has_work = true; + while (has_work) + { + swapData(); + + has_work = false; + // has_work |= updateBlocks(); + // has_work |= changeEntities(); + // has_work |= removeEntities(); + has_work |= updateEvents(); + + //id_manager.optimize(); + has_work |= updateBlocks(); + has_work |= changeEntities(); + has_work |= removeEntities(); + } event_manager.clearEvents(); } @@ -2820,13 +3177,16 @@ export struct EntityManager foreach (caller; pass.system_callers) { index = 0; + ///gets systems which are excluding each other out_for: foreach (caller2; pass.system_callers) { if (caller is caller2) continue; - foreach (cmp; caller.system.m_read_only_components) + + ///check for external dependencies + foreach (cmp; caller.system.m_readonly_dependencies) { - foreach (cmp2; caller2.system.m_modified_components) + foreach (cmp2; caller2.system.m_writable_dependencies) { if (cmp == cmp2) { @@ -2835,7 +3195,39 @@ export struct EntityManager } } } - foreach (cmp; caller.system.m_modified_components) + foreach (cmp; caller.system.m_writable_dependencies) + { + foreach (cmp2; caller2.system.m_readonly_dependencies) + { + if (cmp == cmp2) + { + exclusion[index++] = caller2; + continue out_for; + } + } + foreach (cmp2; caller2.system.m_writable_dependencies) + { + if (cmp == cmp2) + { + exclusion[index++] = caller2; + continue out_for; + } + } + } + + ///check for component dependencies + foreach (cmp; caller.system.m_read_only_components) + { + foreach (cmp2; caller2.system.m_writable_components) + { + if (cmp == cmp2) + { + exclusion[index++] = caller2; + continue out_for; + } + } + } + foreach (cmp; caller.system.m_writable_components) { foreach (cmp2; caller2.system.m_read_only_components) { @@ -2845,7 +3237,7 @@ export struct EntityManager continue out_for; } } - foreach (cmp2; caller2.system.m_modified_components) + foreach (cmp2; caller2.system.m_writable_components) { if (cmp == cmp2) { @@ -2923,6 +3315,19 @@ export struct EntityManager } } + const(UpdatePass)* getPass(const(char)[] name) + { + ushort id = getPassID(name); + if (id == ushort.max) + return null; + return passes[id]; + } + + ushort getPassID(const(char)[] name) + { + return passes_map.get(name, ushort.max); + } + /************************************************************************************************************************ Component info; */ @@ -2956,6 +3361,7 @@ export struct EntityManager EntitiesBlock* block; void* system_pointer; void* event; + Entity* entity; ushort id; } @@ -3046,9 +3452,9 @@ export struct EntityManager return new_info; } - EntityInfo* getNewInfoRemove(ushort id) + EntityInfo* getNewInfoRemove(ushort id) return { - if (comp_rem_info.length <= id) + /*if (comp_rem_info.length <= id) { EntityInfo*[] new_infos = Mallocator.makeArray!(EntityInfo*)( instance.components.length, &this); @@ -3060,7 +3466,7 @@ export struct EntityManager Mallocator.dispose(comp_rem_info); } comp_rem_info = new_infos; - } + }*/ if (comp_rem_info[id]) return comp_rem_info[id]; @@ -3075,8 +3481,9 @@ export struct EntityManager ids[len++] = comp; } } - if (len == components.length) - return &this; + assert(len != components.length); + //if (len == components.length) + // return &this; assert(len == components.length - 1); @@ -3118,7 +3525,7 @@ export struct EntityManager EntityInfo*[] comp_rem_info; ///alignment of whole entity - ushort alignment; //unused in linear-layout + ushort alignment; //unused in linear-layout TODO: to remove ///size of entity (with alignment respect) ushort size; ///max number of entities in block @@ -3137,6 +3544,8 @@ export struct EntityManager EntitiesBlock* first_block; ///pointer to last block EntitiesBlock* last_block; + ///pointer to last updated block + EntitiesBlock* update_block; } /************************************************************************************************************************ @@ -3144,23 +3553,14 @@ export struct EntityManager */ struct EntitiesBlock { - ///return distance (in bytes) from begin of block to data - ///TODO: probably to remove. It's used by old code if I remeber correctly. - /*export uint dataDelta() nothrow @nogc pure - { - ushort dif = EntitiesBlock.sizeof; - alignNum(dif, type_info.alignment); - return dif; - }*/ - ///return pointer to first element in block - export void* dataBegin() nothrow @nogc pure + export void* dataBegin() nothrow @nogc pure return { ushort dif = EntitiesBlock.sizeof; return cast(void*)&this + dif; } - export ushort entityIndex(Entity* entity) nothrow @nogc pure + export ushort entityIndex(const(Entity)* entity) nothrow @nogc pure { static if (EntityID.sizeof == 8) return cast(ushort)((cast(void*) entity - dataBegin()) >> 3); @@ -3218,6 +3618,8 @@ export struct EntityManager ushort end; ///current thread index uint thread_id; + //current job index + uint job_id; } struct ListenerCallData @@ -3231,6 +3633,7 @@ export struct EntityManager struct Job { CallData[] callers; + uint id; export void execute() nothrow @nogc { @@ -3238,6 +3641,7 @@ export struct EntityManager foreach (ref caller; callers) { caller.thread_id = EntityManager.instance.threadID(); + caller.job_id = id; caller.update(); } } @@ -3278,10 +3682,48 @@ export struct EntityManager struct ThreadData { - Vector!EntityID entities_to_remove; - //Vector!ubyte change_entities_list; - SimpleVector change_entities_list; - Vector!(EntitiesBlock*) blocks_to_update; + ref Vector!EntityID entitesToRemove() @nogc nothrow return + { + return entities_to_remove[data_index]; + } + + ref SimpleVector changeEntitiesList() @nogc nothrow return + { + return change_entities_list[data_index]; + } + + ref Vector!(EntityInfo*) infosToUpdate() @nogc nothrow return + { + return infos_to_update[data_index]; + } + + ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow return + { + return entities_to_remove[1 - data_index]; + } + + ref SimpleVector changeEntitiesListPrev() @nogc nothrow return + { + return change_entities_list[1 - data_index]; + } + + ref Vector!(EntityInfo*) infosToUpdatePrev() @nogc nothrow return + { + return infos_to_update[1 - data_index]; + } + + private: + + void swapData() @nogc nothrow + { + data_index = cast(ubyte)(1 - data_index); + } + + Vector!EntityID[2] entities_to_remove; + SimpleVector[2] change_entities_list; + Vector!(EntityInfo*)[2] infos_to_update; + + ubyte data_index = 0; } export struct UpdatePass @@ -3350,6 +3792,7 @@ export struct EntityManager HashMap!(char[], ushort) components_map; HashMap!(const(char)[], ushort) events_map; HashMap!(const(char)[], ushort) passes_map; + HashMap!(const(char)[], ushort) external_dependencies_map; Vector!System systems; Vector!ComponentInfo components; Vector!EventInfo events; diff --git a/source/bubel/ecs/package.d b/source/bubel/ecs/package.d new file mode 100644 index 0000000..51da325 --- /dev/null +++ b/source/bubel/ecs/package.d @@ -0,0 +1,10 @@ +module ecs; + +public import bubel.ecs.core; +public import bubel.ecs.entity; +public import bubel.ecs.manager; +public import bubel.ecs.system; + +import bubel.ecs.events; +import bubel.ecs.id_manager; +import bubel.ecs.std; diff --git a/source/bubel/ecs/simple_vector.d b/source/bubel/ecs/simple_vector.d new file mode 100644 index 0000000..bb4b610 --- /dev/null +++ b/source/bubel/ecs/simple_vector.d @@ -0,0 +1,77 @@ +module bubel.ecs.simple_vector; + +import bubel.ecs.std; + +//import core.stdc.string; + +/************************************************************************************************************************ +Vector for byte data. Simpler than standard template-based implementation designed for better performance. \ +Rellocates 1024 elements at once instead of doubling size. +*/ +struct SimpleVector +{ + + @disable this(this); + + ///Add element to vector + void add(ubyte el) nothrow @nogc + { + while (used >= data.length) + { + if (data is null) + data = Mallocator.makeArray!ubyte(1024); + else + data = Mallocator.expandArray(data, data.length); + } + data[used++] = el; + } + + ///Add array of elements to vector + void add(ubyte[] el) nothrow @nogc + { + while (used + el.length >= data.length) + { + if (data is null) + data = Mallocator.makeArray!ubyte(1024); + else + data = Mallocator.expandArray(data, data.length); + } + memcpy(data.ptr + used, el.ptr, el.length); + used += el.length; + } + + ///Return vector length + size_t length() nothrow @nogc + { + return used; + } + + export ref ubyte opIndex(size_t pos) nothrow @nogc + { + return data[pos]; + } + + export ubyte[] opSlice() nothrow @nogc + { + return data[0 .. used]; + } + + export ubyte[] opSlice(size_t x, size_t y) nothrow @nogc + { + return data[x .. y]; + } + + export size_t opDollar() nothrow @nogc + { + return used; + } + + ///set vector length to 0 + void clear() nothrow @nogc + { + used = 0; + } + + ubyte[] data = null; + size_t used = 0; +} diff --git a/source/bubel/ecs/std.d b/source/bubel/ecs/std.d new file mode 100644 index 0000000..a7dd846 --- /dev/null +++ b/source/bubel/ecs/std.d @@ -0,0 +1,363 @@ +/************************************************************************************************************************ +It's internal code! +This module contain implementation of standard functionality. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ +module bubel.ecs.std; + +version (Emscripten) version = ECSEmscripten; + +import std.traits; + +version (ECSEmscripten) +{ + extern (C) struct pthread_mutex_t + { + union + { + int[6] __i; + void[6]* __p; + } + } + + extern (C) struct pthread_mutexattr_t + { + uint __attr; + } + + extern (C) int memcmp(const void* s1, const void* s2, size_t size); + extern (C) void exit(int status) nothrow @nogc; + extern (C) void __assert(const(char)* msg, const(char)* file, uint line) + { + exit(-20); + } + + extern (C) void free(void*) @nogc nothrow @system; + extern (C) void* malloc(size_t size) @nogc nothrow @system; + extern (C) void* realloc(void*, size_t size) @nogc nothrow @system; + extern (C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system; + extern (C) void* memset(void*, int val, size_t size) @nogc nothrow @system; + extern (C) int posix_memalign(void**, size_t, size_t) @nogc nothrow @system; + extern (C) void qsort(void* base, size_t num, size_t size, + int function(const void*, const void*) compar) @nogc nothrow @system; + + extern (C) int pthread_mutex_lock(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) int pthread_mutex_trylock(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) int pthread_mutex_unlock(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) void pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) @nogc nothrow; + extern (C) void pthread_mutexattr_destroy(pthread_mutexattr_t* attr) @nogc nothrow; + extern (C) int pthread_mutexattr_init(pthread_mutexattr_t* attr) @nogc nothrow; + extern (C) int pthread_mutex_destroy(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) @nogc nothrow; + +} +else +{ + public import core.stdc.stdlib : malloc, free, realloc; + public import core.stdc.string : memcpy, memset; + public import core.stdc.stdlib : qsort; +} + +version (ECSEmscripten) +{ +} +else version (Windows) +{ + import core.sys.windows.windows; + + extern (Windows) void* _aligned_malloc(size_t size, size_t alignment) @nogc nothrow @system; + extern (Windows) void _aligned_free(void* ptr) @nogc nothrow @system; + + version (LDC) + { + /*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system; + alias alloca = __alloca;*/ + + extern (Windows) void ___chkstk_ms() @nogc nothrow @system; + + extern (Windows) void __chkstk() + { + ___chkstk_ms(); + } + } +} +else version (Posix) +{ + import core.sys.posix.pthread; + import core.sys.posix.stdlib : posix_memalign; +} + +version (ECSEmscripten) +{ + private const uint max_alloca = 10000; + private __gshared byte[max_alloca] alloca_array; + private __gshared uint alloca_pos = 0; + export extern (C) void* alloca(size_t length) @nogc nothrow + { + if (alloca_pos + length > max_alloca) + alloca_pos = 0; + void* ret = &alloca_array[alloca_pos]; + alloca_pos += length; + return ret; + } + //extern(C) void* alloca(size_t size) @nogc nothrow; + /*export extern(C) void* alloca(size_t length) @nogc nothrow + { + return null; + }*/ +} +else version (D_BetterC) +{ + private const uint max_alloca = 10000; + private __gshared byte[max_alloca] alloca_array; + private uint alloca_pos = 0; + export extern (C) void* __alloca(size_t length) @nogc nothrow + { + if (alloca_pos + length > max_alloca) + alloca_pos = 0; + void* ret = &alloca_array[alloca_pos]; + alloca_pos += length; + return ret; + } + + alias alloca = __alloca; + + version (DigitalMars) + { + export extern (C) float* _memsetFloat(float* p, float value, size_t count) @nogc nothrow + { + float* pstart = p; + float* ptop; + + for (ptop = &p[count]; p < ptop; p++) + *p = value; + return pstart; + } + } + + version (GNU) + { + extern (C) void __gdc_personality_v0() + { + + } + } +} +else +{ + public import core.stdc.stdlib : alloca; +} + +static struct Mallocator +{ + static T[] makeArray(T)(size_t length) nothrow @nogc + { + T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length]; + + static if (__traits(isPOD, T)) + { + static immutable T init = T.init; + + foreach (i; 0 .. ret.length) + { + memcpy(&ret[i], &init, T.sizeof); + } + } + else + { + static import std.conv; + + foreach (i; 0 .. ret.length) + { + std.conv.emplace(&ret[i]); + } + } + return ret; + } + + static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc + { + T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length]; + foreach (ref v; ret) + v = initializer; + return ret; + } + + static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc + { + size_t new_length = array.length + length; + return (cast(T*) realloc(array.ptr, T.sizeof * new_length))[0 .. new_length]; + } + + static T[] makeArray(T)(T[] array) nothrow @nogc + { + T[] ret = (cast(T*) malloc(T.sizeof * array.length))[0 .. array.length]; //Mallocator.makeArray!(T)(array.length); + foreach (i, ref v; ret) + v = array[i]; + return ret; + } + + static T* make(T, Args...)(Args args) + { + T* ret = cast(T*) malloc(T.sizeof); + static import std.conv; + + static if (__traits(isPOD, T)) + { + static immutable T init = T.init; + memcpy(ret, &init, T.sizeof); + } + else static if (is(T == struct)) + std.conv.emplace(ret, args); + return ret; + } + + static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc + { + void* ret; + version (Posix) + posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length); + else version (Windows) + ret = _aligned_malloc(length, alignment); + else version (ECSEmscripten) + posix_memalign(&ret, alignment, length); //malloc(length); + else + static assert(0, "Unimplemented platform!"); + return ret; + } + + static void dispose(T)(T object) nothrow @nogc + { + static if (__traits(hasMember, T, "__xdtor")) + object.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + object.__dtor(); + free(cast(void*) object); + } + + static void alignDispose(T)(T object) + { + static if (__traits(hasMember, T, "__xdtor")) + object.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + object.__dtor(); + version (Posix) + free(cast(void*) object); + else version (Windows) + _aligned_free(cast(void*) object); + else version (ECSEmscripten) + free(cast(void*) object); + else + static assert(0, "Unimplemented platform!"); + } +} + +struct Mutex +{ + + version (ECSEmscripten) + { + void initialize() nothrow @nogc + { + pthread_mutexattr_t attr = void; + + //pthread_mutexattr_init(&attr); + + //pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(cast(pthread_mutex_t*)&m_handle, &attr); + + //pthread_mutexattr_destroy(&attr); + } + + void destroy() nothrow @nogc + { + pthread_mutex_destroy(&m_handle); + } + + void lock() nothrow @nogc + { + pthread_mutex_lock(&m_handle); + } + + void unlock() nothrow @nogc + { + pthread_mutex_unlock(&m_handle); + } + + int tryLock() nothrow @nogc + { + return pthread_mutex_trylock(&m_handle) == 0; + } + + private pthread_mutex_t m_handle; + } + else version (Windows) + { + void initialize() nothrow @nogc + { + InitializeCriticalSection(cast(CRITICAL_SECTION*)&m_handle); + } + + void destroy() nothrow @nogc + { + DeleteCriticalSection(&m_handle); + } + + void lock() nothrow @nogc + { + EnterCriticalSection(&m_handle); + } + + void unlock() nothrow @nogc + { + LeaveCriticalSection(&m_handle); + } + + int tryLock() nothrow @nogc + { + return TryEnterCriticalSection(&m_handle) != 0; + } + + CRITICAL_SECTION m_handle; + } + else version (Posix) + { + void initialize() nothrow @nogc + { + pthread_mutexattr_t attr = void; + + pthread_mutexattr_init(&attr); + + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(cast(pthread_mutex_t*)&m_handle, &attr); + + pthread_mutexattr_destroy(&attr); + } + + void destroy() nothrow @nogc + { + pthread_mutex_destroy(&m_handle); + } + + void lock() nothrow @nogc + { + pthread_mutex_lock(&m_handle); + } + + void unlock() nothrow @nogc + { + pthread_mutex_unlock(&m_handle); + } + + int tryLock() nothrow @nogc + { + return pthread_mutex_trylock(&m_handle) == 0; + } + + private pthread_mutex_t m_handle; + } + else + static assert(0, "unsupported platform!"); +} diff --git a/source/ecs/system.d b/source/bubel/ecs/system.d similarity index 77% rename from source/ecs/system.d rename to source/bubel/ecs/system.d index 22a3955..7bcaf01 100644 --- a/source/ecs/system.d +++ b/source/bubel/ecs/system.d @@ -4,26 +4,26 @@ System module. Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ -module ecs.system; +module bubel.ecs.system; -import ecs.entity; -import ecs.manager; +import bubel.ecs.entity; +import bubel.ecs.manager; /************************************************************************************************************************ System contain data required to proper glue EntityManager with Systems. System callbacks: $(LIST * void onUpdate(EntitesData); - * void onEnable() - * void onDisable(); - * bool onBegin(); - * void onEnd(); - * void onCreate() - * void onDestroy(); - * void onAddEntity(EntitesData); - * void onRemoveEntity(EntitiesData); - * void onChangeEntity(EntitiesData); - * void handleEvent(Entity*, Event); + * void onEnable() - called inside system.enable() function + * void onDisable() - called inside system.disable() function + * bool onBegin() - called inside manager.begin() + * void onEnd() - called inside manager.end() + * void onCreate() - called after registration inside registerSystem function + * void onDestroy() - called during re-registration and inside manager destructor + * void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components) + * void onRemoveEntity(EntitiesData) - called for every entity removed from system update process + * void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system + * void handleEvent(Entity*, Event) - called for every event supported by system ) */ struct System @@ -66,15 +66,15 @@ struct System } /************************************************************************************************************************ - Get system priority. + Get if system will be executed during current frame. Should be checked after manager.begin(). Its value is setted as result of manager.onBegin() callback. */ - export bool execute() nothrow @nogc + export bool willExecute() nothrow @nogc { return m_execute; } /************************************************************************************************************************ - Get system priority. + Get system id. */ export ushort id() nothrow @nogc { @@ -127,7 +127,10 @@ package: //System*[] m_dependencies; ushort[] m_read_only_components; - ushort[] m_modified_components; + ushort[] m_writable_components; + + ushort[] m_readonly_dependencies; + ushort[] m_writable_dependencies; EntityManager.SystemCaller* m_any_system_caller; diff --git a/source/ecs/traits.d b/source/bubel/ecs/traits.d similarity index 97% rename from source/ecs/traits.d rename to source/bubel/ecs/traits.d index d19259f..7043b2e 100644 --- a/source/ecs/traits.d +++ b/source/bubel/ecs/traits.d @@ -1,4 +1,4 @@ -module ecs.traits; +module bubel.ecs.traits; import std.traits; diff --git a/source/ecs/vector.d b/source/bubel/ecs/vector.d similarity index 53% rename from source/ecs/vector.d rename to source/bubel/ecs/vector.d index 84ecc51..019673b 100644 --- a/source/ecs/vector.d +++ b/source/bubel/ecs/vector.d @@ -1,30 +1,36 @@ -module ecs.vector; +module bubel.ecs.vector; import core.bitop; + //import core.stdc.stdlib : free, malloc; -import ecs.std; +import bubel.ecs.std; + //import core.stdc.string : memcpy, memset; //import std.algorithm : swap; import std.conv : emplace; import std.traits : hasMember, isCopyable, TemplateOf, Unqual; -export @nogc @safe nothrow pure size_t nextPow2(size_t num) { +export @nogc @safe nothrow pure size_t nextPow2(size_t num) +{ return 1 << bsr(num) + 1; } export __gshared size_t gVectorsCreated = 0; export __gshared size_t gVectorsDestroyed = 0; -struct Vector(T) { +struct Vector(T) +{ T[] array; size_t used; public: - export this()(T t) { + export this()(T t) + { add(t); } - export this(X)(X[] t) if (is(Unqual!X == Unqual!T)) { + export this(X)(X[] t) if (is(Unqual!X == Unqual!T)) + { add(t); } @@ -42,76 +48,101 @@ public: @disable this(this); - export ~this() { + export ~this() + { clear(); } - export void clear() { + export void clear() + { removeAll(); } - export void removeAll() { - if (array !is null) { + export void removeAll() + { + if (array !is null) + { /*foreach (ref el; array[0 .. used]) { destroy(el); }*/ - freeData(cast(void[]) array); + //freeData(cast(void[]) array); + freeData((cast(void*) array.ptr)[0 .. array.length * T.sizeof]); gVectorsDestroyed++; } array = null; used = 0; } - export bool empty() { + export bool empty() const + { return (used == 0); } - export size_t length() { + export size_t length() const + { return used; } - export void length(size_t newLength) { - if (newLength > used) { + export void length(size_t newLength) + { + if (newLength > used) + { reserve(newLength); - foreach (ref el; array[used .. newLength]) { + foreach (ref el; array[used .. newLength]) + { emplace(&el); } - } else { - foreach (ref el; array[newLength .. used]) { - destroy(el); + } + else + { + foreach (ref el; array[newLength .. used]) + { + //destroy(el); + static if (__traits(hasMember, T, "__xdtor")) + el.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + el.__dtor(); } } used = newLength; } - export void reset() { + export void reset() + { used = 0; } - export void reserve(size_t numElements) { - if (numElements > array.length) { + export void reserve(size_t numElements) + { + if (numElements > array.length) + { extend(numElements); } } - export size_t capacity() { + export size_t capacity() + { return array.length - used; } - export void extend(size_t newNumOfElements) { + export void extend(size_t newNumOfElements) + { auto oldArray = manualExtend(array, newNumOfElements); - if (oldArray !is null) { + if (oldArray !is null) + { freeData(oldArray); } } - export @nogc void freeData(void[] data) { + export @nogc void freeData(void[] data) + { // 0x0F probably invalid value for pointers and other types memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD free(data.ptr); } - export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) { + export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) + { if (newNumOfElements == 0) newNumOfElements = 2; if (array.length == 0) @@ -122,22 +153,27 @@ public: T* memory = cast(T*) malloc(newSize); memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize); array = memory[0 .. newNumOfElements]; - return cast(void[]) oldArray; + //return cast(void[]) oldArray; + return (cast(void*) oldArray.ptr)[0 .. oldArray.length * T.sizeof]; } - export Vector!T copy()() { + export Vector!T copy()() + { Vector!T duplicate; duplicate.reserve(used); duplicate ~= array[0 .. used]; return duplicate; } - export bool canAddWithoutRealloc(uint elemNum = 1) { + /*export bool canAddWithoutRealloc(uint elemNum = 1) + { return used + elemNum <= array.length; - } + }*/ - export void add()(T t) { - if (used >= array.length) { + export void add()(T t) + { + if (used >= array.length) + { extend(nextPow2(used + 1)); } emplace(&array[used], t); @@ -145,46 +181,62 @@ public: } /// Add element at given position moving others - export void add()(T t, size_t pos) { + export void add()(T t, size_t pos) + { assert(pos <= used); - if (used >= array.length) { + if (used >= array.length) + { extend(array.length * 2); } - foreach_reverse (size_t i; pos .. used) { + foreach_reverse (size_t i; pos .. used) + { //swap(array[i + 1], array[i]); - array[i+1] = array[i]; + array[i + 1] = array[i]; } emplace(&array[pos], t); used++; } - export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { - if (used + t.length > array.length) { + export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) + { + if (used + t.length > array.length) + { extend(nextPow2(used + t.length)); } - foreach (i; 0 .. t.length) { + foreach (i; 0 .. t.length) + { emplace(&array[used + i], t[i]); } used += t.length; } - export void remove(size_t elemNum) { - destroy(array[elemNum]); + export void remove(size_t elemNum) + { + //destroy(array[elemNum]); + static if (__traits(hasMember, T, "__xdtor")) + array[elemNum].__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + array[elemNum].__dtor(); //swap(array[elemNum], array[used - 1]); array[elemNum] = array[used - 1]; used--; } - export void removeStable()(size_t elemNum) { + export void removeStable()(size_t elemNum) + { used--; - foreach (i; 0 .. used) { + foreach (i; 0 .. used) + { array[i] = array[i + 1]; } } - export bool tryRemoveElement()(T elem) { - foreach (i, ref el; array[0 .. used]) { - if (el == elem) { + export bool tryRemoveElement()(T elem) + { + foreach (i, ref el; array[0 .. used]) + { + if (el == elem) + { remove(i); return true; } @@ -192,55 +244,66 @@ public: return false; } - export void removeElement()(T elem) { + export void removeElement()(T elem) + { bool ok = tryRemoveElement(elem); assert(ok, "There is no such an element in vector"); } - export ref T opIndex(size_t elemNum) { + export ref T opIndex(size_t elemNum) const + { //debug assert(elemNum < used, "Range violation [index]"); - return array.ptr[elemNum]; + return *cast(T*)&array.ptr[elemNum]; } - export auto opSlice() { + export auto opSlice() + { return array.ptr[0 .. used]; } - export T[] opSlice(size_t x, size_t y) { + export T[] opSlice(size_t x, size_t y) + { assert(y <= used); return array.ptr[x .. y]; } - export size_t opDollar() { + export size_t opDollar() + { return used; } - export void opAssign(X)(X[] slice) { + export void opAssign(X)(X[] slice) + { reset(); this ~= slice; } - export void opOpAssign(string op)(T obj) { + export void opOpAssign(string op)(T obj) + { //static assert(op == "~"); add(obj); } - export void opOpAssign(string op, X)(X[] obj) { + export void opOpAssign(string op, X)(X[] obj) + { //static assert(op == "~"); add(obj); } - export void opIndexAssign()(T obj, size_t elemNum) { + export void opIndexAssign()(T obj, size_t elemNum) + { assert(elemNum < used, "Range viloation"); array[elemNum] = obj; } - export void opSliceAssign()(T[] obj, size_t a, size_t b) { + export void opSliceAssign()(T[] obj, size_t a, size_t b) + { assert(b <= used && a <= b, "Range viloation"); array.ptr[a .. b] = obj; } - export bool opEquals()(auto ref const Vector!(T) r) const { + export bool opEquals()(auto ref const Vector!(T) r) const + { return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used]; } -} \ No newline at end of file +} diff --git a/source/ecs/atomic.d b/source/ecs/atomic.d deleted file mode 100644 index 5e5f447..0000000 --- a/source/ecs/atomic.d +++ /dev/null @@ -1,97 +0,0 @@ -/************************************************************************************************************************ -It's internal code. Can be used for atomics if emscripten backend will be used. - -This module contain atomic operations which include support for emscripten atomics functions. -Emscripten functions are contained in API similar to druntime. - -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz -License: BSD 3-clause, see LICENSE file in project root folder. -*/ -module ecs.atomic; - -version(Emscripten)version = ECSEmscripten; - -version(ECSEmscripten) -{ - import std.traits; - - enum MemoryOrder - { - acq, - acq_rel, - raw, - rel, - seq - } - - extern(C) ubyte emscripten_atomic_cas_u8(void *addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_cas_u16(void *addr, ushort oldVal, ushort newVal) @nogc nothrow pure; - extern(C) uint emscripten_atomic_cas_u32(void *addr, uint oldVal, uint newVal) @nogc nothrow pure; - - extern(C) ubyte emscripten_atomic_load_u8(const void *addr) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_load_u16(const void *addr) @nogc nothrow pure; - extern(C) uint emscripten_atomic_load_u32(const void *addr) @nogc nothrow pure; - - extern(C) ubyte emscripten_atomic_store_u8(void *addr, ubyte val) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_store_u16(void *addr, ushort val) @nogc nothrow pure; - extern(C) uint emscripten_atomic_store_u32(void *addr, uint val) @nogc nothrow pure; - - extern(C) ubyte emscripten_atomic_add_u8(void *addr, ubyte val) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_add_u16(void *addr, ushort val) @nogc nothrow pure; - extern(C) uint emscripten_atomic_add_u32(void *addr, uint val) @nogc nothrow pure; - - extern(C) ubyte emscripten_atomic_sub_u8(void *addr, ubyte val) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_sub_u16(void *addr, ushort val) @nogc nothrow pure; - extern(C) uint emscripten_atomic_sub_u32(void *addr, uint val) @nogc nothrow pure; - - public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) - { - static if(op == "+=") - { - static if(is(T == byte) || is(T == ubyte))return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, cast(Unqual!T)mod) + 1); - else static if(is(T == short) || is(T == ushort))return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, cast(Unqual!T)mod) + 1); - else static if(is(T == int) || is(T == uint))return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, cast(Unqual!T)mod) + 1); - else static assert(0); - } - else static if(op == "-=") - { - static if(is(T == byte) || is(T == ubyte))return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, cast(Unqual!T)mod) - 1); - else static if(is(T == short) || is(T == ushort))return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, cast(Unqual!T)mod) - 1); - else static if(is(T == int) || is(T == uint))return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, cast(Unqual!T)mod) - 1); - else static assert(0); - } - } - - public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) - { - alias UT = Unqual!T; - static if(is(UT == bool) || is(UT == byte) || is(UT == ubyte))emscripten_atomic_store_u8(cast(void*)&val, cast(UT)newval); - else static if(is(UT == short) || is(UT == ushort))emscripten_atomic_store_u16(cast(void*)&val, cast(UT)newval); - else static if(is(UT == int) || is(UT == uint))emscripten_atomic_store_u32(cast(void*)&val, cast(UT)newval); - else static assert(0); - } - - public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref const T val) - { - alias UT = Unqual!T; - static if(is(UT == bool))return emscripten_atomic_load_u8(cast(const void*)&val) != 0; - else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_load_u8(cast(const void*)&val); - else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_load_u16(cast(const void*)&val); - else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_load_u32(cast(const void*)&val); - else static assert(0); - } - - public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) - { - alias UT = Unqual!T; - static if(is(UT == bool))return emscripten_atomic_cas_u8(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_cas_u8(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_cas_u16(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_cas_u32(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static assert(0); - } -} -else -{ - public import core.atomic; -} \ No newline at end of file diff --git a/source/ecs/package.d b/source/ecs/package.d deleted file mode 100644 index eda440d..0000000 --- a/source/ecs/package.d +++ /dev/null @@ -1,10 +0,0 @@ -module ecs; - -public import ecs.core; -public import ecs.entity; -public import ecs.manager; -public import ecs.system; - -import ecs.events; -import ecs.id_manager; -import ecs.std; \ No newline at end of file diff --git a/source/ecs/simple_vector.d b/source/ecs/simple_vector.d deleted file mode 100644 index 175c015..0000000 --- a/source/ecs/simple_vector.d +++ /dev/null @@ -1,65 +0,0 @@ -module ecs.simple_vector; - -import ecs.std; - -//import core.stdc.string; - -struct SimpleVector -{ - - @disable this(this); - - void add(ubyte el) nothrow @nogc - { - while(used >= data.length) - { - if(data is null)data = Mallocator.makeArray!ubyte(1024); - else data = Mallocator.expandArray(data,data.length); - } - data[used++] = el; - } - - void add(ubyte[] el) nothrow @nogc - { - while(used + el.length >= data.length) - { - if(data is null)data = Mallocator.makeArray!ubyte(1024); - else data = Mallocator.expandArray(data,data.length); - } - memcpy(data.ptr + used, el.ptr, el.length); - used += el.length; - } - - size_t length() nothrow @nogc - { - return used; - } - - export ref ubyte opIndex(size_t pos) nothrow @nogc - { - return data[pos]; - } - - export ubyte[] opSlice() nothrow @nogc - { - return data[0 .. used]; - } - - export ubyte[] opSlice(size_t x, size_t y) nothrow @nogc - { - return data[x .. y]; - } - - export size_t opDollar() nothrow @nogc - { - return used; - } - - void clear() nothrow @nogc - { - used = 0; - } - - ubyte[] data = null; - size_t used = 0; -} \ No newline at end of file diff --git a/source/ecs/std.d b/source/ecs/std.d deleted file mode 100644 index c027fd5..0000000 --- a/source/ecs/std.d +++ /dev/null @@ -1,314 +0,0 @@ -/************************************************************************************************************************ -It's internal code! -This module contain implementation of standard functionality. - -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz -License: BSD 3-clause, see LICENSE file in project root folder. -*/ -module ecs.std; - -version(Emscripten)version = ECSEmscripten; - -import std.traits; - -version(ECSEmscripten) -{ - extern(C) struct pthread_mutex_t - { - union - { - int[6] __i; - void[6] *__p; - } - } - - extern(C) struct pthread_mutexattr_t - { - uint __attr; - } - - extern(C) int memcmp (const void *s1, const void *s2, size_t size); - extern(C) void exit (int status) nothrow @nogc; - extern(C) void __assert(const(char)* msg, const(char)* file, uint line) { exit(-20);} - extern(C) void free(void*) @nogc nothrow @system; - extern(C) void* malloc(size_t size) @nogc nothrow @system; - extern(C) void* realloc(void*, size_t size) @nogc nothrow @system; - extern(C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system; - extern(C) void* memset(void*, int val, size_t size) @nogc nothrow @system; - extern(C) int posix_memalign(void**, size_t, size_t) @nogc nothrow @system; - extern(C) void qsort(void* base, size_t num, size_t size, int function(const void*,const void*) compar) @nogc nothrow @system; - - extern(C) int pthread_mutex_lock(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) int pthread_mutex_trylock(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) int pthread_mutex_unlock(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) void pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) @nogc nothrow; - extern(C) void pthread_mutexattr_destroy(pthread_mutexattr_t *attr) @nogc nothrow; - extern(C) int pthread_mutexattr_init(pthread_mutexattr_t *attr) @nogc nothrow; - extern(C) int pthread_mutex_destroy(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) @nogc nothrow; - -} -else -{ - public import core.stdc.stdlib : malloc, free, realloc; - public import core.stdc.string : memcpy, memset; - public import core.stdc.stdlib : qsort; -} - -version(ECSEmscripten) -{ -} -else version (Windows) -{ - import core.sys.windows.windows; - extern(Windows) void* _aligned_malloc(size_t size,size_t alignment) @nogc nothrow @system; - extern(Windows) void _aligned_free(void* ptr) @nogc nothrow @system; - - version(LDC) - { - /*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system; - alias alloca = __alloca;*/ - - extern(Windows) void ___chkstk_ms() @nogc nothrow @system; - - extern(Windows) void __chkstk() - { - ___chkstk_ms(); - } - } -} -else version (Posix) -{ - import core.sys.posix.pthread; - import core.sys.posix.stdlib; -} - -version(ECSEmscripten) -{ - private const uint max_alloca = 10000; - private __gshared byte[max_alloca] alloca_array; - private __gshared uint alloca_pos = 0; - export extern(C) void* alloca(size_t length) @nogc nothrow - { - if(alloca_pos + length > max_alloca)alloca_pos = 0; - void* ret = &alloca_array[alloca_pos]; - alloca_pos += length; - return ret; - } - //extern(C) void* alloca(size_t size) @nogc nothrow; - /*export extern(C) void* alloca(size_t length) @nogc nothrow - { - return null; - }*/ -} -else version(D_BetterC) -{ - private const uint max_alloca = 10000; - private __gshared byte[max_alloca] alloca_array; - private uint alloca_pos = 0; - export extern(C) void* alloca(size_t length) @nogc nothrow - { - if(alloca_pos + length > max_alloca)alloca_pos = 0; - void* ret = &alloca_array[alloca_pos]; - alloca_pos += length; - return ret; - } -} -else -{ - public import core.stdc.stdlib : alloca; -} - -static struct Mallocator -{ - static T[] makeArray(T)(size_t length) nothrow @nogc - { - T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length]; - - static if(__traits(isPOD, T)) - { - static immutable T init = T.init; - - foreach(i;0..ret.length) - { - memcpy(&ret[i], &init, T.sizeof); - } - } - else - { - static import std.conv; - foreach(i;0..ret.length) - { - std.conv.emplace(&ret[i]); - } - } - return ret; - } - - static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc - { - T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length]; - foreach(ref v; ret)v = initializer; - return ret; - } - - static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc - { - size_t new_length = array.length + length; - return (cast(T*)realloc(array.ptr, T.sizeof * new_length))[0 .. new_length]; - } - - static T[] makeArray(T)(T[] array) nothrow @nogc - { - T[] ret = (cast(T*)malloc(T.sizeof * array.length))[0 .. array.length];//Mallocator.makeArray!(T)(array.length); - foreach(i, ref v;ret)v = array[i]; - return ret; - } - - static T* make(T, Args...)(Args args) - { - T* ret = cast(T*)malloc(T.sizeof); - static import std.conv; - static if(__traits(isPOD, T)) - { - static immutable T init = T.init; - memcpy(ret, &init, T.sizeof); - } - else static if(is(T == struct))std.conv.emplace(ret, args); - return ret; - } - - static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc - { - void* ret; - version(Posix)posix_memalign(&ret, alignment, length);//ret = aligned_alloc(alignment, length); - else version(Windows)ret = _aligned_malloc(length, alignment); - else version(ECSEmscripten)posix_memalign(&ret, alignment, length);//malloc(length); - else static assert(0, "Unimplemented platform!"); - return ret; - } - - static void dispose(T)(T object) nothrow @nogc - { - static if(__traits(hasMember, T, "__xdtor"))object.__xdtor(); - else static if(__traits(hasMember, T, "__dtor"))object.__dtor(); - free(cast(void*)object); - } - - static void alignDispose(T)(T object) - { - static if(__traits(hasMember, T, "__xdtor"))object.__xdtor(); - else static if(__traits(hasMember, T, "__dtor"))object.__dtor(); - version(Posix)free(cast(void*)object); - else version(Windows)_aligned_free(cast(void*)object); - else version(ECSEmscripten)free(cast(void*)object); - else static assert(0, "Unimplemented platform!"); - } -} - -struct Mutex -{ - - version(ECSEmscripten) - { - void initialize() nothrow @nogc - { - pthread_mutexattr_t attr = void; - - //pthread_mutexattr_init(&attr); - - //pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(cast(pthread_mutex_t*) &m_handle, &attr); - - //pthread_mutexattr_destroy(&attr); - } - - void destroy() nothrow @nogc - { - pthread_mutex_destroy(&m_handle); - } - - void lock() nothrow @nogc - { - pthread_mutex_lock(&m_handle); - } - - void unlock() nothrow @nogc - { - pthread_mutex_unlock(&m_handle); - } - - int tryLock() nothrow @nogc - { - return pthread_mutex_trylock(&m_handle) == 0; - } - - private pthread_mutex_t m_handle; - } - else version (Windows) - { - void initialize() nothrow @nogc - { - InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_handle); - } - - void destroy() nothrow @nogc - { - DeleteCriticalSection(&m_handle); - } - - void lock() nothrow @nogc - { - EnterCriticalSection(&m_handle); - } - - void unlock() nothrow @nogc - { - LeaveCriticalSection(&m_handle); - } - - int tryLock() nothrow @nogc - { - return TryEnterCriticalSection(&m_handle) != 0; - } - - CRITICAL_SECTION m_handle; - } - else version (Posix) - { - void initialize() nothrow @nogc - { - pthread_mutexattr_t attr = void; - - pthread_mutexattr_init(&attr); - - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(cast(pthread_mutex_t*) &m_handle, &attr); - - pthread_mutexattr_destroy(&attr); - } - - void destroy() nothrow @nogc - { - pthread_mutex_destroy(&m_handle); - } - - void lock() nothrow @nogc - { - pthread_mutex_lock(&m_handle); - } - - void unlock() nothrow @nogc - { - pthread_mutex_unlock(&m_handle); - } - - int tryLock() nothrow @nogc - { - return pthread_mutex_trylock(&m_handle) == 0; - } - - private pthread_mutex_t m_handle; - } - else static assert(0, "unsupported platform!"); -} \ No newline at end of file diff --git a/tests/access_perf.d b/tests/access_perf.d new file mode 100644 index 0000000..36da0c0 --- /dev/null +++ b/tests/access_perf.d @@ -0,0 +1,135 @@ +module tests.access_perf; + +import tests.runner; + +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.entity; + +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; + +import core.stdc.stdio; + +struct CLong +{ + mixin ECS.Component; + + alias value this; + + long value = 10; +} + +struct CInt +{ + mixin ECS.Component; + + alias value this; + + int value = 10; +} + +struct CUInt +{ + mixin ECS.Component; + + alias value this; + + uint value = 12; +} + +struct CBig +{ + mixin ECS.Component; + uint[32] data; +} + +EntityTemplate* tmpl; + +void beforeEveryTest() +{ + gEM.initialize(0); + + gEM.beginRegister(); + + gEM.registerComponent!CLong; + gEM.registerComponent!CInt; + gEM.registerComponent!CUInt; + gEM.registerComponent!CBig; + + gEM.endRegister(); + + tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray); + foreach(i; 0 .. 100_000)gEM.addEntity(tmpl); +} + +void afterEveryTest() +{ + if(tmpl)gEM.freeTemplate(tmpl); + tmpl = null; + gEM.destroy(); +} + +@("DirectAccess100k1comp") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + CUInt* comp1 = entity.getComponent!CUInt; + comp1.value = 4; + } +} + +@("DirectAccess100k4comp") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + CUInt* comp1 = entity.getComponent!CUInt; + comp1.value = 4; + CInt* comp2 = entity.getComponent!CInt; + comp2.value = 3; + CLong* comp3 = entity.getComponent!CLong; + comp3.value = 1; + CBig* comp4 = entity.getComponent!CBig; + comp4.data[0] = 2; + } +} + +@("DirectAccess100k1compWithMeta") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + EntityMeta meta = entity.getMeta(); + CUInt* comp1 = meta.getComponent!CUInt; + comp1.value = 4; + } +} + +@("DirectAccess100k4compWithMeta") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + EntityMeta meta = entity.getMeta(); + CUInt* comp1 = meta.getComponent!CUInt; + comp1.value = 4; + CInt* comp2 = meta.getComponent!CInt; + comp2.value = 3; + CLong* comp3 = meta.getComponent!CLong; + comp3.value = 1; + CBig* comp4 = meta.getComponent!CBig; + comp4.data[0] = 2; + } +} \ No newline at end of file diff --git a/tests/basic.d b/tests/basic.d index f3f2832..5046dff 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -1,11 +1,18 @@ module tests.basic; -import ecs.core; -import ecs.manager; -import ecs.system; -import ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.system; +import bubel.ecs.attributes; -import std.array : staticArray; +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; struct CInt { @@ -61,6 +68,11 @@ struct CUnregistered short value = 12; } +struct CFlag +{ + mixin ECS.Component; +} + struct LongAddSystem { mixin ECS.System; @@ -103,6 +115,7 @@ struct EmptySystem void beforeEveryTest() { + CUnregistered.component_id = ushort.max; gEM.initialize(0); gEM.beginRegister(); @@ -112,6 +125,7 @@ void beforeEveryTest() gEM.registerComponent!CDouble; gEM.registerComponent!CLong; gEM.registerComponent!CShort; + gEM.registerComponent!CFlag; gEM.endRegister(); } @@ -121,14 +135,33 @@ void afterEveryTest() gEM.destroy(); } +@("EntityMeta") +unittest +{ + EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + Entity* entity = gEM.addEntity(tmpl_); + EntityMeta meta = entity.getMeta(); + assert(meta.hasComponent(CInt.component_id)); + assert(meta.getComponent!CInt); + assert(meta.hasComponent(CFloat.component_id)); + assert(meta.getComponent!CFloat); + assert(!meta.getComponent!CLong); + assert(!meta.hasComponent(CLong.component_id)); + assert(!meta.getComponent!CUnregistered); + assert(!meta.hasComponent(CUnregistered.component_id)); + assert(*meta.getComponent!CInt == 1); + assert(*meta.getComponent!CFloat == 2.0); +} + @("AddEntity") unittest { - ushort[2] ids = [CInt.component_id, CFloat.component_id]; - EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); - assert(tmpl_.info.components.length == 2); + EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + assert(tmpl_.info.components.length == 3); + assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof)); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); + assert(tmpl_.getComponent!CFlag); assert(!tmpl_.getComponent!CLong); assert(!tmpl_.getComponent!CUnregistered); assert(*tmpl_.getComponent!CInt == 1); @@ -146,6 +179,67 @@ unittest assert(entity2.getComponent!CFloat); assert(*entity2.getComponent!CInt == 2); assert(*entity2.getComponent!CFloat == 2.0); + + //CInt cint = CInt(10); + //CLong clong; + //Entity* entity3 = gEM.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray); + Entity* entity3 = gEM.addEntity(tmpl_, [CInt(10).ref_, CLong().ref_, CFlag().ref_].staticArray); + EntityID id = entity3.id; + assert(entity3.hasComponent(CInt.component_id)); + assert(entity3.hasComponent(CFloat.component_id)); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + + gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CInt); + assert(entity3.getComponent!CFloat); + assert(entity3.getComponent!CFlag); + assert(entity3.getComponent!CShort); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + assert(*entity3.getComponent!CShort == 2); + + gEM.removeComponents(entity3.id, [CFlag().component_id,CShort(2).component_id].staticArray); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CInt); + assert(entity3.getComponent!CFloat); + assert(!entity3.getComponent!CFlag); + assert(!entity3.getComponent!CShort); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + + gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray); + gEM.removeComponents(entity3.id, [CUnregistered.component_id].staticArray); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CInt); + assert(entity3.getComponent!CFloat); + assert(entity3.getComponent!CFlag); + assert(entity3.getComponent!CShort); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + assert(*entity3.getComponent!CShort == 2); + + gEM.beginRegister(); + + gEM.registerComponent!CUnregistered; + + gEM.endRegister(); + + gEM.addComponents(entity3.id, [CUnregistered(4).ref_].staticArray); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CUnregistered); + assert(*entity3.getComponent!CUnregistered == 4); + + gEM.removeComponents(entity3.id, [CUnregistered.component_id].staticArray); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(!entity3.getComponent!CUnregistered); + } //allocate templates @@ -156,32 +250,48 @@ unittest ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); EntityTemplate* tmpl_d = gEM.allocateTemplate([CFloat.component_id, CInt.component_id, CFloat.component_id].staticArray); + EntityTemplate* tmpl_cp = gEM.allocateTemplate(tmpl_); assert(tmpl_d.info == tmpl_.info); + assert(tmpl_cp.info == tmpl_cp.info); assert(tmpl_.info.components.length == 2); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); + assert(tmpl_cp.getComponent!CFloat); + assert(tmpl_cp.getComponent!CInt); + assert(tmpl_.getComponent!CInt != tmpl_cp.getComponent!CInt); + assert(tmpl_.getComponent!CFloat != tmpl_cp.getComponent!CFloat); + assert(*tmpl_.getComponent!CInt == *tmpl_cp.getComponent!CInt); + assert(*tmpl_.getComponent!CFloat == *tmpl_cp.getComponent!CFloat); *tmpl_.getComponent!CInt = 4; *tmpl_.getComponent!CFloat = 5.0; - //allocate template from template with additional component - ushort[1] ids2 = [CDouble.component_id]; + //allocate template from template with additional components + ushort[2] ids2 = [CDouble.component_id,CFlag.component_id]; EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2); - assert(tmpl_2.info.components.length == 3); + assert(tmpl_2.info.components.length == 4); assert(tmpl_2.getComponent!CInt); assert(tmpl_2.getComponent!CFloat); assert(tmpl_2.getComponent!CDouble); + assert(tmpl_2.getComponent!CFlag); assert(*tmpl_2.getComponent!CInt == 4); assert(*tmpl_2.getComponent!CFloat == 5.0); assert(*tmpl_2.getComponent!CDouble == 3.0); + assert(tmpl_.info.blocksCount() == 0); + Entity* entity = gEM.addEntity(tmpl_); - gEM.addComponents(entity.id, CDouble(8.0)); + gEM.addComponents(entity.id, CFloat(100)); + gEM.addComponents(entity.id, CDouble(8.0), CFloat(100)); + + assert(tmpl_.info.blocksCount() == 1); //apply entity changes gEM.commit(); + assert(tmpl_.info.blocksCount() == 0); + //allocate template as entity copy EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id); assert(tmpl_3.info.components.length == 3); @@ -202,18 +312,20 @@ unittest assert(*tmpl_4.getComponent!CFloat == 2.0); assert(*tmpl_4.getComponent!CDouble == 3.0); - //allocate template from template with two additional component - ushort[2] ids3 = [CDouble.component_id, CLong.component_id]; - EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_, ids3); - assert(tmpl_5.info.components.length == 4); + //allocate template from template with three additional component + ushort[3] ids3 = [CDouble.component_id, CLong.component_id, CShort.component_id]; + EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_2, ids3); + assert(tmpl_5.info.components.length == 6); assert(tmpl_5.getComponent!CInt); assert(tmpl_5.getComponent!CFloat); assert(tmpl_5.getComponent!CDouble); assert(tmpl_5.getComponent!CLong); + assert(tmpl_5.getComponent!CShort); assert(*tmpl_5.getComponent!CInt == 4); assert(*tmpl_5.getComponent!CFloat == 5.0); assert(*tmpl_5.getComponent!CDouble == 3.0); assert(*tmpl_5.getComponent!CLong == 10); + assert(*tmpl_5.getComponent!CShort == 12); //allocate template from template without one component ushort[1] rem_ids = [CFloat.component_id]; @@ -224,7 +336,7 @@ unittest //allocate template from template without one component and two additional EntityTemplate* tmpl_7 = gEM.allocateTemplate(tmpl_, ids3, rem_ids); - assert(tmpl_7.info.components.length == 3); + assert(tmpl_7.info.components.length == 4); assert(tmpl_7.getComponent!CInt); assert(tmpl_7.getComponent!CDouble); assert(tmpl_7.getComponent!CLong); @@ -481,6 +593,10 @@ unittest gEM.endRegister(); + assert(gEM.getPass("custom")); + assert(!gEM.getPass("custommm")); + + LongAddSystem* system = gEM.getSystem!LongAddSystem; assert(system !is null); assert(system.updates_count == 0); @@ -1116,9 +1232,9 @@ unittest assert(*entity2.getComponent!CShort == 12); gEM.sendEvent(id,ETest()); - gEM.sendEvent(id,ETest2(id,10)); + gEM.sendEvent(id,ETest2(10)); gEM.sendEvent(id2,ETest()); - gEM.sendEvent(id2,ETest2(id2,12)); + gEM.sendEvent(id2,ETest2(12)); gEM.commit(); assert(ETest2.destory == 2); @@ -1129,20 +1245,20 @@ unittest gEM.addComponents(id, CInt(2), CShort(1)); gEM.sendEvent(id,ETest()); - gEM.sendEvent(id,ETest2(id,2)); + gEM.sendEvent(id,ETest2(2)); gEM.commit(); assert(ETest2.destory == 3); entity = gEM.getEntity(id); assert(*entity.getComponent!CLong == 66); - assert(*entity.getComponent!CInt == 36); + assert(*entity.getComponent!CInt == 2);//36); //test for multiple event blocks long result = *entity.getComponent!CLong; foreach(i;0..10000) { gEM.sendEvent(id,ETest()); - gEM.sendEvent(id,ETest2(id,4)); + gEM.sendEvent(id,ETest2(4)); result += 16; result += 8; } @@ -1220,4 +1336,247 @@ unittest assert(*entity2.getComponent!CInt == 13); gEM.end(); +} + +@("SystemDependencies") +unittest +{ + struct TestSystem + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem2 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem3 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem4 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + CInt[] int_; + CLong[] long_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem5 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @readonly CLong[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + gEM.beginRegister(); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + gEM.registerSystem!TestSystem3(2); + gEM.registerSystem!TestSystem4(3); + gEM.registerSystem!TestSystem5(4); + + gEM.endRegister(); + + const (EntityManager.UpdatePass)* pass = gEM.getPass("update"); + assert(pass != null); + assert(pass.system_callers.length == 5); + assert(pass.system_callers[0].system_id == TestSystem.system_id); + assert(pass.system_callers[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[3].system_id == TestSystem4.system_id); + assert(pass.system_callers[4].system_id == TestSystem5.system_id); + assert(pass.system_callers[0].dependencies.length == 0); + assert(pass.system_callers[1].dependencies.length == 1); + assert(pass.system_callers[2].dependencies.length == 1); + assert(pass.system_callers[3].dependencies.length == 3); + assert(pass.system_callers[4].dependencies.length == 1); + assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[4].dependencies[0].system_id == TestSystem4.system_id); +} + +@("ExternalSystemDependencies") +unittest +{ + enum TestDependency = "TestDepencency"; + + struct TestSystem + { + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem2 + { + mixin ECS.System; + + mixin ECS.WritableDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem3 + { + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(TestDependency); + + struct EntitiesData + { + uint thread_id; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem4 + { + mixin ECS.System; + + mixin ECS.WritableDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem5 + { + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CLong[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + gEM.beginRegister(); + + gEM.registerDependency(TestDependency); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + gEM.registerSystem!TestSystem3(2); + gEM.registerSystem!TestSystem4(3); + gEM.registerSystem!TestSystem5(4); + + gEM.endRegister(); + + const (EntityManager.UpdatePass)* pass = gEM.getPass("update"); + assert(pass != null); + assert(pass.system_callers.length == 5); + assert(pass.system_callers[0].system_id == TestSystem.system_id); + assert(pass.system_callers[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[3].system_id == TestSystem4.system_id); + assert(pass.system_callers[4].system_id == TestSystem5.system_id); + assert(pass.system_callers[0].dependencies.length == 0); + assert(pass.system_callers[1].dependencies.length == 1); + assert(pass.system_callers[2].dependencies.length == 1); + assert(pass.system_callers[3].dependencies.length == 3); + assert(pass.system_callers[4].dependencies.length == 2); + assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[4].dependencies[0].system_id == TestSystem2.system_id); + assert(pass.system_callers[4].dependencies[1].system_id == TestSystem4.system_id); } \ No newline at end of file diff --git a/tests/bugs.d b/tests/bugs.d new file mode 100644 index 0000000..4c4153a --- /dev/null +++ b/tests/bugs.d @@ -0,0 +1,142 @@ +module tests.bugs; + +import tests.basic; + +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.system; +import bubel.ecs.attributes; + +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; + +@("Bug0001") +unittest +{ + struct Event1 + { + mixin ECS.Event; + + EntityID id; + } + + struct Event2 + { + mixin ECS.Event; + } + + struct System1 + { + mixin ECS.System; + + struct EntitiesData + { + CInt[] int_; + } + + EntityTemplate* tmpl; + EntityID id; + + void onCreate() + { + tmpl = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray); + } + + void onDestroy() + { + gEM.freeTemplate(tmpl); + } + + void handleEvent(Entity* entity, Event1 event) + { + gEM.removeEntity(event.id); + gEM.sendEvent(entity.id,Event2()); + } + + void handleEvent(Entity* entity, Event2 event) + { + id = gEM.addEntity(tmpl).id; + } + } + + struct System2 + { + mixin ECS.System; + + struct EntitiesData + { + Entity[] entity; + } + + ///check if every entity was removed correctly + void onUpdate(EntitiesData data) + { + assert(0); + } + } + + struct System3 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + Entity[] entity; + } + + ///remove every entity + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length)gEM.removeEntity(data.entity[i].id); + } + } + + gEM.initialize(0); + + gEM.beginRegister(); + + gEM.registerComponent!CInt; + gEM.registerComponent!CFloat; + gEM.registerComponent!CDouble; + gEM.registerComponent!CLong; + gEM.registerComponent!CShort; + gEM.registerComponent!CFlag; + + gEM.registerEvent!Event1; + gEM.registerEvent!Event2; + + gEM.registerSystem!System1(0); + gEM.registerSystem!System2(-200); + gEM.registerSystem!System3(-200); + + gEM.endRegister(); + + EntityTemplate* tmpl = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray); + EntityID id = gEM.addEntity(tmpl,[CLong(10).ref_, CInt(6).ref_].staticArray).id; + EntityID id2 = gEM.addEntity(tmpl,[CInt(4).ref_].staticArray).id; + gEM.freeTemplate(tmpl); + gEM.commit(); + + gEM.sendEvent(id2, Event1(id)); + + gEM.getSystem(System2.system_id).disable(); + + gEM.begin(); + gEM.update(); + gEM.end(); + + gEM.getSystem(System2.system_id).enable(); + + gEM.begin(); + gEM.update(); + gEM.end(); + + gEM.destroy(); +} \ No newline at end of file diff --git a/tests/id_manager.d b/tests/id_manager.d index afaafe5..b0f97d5 100644 --- a/tests/id_manager.d +++ b/tests/id_manager.d @@ -1,7 +1,7 @@ module tests.id_manager; -import ecs.id_manager; -import ecs.entity; +import bubel.ecs.id_manager; +import bubel.ecs.entity; unittest { diff --git a/tests/map.d b/tests/map.d new file mode 100644 index 0000000..a82985b --- /dev/null +++ b/tests/map.d @@ -0,0 +1,53 @@ +module tests.map; + +import bubel.ecs.hash_map; + +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; + +@("HashMap") +unittest +{ + HashMap!(string, int) map; + + assert(map.length == 0); + map.add("asd",1); + assert(map.length == 1); + map.clear(); + assert(map.length == 0); + map.add("asd",1); + assert(map.length == 1); + map.reset(); + assert(map.length == 0); + + map.add("asd",1); + string asd = "asd"; + assert(map.isIn("asd")); + assert(map.isIn(asd)); + assert(!map.isIn("asdf")); + map.tryRemove("asdf"); + map.tryRemove("asd"); + assert(map.length == 0); + map.add("asdf",1); + map.add("asd",2); + assert(map["asd"] == 2); + assert(map["asdf"] == 1); + assert(map.length == 2); + map.tryRemove("asdf"); + assert(map.length == 1); + map.remove("asd"); + assert(map.length == 0); + + map.add("asd",1); + map.add("asdwwghe",6); + foreach(k,v;&map.byKeyValue) + { + assert(map[k] == v); + } +} \ No newline at end of file diff --git a/tests/perf.d b/tests/perf.d new file mode 100644 index 0000000..b6a2692 --- /dev/null +++ b/tests/perf.d @@ -0,0 +1,185 @@ +module tests.perf; + +import tests.runner; + +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.entity; + +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; + +import core.stdc.stdio; + +struct CLong +{ + mixin ECS.Component; + + alias value this; + + long value = 10; +} + +struct CShort +{ + mixin ECS.Component; + + alias value this; + + short value = 12; +} + +struct CInt +{ + mixin ECS.Component; + + alias value this; + + int value = 10; +} + +struct CUInt +{ + mixin ECS.Component; + + alias value this; + + uint value = 12; +} + +struct CBig +{ + mixin ECS.Component; + uint[32] data; +} + +EntityTemplate* tmpl; + +void beforeEveryTest() +{ + gEM.initialize(0); + + gEM.beginRegister(); + + gEM.registerComponent!CLong; + gEM.registerComponent!CShort; + gEM.registerComponent!CInt; + gEM.registerComponent!CUInt; + gEM.registerComponent!CBig; + + gEM.endRegister(); + tmpl = null; +} + +void afterEveryTest() +{ + if(tmpl)gEM.freeTemplate(tmpl); + tmpl = null; + gEM.destroy(); +} + +void smallTmpl() +{ + tmpl = gEM.allocateTemplate([CShort.component_id].staticArray); +} + +void bigTmpl() +{ + tmpl = gEM.allocateTemplate([CBig.component_id].staticArray); +} + +void multiSmallTmpl() +{ + tmpl = gEM.allocateTemplate([CShort.component_id, CLong.component_id, CInt.component_id, CUInt.component_id].staticArray); +} + +void multiBigTmpl() +{ + tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray); +} + +@("AddEntities100k1comp2b") @(before, &smallTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k1comp128b") @(before, &bigTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp18b") @(before, &multiSmallTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp144b") @(before, &multiBigTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +void allocDealloc100k() +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); + gEM.commit(); + foreach(i; 0..100_000)gEM.removeEntity(EntityID(i + 1,0)); + gEM.commit(); +} + +void smallTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CShort.component_id].staticArray); + allocDealloc100k(); +} + +void bigTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CBig.component_id].staticArray); + allocDealloc100k(); +} + +void multiSmallTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CShort.component_id, CLong.component_id, CInt.component_id, CUInt.component_id].staticArray); + allocDealloc100k(); +} + +void multiBigTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray); + allocDealloc100k(); +} + +@("AddEntities100k1comp2bPreAlloc") @(before, &smallTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k1comp128bPreAlloc") @(before, &bigTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp18bPreAlloc") @(before, &multiSmallTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp144bPreAlloc") @(before, &multiBigTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} \ No newline at end of file diff --git a/tests/runner.d b/tests/runner.d index 5eaf99b..7e3dd07 100644 --- a/tests/runner.d +++ b/tests/runner.d @@ -5,9 +5,22 @@ import core.stdc.stdio; import core.stdc.string; import core.sys.posix.setjmp; -import ecs.vector; -import ecs.simple_vector; -import ecs.std; +import bubel.ecs.vector; +import bubel.ecs.simple_vector; +import bubel.ecs.std; + +import std.traits; + +import tests.time; + +version (LDC) +{ + import ldc.attributes; +} +else +{ + enum optStrategy = 0; +} enum int ASSERTED = 123; enum string OUT_FILE = "test_report.xml"; @@ -95,6 +108,8 @@ struct TestRunner(Args...) write(test.name); write("\" classname=\""); write(suite.name); + write("\" time=\""); + write(cast(double)test.time*0.000001); write("\">"); if (test.msg) { @@ -120,42 +135,65 @@ struct TestRunner(Args...) write(""); } + @(optStrategy,"none") void runTests(string[] include = null, string[] exclude = null) { foreach (i, module_; Args) { TestSuite* suite = &suites[i]; suite.name = module_.stringof; + + void function() before; + void function() after; + foreach (index, unittest_; __traits(getUnitTests, module_)) { enum attributes = __traits(getAttributes, unittest_); + static if (attributes.length != 0) { - if (include.length > 0) + foreach(attr_id, attr; attributes) { - bool matched = false; - foreach (str; include) + static if(isFunctionPointer!(attr)){} + else static if(attr == "_tr_before") { - if (match(str, attributes[0])) - { - matched = true; - break; - } + static assert(attr_id+1 < attributes.length); + enum attr2 = attributes[attr_id + 1]; + //static assert(__traits(hasMember, module_, attr2)); + //alias func = __traits(getMember, module_, attr2); + //attr2(); + before = attr2; + //static assert(is(typeof(__traits(getMember, module_, attr2)) == void function())); } - - foreach (str; exclude) + else { - if (match(str, attributes[0])) + if (include.length > 0) { - matched = false; - break; - } - } + bool matched = false; + foreach (str; include) + { + if (match(str, attr)) + { + matched = true; + break; + } + } - if (!matched) - { - suite.skipped++; - continue; + foreach (str; exclude) + { + if (match(str, attr)) + { + matched = false; + break; + } + } + + if (!matched) + { + suite.skipped++; + continue; + } + } } } } @@ -172,8 +210,9 @@ struct TestRunner(Args...) else test.name = attributes[0]; - static if (__traits(hasMember, module_, "beforeEveryTest")) + static if (__traits(hasMember, module_, "beforeEveryTest") && __traits(compiles, module_.beforeEveryTest())) module_.beforeEveryTest(); + if(before)before(); version(D_BetterC) { @@ -189,8 +228,10 @@ struct TestRunner(Args...) } else { + long time = Time.getUSecTime(); unittest_(); test.passed = true; + test.time = cast(int)(Time.getUSecTime() - time); } } else @@ -215,7 +256,7 @@ struct TestRunner(Args...) else suite.failed++; suite.tests ~= test; - static if (__traits(hasMember, module_, "afterEveryTest")) + static if (__traits(hasMember, module_, "afterEveryTest") && __traits(compiles, module_.beforeEveryTest())) module_.afterEveryTest(); } passed += suite.passed; @@ -281,6 +322,14 @@ struct TestRunner(Args...) junit.add(cast(ubyte[]) txt); } + void write(double num) + { + ubyte[40] buffer; + int len; + len = sprintf(cast(char*) buffer.ptr, "%2.8lf", num); + junit.add(buffer[0 .. len]); + } + void write(uint num) { ubyte[20] buffer; @@ -304,6 +353,8 @@ version (notBetterC) version (D_Coverage) extern (C) void dmd_coverDestPath(string path); } +enum before = "_tr_before"; + void extractStrings(ref Vector!string container, string str) { uint s = 0; @@ -369,7 +420,7 @@ extern (C) int main(int argc, char** args) } } - TestRunner!(tests.runner, tests.id_manager, tests.vector, tests.basic) runner; + TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf, tests.bugs, tests.map) runner; runner.runTests(include[], exclude[]); diff --git a/tests/tests.d b/tests/tests.d index ea554d1..d923116 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -3,12 +3,12 @@ module tests.tests; import std.experimental.allocator; import std.experimental.allocator.mallocator;*/ -import ecs.entity; -import ecs.events; -import ecs.manager; -import ecs.system; -import ecs.attributes; -import ecs.core; +import bubel.ecs.entity; +import bubel.ecs.events; +import bubel.ecs.manager; +import bubel.ecs.system; +import bubel.ecs.attributes; +import bubel.ecs.core; version (WebAssembly) { @@ -714,7 +714,7 @@ else: //foreach(i; 0..1_000_000)gEM.removeEntity(gEM.addEntity(tmpl).id); - import ecs.std; + import bubel.ecs.std; EntityID[] idss = Mallocator.makeArray!EntityID(5000); //[5000] //scope(exit)Mallocator.dispose(idss); diff --git a/tests/time.d b/tests/time.d new file mode 100644 index 0000000..e91b76e --- /dev/null +++ b/tests/time.d @@ -0,0 +1,66 @@ +module tests.time; + + +version (WebAssembly) +{ + alias int time_t; + alias int clockid_t; + enum CLOCK_REALTIME = 0; + + struct timespec + { + time_t tv_sec; + int tv_nsec; + } + + extern (C) int clock_gettime(clockid_t, timespec*) @nogc nothrow @system; + + struct Time + { + static long getUSecTime() + { + time_t time; + timespec spec; + + clock_gettime(CLOCK_REALTIME, &spec); + + //time = spec.tv_sec; + return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; //time / 1000_000; + } + } +} +else version (Windows) +{ + import core.stdc.stdio : printf; + import core.sys.windows.windows; + + struct Time + { + static long getUSecTime() + { + LARGE_INTEGER time, freq; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&time); + return time.QuadPart / (freq.QuadPart / 1000_000); + } + } +} +else version (Posix) +{ + import core.stdc.stdio : printf; + import core.sys.posix.time; + + struct Time + { + static long getUSecTime() + { + time_t time; + timespec spec; + + clock_gettime(CLOCK_REALTIME, &spec); + + //time = spec.tv_sec; + return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; //time / 1000_000; + } + } +} \ No newline at end of file diff --git a/tests/vector.d b/tests/vector.d index 4b2ad5b..1756518 100644 --- a/tests/vector.d +++ b/tests/vector.d @@ -1,7 +1,16 @@ module tests.vector; -import ecs.simple_vector; -//import ecs.vector; +import bubel.ecs.simple_vector; +import bubel.ecs.vector; + +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; @("simple-vector") unittest @@ -33,3 +42,38 @@ unittest assert(vector2[1023] == 'a'); assert(vector2[1024] == 'b'); } + +@("Vector") +unittest +{ + struct G + { + int a; + } + + Vector!G vector; + assert(vector.empty()); + vector.add(G(1)); + assert(!vector.empty()); + vector.clear(); + assert(vector.empty()); + vector.add(G(1)); + assert(!vector.empty()); + vector.reset(); + assert(vector.empty()); + + vector.add(G(1)); + vector.add([G(2),G(5)].staticArray); + assert(vector.length == 3); + assert(vector.capacity == 1); + + Vector!G vector2; + vector2.add([G(1),G(2),G(5)].staticArray); + assert(vector == vector2); + vector2.remove(1); + assert(vector != vector2); + assert(vector2.length == 2); + assert(vector2[1] == G(5)); + vector2.add(G(2),1); + assert(vector == vector2); +}