From 72617bc2a58225f0367762316d98740b2d9ef2d4 Mon Sep 17 00:00:00 2001 From: Dawid Masiukiewicz Date: Tue, 14 Apr 2020 18:37:00 +0000 Subject: [PATCH 1/4] Added pipeline status indicator to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 08cab68..76fff75 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # Dynamic Entity Component System +[![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) +[![coverage report](https://gitlab.com/Mergul/bubel-ecs/badges/master/coverage.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) Entity-Component-System implementation in D language. \ No newline at end of file From d68eecf2725d457fb66c52091abf58a8341114b6 Mon Sep 17 00:00:00 2001 From: Dawid Masiukiewicz Date: Fri, 24 Apr 2020 20:10:23 +0000 Subject: [PATCH 2/4] Add LICENSE --- LICENSE | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ede0cb3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Dawid Masiukiewicz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From b434d851edab42452ffbcc847fa7b6ebd5608c5e Mon Sep 17 00:00:00 2001 From: Dawid Masiukiewicz Date: Sat, 25 Apr 2020 11:09:35 +0000 Subject: [PATCH 3/4] CIUpdate - added build test - probably fixed some trash - coverage test should only wait for test_dmd_debug --- .gitlab-ci.yml | 59 +++++++++++++++++++++++++++++++++++++++++--- README.md | 4 +-- codecov.yml | 3 +++ source/ecs/manager.d | 11 +++++---- 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 codecov.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 25b0820..cccf254 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,60 @@ -test_dmd: - stage: test - image: mmcomando/ecs_enviroment +image: "registry.gitlab.com/mergul/bubel-ecs:latest" + +variables: + DOCKER_DRIVER: overlay2 + +stages: + - build + - test + - testcov + +test_compile: + stage: build script: - source $(/script/dlang/install.sh dmd -a) && dmd --version - - source $(/script/dlang/install.sh dmd -a) && dub -c unittest-runner -b unittest + - dub build -c unittest-runner -b debug --verror + - dub build -c unittest-runner -b release --verror + - dub build -c unittest-runner-betterC -b debug --verror + - dub build -c unittest-runner-betterC -b release --verror + - deactivate + - source $(/script/dlang/install.sh ldc -a) && ldc2 --version + - dub build -c unittest-runner --compiler=ldc2 -b debug --verror + - dub build -c unittest-runner --compiler=ldc2 -b release --verror + - dub build -c unittest-runner-betterC --compiler=ldc2 -b debug --verror + - dub build -c unittest-runner-betterC --compiler=ldc2 -b release --verror + - deactivate + allow_failure: true + +test_dmd_debug: + stage: test + script: + - source $(/script/dlang/install.sh dmd -a) && dmd --version + - dub -c unittest-runner -b debug --verror artifacts: reports: junit: test_report.xml +test_dmd: + stage: test + script: + - source $(/script/dlang/install.sh dmd -a) && dmd --version + - dub -c unittest-runner -b release --verror + artifacts: + reports: + junit: test_report.xml +test_dmd_betterC: + stage: test + script: + - source $(/script/dlang/install.sh dmd -a) && dmd --version + - dub -c unittest-runner-betterC -b release --verror + artifacts: + reports: + junit: test_report.xml +coverage_test_dmd: + stage: testcov + needs: ["test_dmd_debug"] + script: + - mkdir reports + - source $(/script/dlang/install.sh dmd -a) && dmd --version + - dub -c unittest-runner-cov -b debug --verror + after_script: + - bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c diff --git a/README.md b/README.md index 76fff75..d3f6816 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Dynamic Entity Component System [![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) -[![coverage report](https://gitlab.com/Mergul/bubel-ecs/badges/master/coverage.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. \ No newline at end of file +Entity-Component-System implementation in D language. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..a6afdbc --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +ignore: + - "tests/*" + - "**/traits*" \ No newline at end of file diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 1b9b959..5ec5b87 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -442,11 +442,12 @@ export struct EntityManager static struct ComponentsCounts { - uint readonly; - uint mutable; - uint excluded; - uint optional; - uint req; + //one more than should be to prevent null arrays (zero length arrays) + uint readonly = 1; + uint mutable = 1; + uint excluded = 1; + uint optional = 1; + uint req = 1; } static ComponentsCounts getComponentsCounts()() From 54a6d5dec27335f50f8c0de342991f8db7f5bc83 Mon Sep 17 00:00:00 2001 From: Dawid Masiukiewicz Date: Fri, 1 May 2020 19:26:21 +0000 Subject: [PATCH 4/4] CI and common update: -added webpage deploymnet stage -added separate build stage which build all binaries and generate documentation -added Emscripten build stage for merge to master only -added VBO batch rendering (current default, no render mode switch yet) -fixed camera positioning calculation -fixed buffer issue with WebGL -added viewport scalling (at least 300 pixels height). Pixels are scalled if screen is bigger. -center demos gameplay area -added fullpage html template for Emscripten build --- .gitignore | 2 + .gitlab-ci.yml | 87 +++-- demos/.gitignore | 2 + demos/assets/textures/atlas.png | Bin 17825 -> 24668 bytes demos/compile_wasm.py | 42 ++- demos/emscripten_multi_shell.html | 170 +++++++++ demos/emscripten_shell.html | 154 ++++++++ demos/external/sources/mmutils/thread_pool.d | 10 +- demos/source/app.d | 15 +- demos/source/demos/simple.d | 15 +- demos/source/demos/snake.d | 347 +++++++++++++++++-- demos/source/demos/space_invaders.d | 47 ++- demos/utils/source/ecs_utils/gfx/buffer.d | 6 +- demos/utils/source/ecs_utils/gfx/renderer.d | 28 +- demos/utils/source/ecs_utils/gfx/texture.d | 1 + demos/utils/source/ecs_utils/math/vector.d | 19 + dub.json | 2 +- skeleton.html | 36 ++ source/ecs/atomic.d | 11 +- source/ecs/attributes.d | 33 +- source/ecs/block_allocator.d | 21 +- source/ecs/core.d | 89 +++-- source/ecs/entity.d | 31 +- source/ecs/events.d | 10 +- source/ecs/id_manager.d | 18 +- source/ecs/manager.d | 237 +++++++------ source/ecs/std.d | 7 +- source/ecs/system.d | 47 +-- source/ecs/traits.d | 2 +- 29 files changed, 1167 insertions(+), 322 deletions(-) create mode 100644 demos/emscripten_multi_shell.html create mode 100644 demos/emscripten_shell.html create mode 100644 skeleton.html diff --git a/.gitignore b/.gitignore index 8a5c5c8..e595637 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ !README.md !./dub.json !.gitignore +!codecov.yml +!skeleton.html !meson.build !meson_options.txt !compile_wasm.py \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cccf254..14ba23c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,60 +1,91 @@ -image: "registry.gitlab.com/mergul/bubel-ecs:latest" - -variables: - DOCKER_DRIVER: overlay2 - stages: - build - test - testcov + - build_emscripten + - deploy -test_compile: +build_code: stage: build + image: "registry.gitlab.com/mergul/bubel-ecs:latest" script: - - source $(/script/dlang/install.sh dmd -a) && dmd --version - - dub build -c unittest-runner -b debug --verror - - dub build -c unittest-runner -b release --verror - - dub build -c unittest-runner-betterC -b debug --verror - - dub build -c unittest-runner-betterC -b release --verror - - deactivate - - source $(/script/dlang/install.sh ldc -a) && ldc2 --version - - dub build -c unittest-runner --compiler=ldc2 -b debug --verror - - dub build -c unittest-runner --compiler=ldc2 -b release --verror - - dub build -c unittest-runner-betterC --compiler=ldc2 -b debug --verror - - dub build -c unittest-runner-betterC --compiler=ldc2 -b release --verror - - deactivate + - 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 allow_failure: true test_dmd_debug: stage: test + image: frolvlad/alpine-glibc script: - - source $(/script/dlang/install.sh dmd -a) && dmd --version - - dub -c unittest-runner -b debug --verror + - build/dmd_debug_unittest artifacts: reports: junit: test_report.xml test_dmd: stage: test + image: frolvlad/alpine-glibc script: - - source $(/script/dlang/install.sh dmd -a) && dmd --version - - dub -c unittest-runner -b release --verror + - build/dmd_release_unittest artifacts: reports: junit: test_report.xml test_dmd_betterC: - stage: test + stage: test + image: frolvlad/alpine-glibc script: - - source $(/script/dlang/install.sh dmd -a) && dmd --version - - dub -c unittest-runner-betterC -b release --verror + - build/dmd_release_unittest_bc artifacts: reports: junit: test_report.xml + coverage_test_dmd: stage: testcov - needs: ["test_dmd_debug"] + image: "registry.gitlab.com/mergul/bubel-ecs/curl:latest" + needs: ["test_dmd_debug", "build_code"] + dependencies: + - build_code script: - mkdir reports - - source $(/script/dlang/install.sh dmd -a) && dmd --version - - dub -c unittest-runner-cov -b debug --verror + - build/dmd_unittest_cov after_script: - bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c + +emscripten: + stage: build_emscripten + image: "registry.gitlab.com/mergul/bubel-ecs/emscripten:latest" + dependencies: + - build_code + script: + - /bin/bash /build.sh + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + when: always + artifacts: + expire_in: 1h + paths: + - wasm + +pages: + stage: deploy + image: frolvlad/alpine-glibc + script: + - mkdir public + - cp -r wasm/* public/ + - cp -r build/public/* public/ + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + when: always + artifacts: + expire_in: 1h + paths: + - public \ No newline at end of file diff --git a/demos/.gitignore b/demos/.gitignore index 30c9e9c..1ac2a4a 100644 --- a/demos/.gitignore +++ b/demos/.gitignore @@ -12,4 +12,6 @@ !.gitignore !compile_wasm.py !cimgui.bc +!emscripten_shell.html +!emscripten_multi_shell.html .dub \ No newline at end of file diff --git a/demos/assets/textures/atlas.png b/demos/assets/textures/atlas.png index 498f34dd82959385bc9a96199c88707d782b46a0..c628969c0e7149419eb62313fb5c3274457838f3 100644 GIT binary patch literal 24668 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 literal 17825 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 + + + + + ECS Demo + + + + +
emscripten
+
Downloading...
+
+ +
+
+ +
+ + +
+ +
+ + + + diff --git a/demos/emscripten_shell.html b/demos/emscripten_shell.html new file mode 100644 index 0000000..a657766 --- /dev/null +++ b/demos/emscripten_shell.html @@ -0,0 +1,154 @@ + + + + + + ECS Demo + + + + +
emscripten
+
Downloading...
+
+ +
+
+ +
+ + +
+ +
+ + {{{ SCRIPT }}} + + diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index a5fbf91..374fbc1 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -12,6 +12,8 @@ import std.algorithm : map; version = MM_NO_LOGS; // Disable log creation //version = MM_USE_POSIX_THREADS; // Use posix threads insted of standard library, required for betterC +version (Posix)version = MM_USE_POSIX_THREADS; + version (WebAssembly) { extern(C) struct FILE @@ -374,7 +376,8 @@ version (MM_USE_POSIX_THREADS) { threadStart = dg; int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - assert(ok == 0); + if(!ok)handle = pthread_t(); + //assert(ok == 0); } void join() @@ -459,7 +462,8 @@ else version(D_BetterC) { threadStart = dg; int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - assert(ok == 0); + if(!ok)handle = pthread_t(); + //assert(ok == 0); } void join() @@ -567,7 +571,7 @@ else version(D_BetterC) threadStart = dg; handle = cast(HANDLE) _beginthreadex( null, 0, &threadRunFunc, cast(void*)&this, 0, null ); //int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - assert(handle != null); + //assert(handle != null); } void join() diff --git a/demos/source/app.d b/demos/source/app.d index 6338035..150d653 100644 --- a/demos/source/app.d +++ b/demos/source/app.d @@ -51,6 +51,7 @@ struct Launcher void function() end; void function(SDL_Event*) event; void function(vec2, Tool, int) tool; + float scalling; ivec2 window_size = ivec2(1024,768); Renderer renderer; ubyte[] keys; @@ -60,6 +61,7 @@ struct Launcher ulong timer_freq; double delta_time; uint fps; + vec2 render_position; Tool used_tool; int tool_size = 0; @@ -229,7 +231,7 @@ void mainLoop(void* arg) } if(launcher.tool && event.button.button == SDL_BUTTON_LEFT && launcher.tool_repeat == 0 && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow)) { - launcher.tool(vec2(event.button.x, launcher.window_size.y - event.button.y), launcher.used_tool, launcher.tool_size); + launcher.tool(vec2(event.button.x, launcher.window_size.y - event.button.y) * launcher.scalling - launcher.render_position, launcher.used_tool, launcher.tool_size); } } else if(event.type == SDL_MOUSEBUTTONUP) @@ -255,7 +257,7 @@ void mainLoop(void* arg) while(launcher.repeat_time > range) { launcher.repeat_time -= range; - launcher.tool(launcher.mouse.position, launcher.used_tool, launcher.tool_size); + launcher.tool((launcher.mouse.position*launcher.scalling)-launcher.render_position, launcher.used_tool, launcher.tool_size); } } @@ -525,7 +527,14 @@ void mainLoop(void* arg) } launcher.renderer.resize(launcher.window_size); - launcher.renderer.view(vec2(0,0),vec2(launcher.window_size.x,launcher.window_size.y)); + //launcher.renderer.view(vec2(0,0),vec2(launcher.window_size.x,launcher.window_size.y)); + //if(384, 768, 1152, 1536) + //576 960 1344 1728 + //float scalling; + if(launcher.window_size.y < 360)launcher.scalling = 1; + else launcher.scalling = 1.0 / ((launcher.window_size.y+120)/360); + launcher.renderer.view(launcher.render_position,vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling); + //launcher.renderer.view(vec2(0,0),vec2(1024*launcher.window_size.x/launcher.window_size.y,768)); //glClear(GL_COLOR_BUFFER_BIT); launcher.renderer.clear(); diff --git a/demos/source/demos/simple.d b/demos/source/demos/simple.d index 36187c8..edd20c6 100644 --- a/demos/source/demos/simple.d +++ b/demos/source/demos/simple.d @@ -60,7 +60,7 @@ struct DrawSystem { foreach(i; 0..data.length) { - launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(32,32), 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), 0, 0 , 0); //draw(renderer, data.textures[i].tex, data.locations[i], vec2(32,32), vec4(0,0,1,1)); } } @@ -81,7 +81,7 @@ struct MoveSystem foreach(i; 0..data.length) { data.locations[i].location.y = data.locations[i].location.y + 1; - if(data.locations[i].location.y > 400)data.locations[i].location.y = 0; + if(data.locations[i].location.y > 300)data.locations[i].location.y = 0; } } } @@ -119,7 +119,7 @@ void simpleStart() foreach(i; 0..10) foreach(j; 0..10) { - loc_comp.location = vec2(i*32+64,j*32+64); + loc_comp.location = vec2(i*16+64,j*16+64); launcher.manager.addEntity(simple.tmpl); } } @@ -147,6 +147,10 @@ void simpleTool(vec2 position, Tool tool, int size) { position.x += (randomf - 0.5) * size; position.y += (randomf - 0.5) * size; + if(position.x > 400)position.x -= 400; + else if(position.x < 0)position.x += 400; + if(position.y > 300)position.y -= 300; + else if(position.y < 0)position.y += 300; *location = position; } launcher.manager.addEntity(tmpl); @@ -169,17 +173,18 @@ void simpleEvent(SDL_Event* event) void spawnEntity() { CLocation* loc_comp = simple.tmpl.getComponent!CLocation; - loc_comp.location = vec2(randomf() * 600,0); + loc_comp.location = vec2(randomf() * 400,0); launcher.manager.addEntity(simple.tmpl); } bool simpleLoop() { + launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5; + if(launcher.getKeyState(SDL_SCANCODE_SPACE)) { foreach(i;0..1)spawnEntity(); } - launcher.manager.begin(); if(launcher.multithreading) diff --git a/demos/source/demos/snake.d b/demos/source/demos/snake.d index f43c5a5..6bb8cef 100644 --- a/demos/source/demos/snake.d +++ b/demos/source/demos/snake.d @@ -17,6 +17,10 @@ import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +import std.array : staticArray; + +enum float px = 1.0/512.0; + extern(C): struct MapElement @@ -24,9 +28,24 @@ struct MapElement enum Type { empty = 0, - snake = 1, - apple = 2, - wall = 3 + apple = 1, + wall = 2, + + snake_head_up = 5, + snake_head_down = 6, + snake_head_left = 7, + snake_head_right = 8, + snake_tail_up = 9, + snake_tail_down = 10, + snake_tail_left = 11, + snake_tail_right = 12, + snake_turn_ld = 13, + snake_turn_lu = 14, + snake_turn_rd = 15, + snake_turn_ru = 16, + snake_vertical = 17, + snake_horizontal = 18 + } Type type; EntityID id; @@ -38,8 +57,13 @@ struct Snake EntityTemplate* apple_tmpl; EntityTemplate* snake_tmpl; + EntityTemplate* snake_destroy_particle; Texture texture; + vec4[] snake_destroy_particle_frames; + vec4[] smoke_frames; + + bool move_system = true; bool draw_system = true; @@ -83,16 +107,30 @@ struct Snake void drawMap() { - const float px = 1.0/512.0; 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*32,y*32), vec2(32,32), vec4(0,32*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.snake:launcher.renderer.draw(texture, vec2(x*32,y*32), vec2(32,32), vec4(0,48*px,16*px,16*px), 0, 0 , 0);break; - case MapElement.Type.wall:launcher.renderer.draw(texture, vec2(x*32,y*32), vec2(32,32), vec4(0,0,1,1), 0, 0 , 0);break; + 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; } } @@ -101,6 +139,19 @@ struct Snake } +struct Animation +{ + +} + +struct CAnimation +{ + mixin ECS.Component; + + vec4[] frames; + float time = 0; +} + struct CILocation { mixin ECS.Component; @@ -116,7 +167,7 @@ struct CLocation alias location this; - vec2 location; + vec2 location = vec2(0,0); } struct CSnake @@ -176,13 +227,15 @@ struct CApple struct CParticle { mixin ECS.Component; + + float life = 0; } struct CParticleVector { mixin ECS.Component; - vec2 velocity; + vec2 velocity = vec2(0,0); } struct CMovement @@ -226,10 +279,98 @@ struct AppleSystem } } +struct ParticleSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + @readonly Entity[] entities; + @readonly 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.entities[i].id); + } + } +} + +struct ParticleMovementSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + @readonly Entity[] entities; + @readonly CParticleVector[] movement; + CLocation[] location; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.location[i].location -= data.movement[i].velocity; + } + } +} + + +struct AnimationSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + CAnimation[] animation; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.animation[i].time += launcher.delta_time * 0.01; + while(data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length; + } + } +} + + +struct AnimationRenderSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + @readonly CAnimation[] animation; + @readonly CLocation[] location; + } + + void onUpdate(EntitiesData data) + { + 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); + } + } +} + struct MoveSystem { mixin ECS.System!64; + EntityTemplate* destroy_template; + CLocation* destroy_location; + CParticleVector* destroy_vector; + struct EntitiesData { uint length; @@ -239,6 +380,13 @@ struct MoveSystem CILocation[] location; } + void setTemplates() + { + destroy_template = snake.snake_destroy_particle; + destroy_location = destroy_template.getComponent!CLocation; + destroy_vector = destroy_template.getComponent!CParticleVector; + } + void moveLocation(ref CILocation location, CMovement.Direction direction) { final switch(direction) @@ -276,6 +424,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) @@ -286,21 +484,104 @@ struct MoveSystem moveLocation(data.location[i], data.movement[i].direction); final switch(snake.element(data.location[i].location).type) { - case MapElement.Type.snake: - launcher.manager.removeEntity(data.entities[i].id); - break; - case MapElement.Type.wall: + 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) + { + 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); + 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; + snake.element(MapElement(MapElement.Type.empty, EntityID()),loc); + launcher.manager.addEntity(snake.snake_destroy_particle); + } + + } + 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.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); - snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].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; + } + 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]); + } + 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]); + } break; case MapElement.Type.apple: launcher.manager.removeEntity(snake.element(data.location[i].location).id); - data.snakes[i].parts.add(new_location); - 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); + if(data.snakes[i].parts.length < 100)data.snakes[i].parts.add(new_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]); + } + 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.addApple(); break; } @@ -455,19 +736,31 @@ void snakeStart() launcher.manager.registerComponent!CSnake; launcher.manager.registerComponent!CApple; launcher.manager.registerComponent!CParticle; + launcher.manager.registerComponent!CParticleVector; launcher.manager.registerComponent!CMovement; launcher.manager.registerComponent!CInput; + launcher.manager.registerComponent!CAnimation; launcher.manager.registerSystem!MoveSystem(0,"fixed"); launcher.manager.registerSystem!InputSystem(-100); launcher.manager.registerSystem!FixSnakeDirectionSystem(-1,"fixed"); launcher.manager.registerSystem!AppleSystem(-1,"fixed"); + launcher.manager.registerSystem!AnimationRenderSystem(100); + launcher.manager.registerSystem!AnimationSystem(-1); + launcher.manager.registerSystem!ParticleSystem(-1); + launcher.manager.registerSystem!ParticleMovementSystem(-1); launcher.manager.endRegister(); launcher.gui_manager.addSystem(MoveSystem.system_id,"Move System"); launcher.gui_manager.addSystem(InputSystem.system_id,"Input System"); launcher.gui_manager.addSystem(FixSnakeDirectionSystem.system_id,"Fix Direction System"); + launcher.gui_manager.addSystem(AnimationRenderSystem.system_id,"Animation Render System"); + launcher.gui_manager.addSystem(AnimationSystem.system_id,"Animation System"); + launcher.gui_manager.addSystem(ParticleSystem.system_id,"Particle Life System"); + launcher.gui_manager.addSystem(ParticleMovementSystem.system_id,"Particle Movement System"); + + snake.snake_destroy_particle_frames = Mallocator.makeArray([vec4(64,144,16,16)*px,vec4(80,144,16,16)*px,vec4(96,144,16,16)*px,vec4(112,144,16,16)*px].staticArray); { ushort[4] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id]; @@ -477,14 +770,26 @@ void snakeStart() launcher.manager.addEntity(snake.snake_tmpl); } + { + snake.snake_destroy_particle = launcher.manager.allocateTemplate([CLocation.component_id, CParticle.component_id, CParticleVector.component_id, CAnimation.component_id].staticArray); + CAnimation* canim = snake.snake_destroy_particle.getComponent!CAnimation; + canim.frames = snake.snake_destroy_particle_frames; + CParticle* particle = snake.snake_destroy_particle.getComponent!CParticle; + particle.life = 400; + } + { ushort[2] components = [CILocation.component_id, CApple.component_id]; snake.apple_tmpl = launcher.manager.allocateTemplate(components); 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"); + + MoveSystem* move_system = launcher.manager.getSystem!MoveSystem(); + move_system.setTemplates(); /*foreach(i; 0..10) foreach(j; 0..10) @@ -520,8 +825,8 @@ void snakeTool(vec2 position, Tool tool, int size) position.x += (randomf - 0.5) * size; position.y += (randomf - 0.5) * size; ivec2 ipos; - ipos.x = cast(int)(position.x / 32); - ipos.y = cast(int)(position.y / 32); + ipos.x = cast(int)(position.x / 16); + ipos.y = cast(int)(position.y / 16); *ilocation = ipos; if(snake.element(ipos).type != MapElement.Type.empty)return; } @@ -540,6 +845,8 @@ void snakeEvent(SDL_Event* event) bool snakeLoop() { + launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(288,288)) * 0.5; + /*if(launcher.show_demo_wnd) { igSetNextWindowPos(ImVec2(800 - 260, 30), ImGuiCond_Once, ImVec2(0,0)); diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index 5473aed..bddf53c 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -16,6 +16,8 @@ import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +enum float px = 1.0/512.0; + extern(C): /*####################################################################################################################### @@ -34,7 +36,7 @@ struct SpaceInvaders bool move_system = true; bool draw_system = true; - const vec2 map_size = vec2(600,600); + const vec2 map_size = vec2(400,300); const float cell_size = 60; } @@ -101,7 +103,7 @@ struct CScale ///use component as it value alias value this; - vec2 value = vec2(32,32); + vec2 value = vec2(16,16); } struct CTexture @@ -564,8 +566,8 @@ struct MovementSystem { foreach(i;0..data.length) { - data.locations[i].x += data.velocity[i].x * launcher.delta_time; - data.locations[i].y += data.velocity[i].y * launcher.delta_time; + data.locations[i].x += data.velocity[i].x * launcher.delta_time * 0.5; + data.locations[i].y += data.velocity[i].y * launcher.delta_time * 0.5; } } } @@ -587,6 +589,7 @@ struct InputMovementSystem const (CInput)[] input; //components are treated as required by default CLocation[] locations; + CTexture[] textures; } /** @@ -595,6 +598,7 @@ struct InputMovementSystem */ bool onBegin() { + move_vector = vec2(0,0); if(launcher.getKeyState(SDL_SCANCODE_W)) { move_vector = vec2(0,1); @@ -616,7 +620,7 @@ struct InputMovementSystem return true; } //don't call system update because no key pressed - return false; + return true; } /** @@ -627,11 +631,21 @@ struct InputMovementSystem */ void onUpdate(EntitiesData data) { + if(move_vector.x == 0) + { + foreach(i; 0..data.length) + { + data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); + } + return; + } //move every entity using movement vector foreach(i; 0..data.length) { - data.locations[i].x += move_vector.x * launcher.delta_time * 0.5; - data.locations[i].y += move_vector.y * launcher.delta_time * 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); } } } @@ -644,8 +658,6 @@ __gshared SpaceInvaders* space_invaders; void spaceInvadersStart() { - const float px = 1.0/512.0; - space_invaders = Mallocator.make!SpaceInvaders; space_invaders.texture.create(); @@ -690,9 +702,11 @@ void spaceInvadersStart() 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); + 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,48*px,16*px,16*px); + 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; @@ -707,9 +721,9 @@ void spaceInvadersStart() CTexture* tex_comp = space_invaders.laser_tmpl.getComponent!CTexture; tex_comp.tex = space_invaders.texture;//laser_tex; - tex_comp.coords = vec4(0*px,48*px,16*px,16*px); + 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(4,16); + scale_comp.value = vec2(2,8); CVelocity* vel_comp = space_invaders.laser_tmpl.getComponent!CVelocity; vel_comp.value = vec2(0,1); } @@ -727,7 +741,7 @@ void spaceInvadersStart() 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 - 64); + 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; @@ -738,17 +752,17 @@ void spaceInvadersStart() current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl); launcher.manager.addComponents(current_entity.id,CSideMove(0)); - loc_comp.value = vec2(128,space_invaders.map_size.y - 64); + loc_comp.value = vec2(128,space_invaders.map_size.y - 16); current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl); 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 - 64); + loc_comp.value = vec2(256,space_invaders.map_size.y - 16); launcher.manager.addEntity(space_invaders.enemy_tmpl); - loc_comp.value = vec2(0,space_invaders.map_size.y - 64); + loc_comp.value = vec2(0,space_invaders.map_size.y - 16); current_entity = launcher.manager.addEntity(space_invaders.enemy_tmpl); launcher.manager.addComponents(current_entity.id,CSideMove(0)); @@ -809,6 +823,7 @@ void spaceInvadersEvent(SDL_Event* event) bool spaceInvadersLoop() { + launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5; /*if(launcher.show_demo_wnd) { diff --git a/demos/utils/source/ecs_utils/gfx/buffer.d b/demos/utils/source/ecs_utils/gfx/buffer.d index 1f38624..2c25324 100644 --- a/demos/utils/source/ecs_utils/gfx/buffer.d +++ b/demos/utils/source/ecs_utils/gfx/buffer.d @@ -55,10 +55,10 @@ struct Buffer glBufferStorage(GL_ARRAY_BUFFER,size*count,data, flags); }*/ - void bufferSubData(uint size, uint offset, void* data) nothrow + void bufferSubData(BindTarget target, uint size, uint offset, void* data) nothrow { - bind(BindTarget.array); - glBufferSubData(GL_ARRAY_BUFFER,offset,size,data); + bind(target); + glBufferSubData(target,offset,size,data); } void map(BindTarget target) nothrow diff --git a/demos/utils/source/ecs_utils/gfx/renderer.d b/demos/utils/source/ecs_utils/gfx/renderer.d index e064c9e..2a04c57 100644 --- a/demos/utils/source/ecs_utils/gfx/renderer.d +++ b/demos/utils/source/ecs_utils/gfx/renderer.d @@ -98,7 +98,7 @@ struct Renderer alias Technique = RenderTechnique; - __gshared Technique technique = Technique.simple; + __gshared Technique technique = Technique.vbo_batch; void* data_ptr; //import ecs_utils.core : RenderTechnique; @@ -339,6 +339,7 @@ struct Renderer //import core.stdc.string; with(this_) { + if(item_id >= MaxObjects)return; //pos += view_pos; size.x *= view_size.x; size.y *= view_size.y; @@ -470,8 +471,8 @@ struct Renderer break; case Technique.vbo_batch: //if(data_index){ - batch_vbo[0].bufferSubData(item_id*4*16,0,batch_vertices.ptr); - batch_ibo[0].bufferSubData(item_id*6*2,0,batch_indices.ptr); + 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].bind(Buffer.BindTarget.array); batch_ibo[0].bind(Buffer.BindTarget.element_array); @@ -480,8 +481,8 @@ struct Renderer glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)8);//} break; case Technique.instanced_attrib_divisor: - ubos[0].bufferSubData(data_index,0,uniform_block.ptr); - ubos[0].bind(Buffer.BindTarget.array); + ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr); + ubos[0].bind(Buffer.BindTarget.uniform); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); glEnableVertexAttribArray(4); @@ -496,7 +497,7 @@ struct Renderer break; case Technique.uniform_buffer: //ubos[0].bufferData(1,64*MaxObjects,BufferUsage,null); - /*if(data_index)*/ubos[0].bufferSubData(data_index,0,uniform_block.ptr); + /*if(data_index)*/ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr); break; case Technique.uniform_buffer_indexed: ubos[0].bindRange(Buffer.BindTarget.uniform,0,0,block_max_size); @@ -581,6 +582,8 @@ struct Renderer { material_id = render_list[i].material_id; GfxConfig.materials[material_id].bind(); + float[3*4] data = [1,0,0,1,0,0,0,0,0,0,1,1]; + GfxConfig.materials[material_id].pushUniforms(data.ptr); } if(texture.data != render_list[i].texture.data) { @@ -589,17 +592,17 @@ struct Renderer } uint instance_count = 16_384; - if(i*16_384 > item_id) + if((i+1)*16_384 > item_id) { - instance_count = i*16_384 - item_id; + instance_count = item_id%16_384; } - /*glVertexAttribPointer(0,2,GL_FLOAT,false,16,cast(void*)(i*16_384*4*16)); + 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)); - glDrawElements(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2));*/ + glDrawElements(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2)); - glDrawElementsBaseVertex(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2),i*16_384*4); + //glDrawElementsBaseVertex(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2),i*16_384*4); } } else if(technique == Technique.ssbo_instanced || technique == Technique.instanced_attrib_divisor) @@ -794,9 +797,10 @@ struct Renderer void view(vec2 pos, vec2 size) { - view_pos = pos * size - 1; + //view_pos = pos * size - 1; view_size = vec2(2/size.x,2/size.y); sdl_transform = vec4(0,0,1.0/size.x,1.0/size.y); + 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; diff --git a/demos/utils/source/ecs_utils/gfx/texture.d b/demos/utils/source/ecs_utils/gfx/texture.d index 4407123..bb4c62a 100644 --- a/demos/utils/source/ecs_utils/gfx/texture.d +++ b/demos/utils/source/ecs_utils/gfx/texture.d @@ -55,6 +55,7 @@ struct Texture data.data[0..$] = (cast(ubyte*)surf.pixels)[0..data.data.length]; glGenTextures(1, &data.gl_handle); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,data.gl_handle); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/demos/utils/source/ecs_utils/math/vector.d b/demos/utils/source/ecs_utils/math/vector.d index 10b4de5..4b9f3af 100644 --- a/demos/utils/source/ecs_utils/math/vector.d +++ b/demos/utils/source/ecs_utils/math/vector.d @@ -30,6 +30,11 @@ struct vec2 else static assert(0, "Operator "~op~" not implemented"); } + ivec2 opCast() + { + return ivec2(cast(int)x,cast(int)y); + } + void opOpAssign(string op)(vec2 v) { static if (op == "+") @@ -69,6 +74,15 @@ struct vec4 } float[4] data; } + + vec4 opBinary(string op)(float v) + { + static if (op == "+") return vec4(x + v, y + v, z + v, w + v); + else static if (op == "-") return vec4(x - v, y - v, z - v, w - v); + else static if (op == "*") return vec4(x * v, y * v, z * v, w * v); + else static if (op == "/") return vec4(x / v, y / v, z / v, w / v); + else static assert(0, "Operator "~op~" not implemented"); + } } struct ivec2 @@ -82,6 +96,11 @@ struct ivec2 } int[2] data; } + + vec2 opCast() + { + return vec2(x,y); + } } struct ivec4 diff --git a/dub.json b/dub.json index 1709ae0..c23a4e6 100755 --- a/dub.json +++ b/dub.json @@ -5,7 +5,7 @@ ], "description": "Dynamic Entity Component System", "copyright": "Copyright © 2018-2019, Michał Masiukiewicz, Dawid Masiukiewicz", - "license": "BSD", + "license": "BSD 3-clause", "sourcePaths" : ["source\/"], "excludedSourceFiles":[ "source\/ecs\/traits.d" diff --git a/skeleton.html b/skeleton.html new file mode 100644 index 0000000..02f57a2 --- /dev/null +++ b/skeleton.html @@ -0,0 +1,36 @@ + + + + BubelECS + + + + + + + + + + +
+
+
+ +
+ + + diff --git a/source/ecs/atomic.d b/source/ecs/atomic.d index 91cc3d4..5e5f447 100644 --- a/source/ecs/atomic.d +++ b/source/ecs/atomic.d @@ -1,8 +1,11 @@ /************************************************************************************************************************ -*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. +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; diff --git a/source/ecs/attributes.d b/source/ecs/attributes.d index 2b24178..20d0f60 100644 --- a/source/ecs/attributes.d +++ b/source/ecs/attributes.d @@ -1,17 +1,24 @@ /************************************************************************************************************************ -*This module contain attributes used to mark components. -*Currently only two attributes are supported: -* - optional: mark component as optional for system update -* - readonly: mark component access as read only (used for multithreading) -* -*By default components are required and mutable. "const" attribute can be used insteac od readonly mark. -*ex. -*Struct EntitiesData -*{ -* Comp1[] cmp; //mutable required component -* @readonly @optional Comp2[] cmp2; //optional read only component -* @optional const (Comp3)[] cmp3; //same as cmp2 -*} +This module contain attributes used to mark components. +Currently only two attributes are supported: +$(LIST + * optional: mark component as optional for system update + * readonly: mark component access as read only (used for multithreading) +) + +By default components are required and mutable. "const" attribute can be used insteac od readonly mark. + +--- +Struct EntitiesData +{ + Comp1[] cmp; //mutable required component + @readonly @optional Comp2[] cmp2; //optional read only component + @optional const (Comp3)[] cmp3; //same as cmp2 +} +--- + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.attributes; diff --git a/source/ecs/block_allocator.d b/source/ecs/block_allocator.d index 5db4dab..d9f08ca 100644 --- a/source/ecs/block_allocator.d +++ b/source/ecs/block_allocator.d @@ -1,7 +1,10 @@ /************************************************************************************************************************ -*It's internal code. -* -*Module contain memory allocator. +It's internal code. + +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; @@ -9,14 +12,14 @@ import ecs.manager; import ecs.std; /************************************************************************************************************************ -*Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated. -*By default freeing memory only returns it to allocator. To free large memory chunks freeMemory function is used. -*freeMemory function return to system memory even if chunk blocks wasn't freed. +Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated. +By default freeing memory only returns it to allocator. To free large memory chunks freeMemory function is used. +freeMemory function return to system memory even if chunk blocks wasn't freed. */ struct BlockAllocator { /************************************************************************************************************************ - *Get new block. Allocator automatically allocate next memory chunk if needed. + Get new block. Allocator automatically allocate next memory chunk if needed. */ void* getBlock() nothrow @nogc { @@ -28,7 +31,7 @@ struct BlockAllocator } /************************************************************************************************************************ - *Return block to allocator for further use. + Return block to allocator for further use. */ void freeBlock(void* block) nothrow @nogc { @@ -37,7 +40,7 @@ struct BlockAllocator } /************************************************************************************************************************ - *Free whole used memory. This function return to system all memory chunks even if not every black was freed. + Free whole used memory. This function return to system all memory chunks even if not every black was freed. */ void freeMemory() nothrow @nogc { diff --git a/source/ecs/core.d b/source/ecs/core.d index 91c9065..c346c9f 100644 --- a/source/ecs/core.d +++ b/source/ecs/core.d @@ -1,34 +1,53 @@ /************************************************************************************************************************ -*This module contain main templates for user. -*There are three structure templates (mixins) which should be added on top of structure: -* - System: make system structure -* - Component: make component structure -* - Event: make event structure -* -*ex. -*Struct System1 -*{ -* mixin!ECS.System; -*} -* -*Struct System2 -*{ -* mixin!ECS.System(16);//set number of jobs generated for system by multithreaded update -*} -* -*Struct Component1 -*{ -* mixin!ECS.Component; -*} -* -*Struct Event1 -*{ -* mixin!ECS.Event; -*} -* -*There is also template for generating list of excluded components "ExcludedComponets(T...)". -*This template takes component structure types and making list of excluded components used in "registerSystem" function. -* +This module contain main templates for user. +There are three structure templates (mixins) which should be added on top of structure: +$(LIST + * System: make system structure + * Component: make component structure + * Event: make event structure +) + +--- +Struct System1 +{ + mixin!ECS.System; +} + +Struct System2 +{ + mixin!ECS.System(16);//set number of jobs generated for system by multithreaded update +} + +Struct Component1 +{ + mixin!ECS.Component; +} + +Struct Event1 +{ + mixin!ECS.Event; +} +--- + +There is also template for generating list of excluded components "ExcludedComponets(T...)". +This template takes component structure types and making list of excluded components used in "registerSystem" function. + +--- +Struct System1 +{ + mixin!ECS.System; + + struct EntitiesData + { + ... //used components + } + + ExcludedComponets!(Comp1, Comp2); +} +--- + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.core; @@ -36,12 +55,12 @@ public import ecs.manager; public import ecs.entity; /************************************************************************************************************************ -*Main struct used as namespace for templates. +Main struct used as namespace for templates. */ static struct ECS { /************************************************************************************************************************ - *Mark structure as System. Should be added on top of structure (before any data). + Mark structure as System. Should be added on top of structure (before any data). */ mixin template System(uint jobs_count = 32) { @@ -50,7 +69,7 @@ static struct ECS } /************************************************************************************************************************ - *Mark structure as Component. Should be added on top of structure (before any data). + Mark structure as Component. Should be added on top of structure (before any data). */ mixin template Component() { @@ -58,7 +77,7 @@ static struct ECS } /************************************************************************************************************************ - *Mark structure as Event. Should be added on top of structure (before any data). + Mark structure as Event. Should be added on top of structure (before any data). */ mixin template Event() { @@ -67,7 +86,7 @@ static struct ECS } /************************************************************************************************************************ - *Make list of excluded components. This template get structure types as argument. Should be added inside System structure. + Make list of excluded components. This template get structure types as argument. Should be added inside System structure. */ mixin template ExcludedComponents(T...) { diff --git a/source/ecs/entity.d b/source/ecs/entity.d index f3a8d97..0bd2dea 100644 --- a/source/ecs/entity.d +++ b/source/ecs/entity.d @@ -1,5 +1,8 @@ /************************************************************************************************************************ -*Entity module. +Entity module. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.entity; @@ -7,7 +10,7 @@ import ecs.system; import ecs.manager; /************************************************************************************************************************ -*Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference! +Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference! */ struct EntityID { @@ -18,7 +21,7 @@ struct EntityID } /************************************************************************************************************************ -*Structure of Entity. It's have only ID, but have full konwlege to get to components memory (If pointer to it is proper). +Structure of Entity. It's have only ID, but have full konwlege to get to components memory (If pointer to it is proper). */ struct Entity { @@ -26,8 +29,8 @@ struct Entity EntityID id; /************************************************************************************************************************ - *Get specified component. If component doesn't exist function retun null. Pointer is valid only before next "commit()", "begin()" or "end()" - *function is called. Returned pointer shouldn't be used to store reference to entity data. + Get specified component. If component doesn't exist function retun null. Pointer is valid only before next "commit()", "begin()" or "end()" + function is called. Returned pointer shouldn't be used to store reference to entity data. */ T* getComponent(T)() const { @@ -45,13 +48,15 @@ struct Entity } /************************************************************************************************************************ -*Entity template structure. -*Entity contain whole information needed to create new entity. Allocating EntityTemplate is considered as more expensive operation -*than adding entity. Whole components memory is stored in EntityTemplate and is copyied to newly added entity. -*If you want to place several entity with small difference in data then you should take pointer to component and change it before every -*entity addition. -*There is no restriction about number of allocated templates. Single template can be used from multiple threads, but if you -*want to changes some components data before add entity (entity position for example) it's better to use multiple templates. +Entity template structure. + +Entity contain whole information needed to create new entity. Allocating EntityTemplate is considered as more expensive operation +than adding entity. Whole components memory is stored in EntityTemplate and is copyied to newly added entity. +If you want to place several entity with small difference in data then you should take pointer to component and change it before every +entity addition. + +There is no restriction about number of allocated templates. Single template can be used from multiple threads, but if you +want to changes some components data before add entity (entity position for example) it's better to use multiple templates. */ export struct EntityTemplate { @@ -61,7 +66,7 @@ export struct EntityTemplate EntityManager.EntityInfo* info; /************************************************************************************************************************ - *Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. + Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. */ T* getComponent(T)() nothrow @nogc { diff --git a/source/ecs/events.d b/source/ecs/events.d index 317b2f0..ac7e185 100644 --- a/source/ecs/events.d +++ b/source/ecs/events.d @@ -34,7 +34,7 @@ package struct EventManager EventData* data = &events[Ev.event_id]; EventBlock* block = data.blocks[block_id]; - //EntityManager.EventInfo* info = &gEM.events[Ev.event_id]; + //EntityManager.EventInfo* info = &manager.events[Ev.event_id]; event.entity_id = id; if(block is null) @@ -119,15 +119,15 @@ package struct EventManager void allocateData(uint threads_count) nothrow @nogc { disposeData(); - events = Mallocator.makeArray!EventData(gEM.events.length); + events = Mallocator.makeArray!EventData(manager.events.length); 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;//gEM.events[i]. - gEM.alignNum(event.data_offset, gEM.events[i].alignment); + 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) / gEM.events[i].size); + event.max_events = cast(ushort)((events_block_size - event.data_offset) / manager.events[i].size); } } diff --git a/source/ecs/id_manager.d b/source/ecs/id_manager.d index 4f5a949..1ab8465 100644 --- a/source/ecs/id_manager.d +++ b/source/ecs/id_manager.d @@ -8,12 +8,12 @@ import ecs.atomic; import core.stdc.string : memcpy; /************************************************************************************************************************ -*IDManager is responsible for assignment and removing IDs. IDs are unique throughtout a whole duration of the program. +IDManager is responsible for assignment and removing IDs. IDs are unique throughtout a whole duration of the program. */ struct IDManager { /************************************************************************************************************************ - *Get new ID. + Get new ID. */ pragma(inline, false) EntityID getNewID() nothrow @nogc { @@ -74,7 +74,7 @@ struct IDManager } /************************************************************************************************************************ - *Release ID. + Release ID. */ void releaseID(EntityID id) nothrow @nogc { @@ -91,7 +91,7 @@ struct IDManager } /************************************************************************************************************************ - *Update pointer to entity. The purpose of this function is to ensure that pointer to entity is always correct. + Update pointer to entity. The purpose of this function is to ensure that pointer to entity is always correct. */ void update(ref Entity entity) nothrow @nogc { @@ -107,7 +107,7 @@ struct IDManager } /************************************************************************************************************************ - *Returns pointer to entity. + Returns pointer to entity. */ export Entity* getEntityPointer(EntityID id) nothrow @nogc { @@ -132,7 +132,7 @@ struct IDManager } /************************************************************************************************************************ - *Check if entity with specified ID exist. + Check if entity with specified ID exist. */ export bool isExist(EntityID id) nothrow @nogc { @@ -143,7 +143,7 @@ struct IDManager } /************************************************************************************************************************ - *Initialize manager. + Initialize manager. */ void initialize() nothrow @nogc { @@ -162,7 +162,7 @@ struct IDManager } /************************************************************************************************************************ - *Free manager memory. + Free manager memory. */ void deinitialize() @trusted @nogc nothrow { @@ -185,7 +185,7 @@ struct IDManager } /************************************************************************************************************************ - *Optimize memory. Must be called if any ID was added and some ID will be removed. + Optimize memory. Must be called if any ID was added and some ID will be removed. */ void optimize() nothrow @nogc { diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 5ec5b87..aca7f53 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -1,5 +1,8 @@ /************************************************************************************************************************ -*Most important module. Almost every function is called from EntityManager. +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; @@ -28,37 +31,38 @@ export alias gEntityManager = EntityManager.instance; alias SerializeVector = ecs.vector.Vector!ubyte; /************************************************************************************************************************ -*Entity manager is responsible for everything. -* -*Entity manager can be in three states: -* - registration: time between beginRegister() and endRegister() calls. -* - update: time between being() and end() calls. -* - default: when it's not in registration or update time -* -*Manager can be only in one state simultaneously. -* -*Manager must be initialized before use. There is global instance of EntityManager: EntityManager.instance or gEM as alias. -* -*Registration process consist of registration of passes, systems, entities and events. -* -*Pass is group of system which should be used inside one update() call. Passes are added as name (string) and can be referenced by name or id.
-*System is structure responsible for update of specified group of entities. System consist of EntitiesData structure which contain components used -*by system and some callback. Main callback is onUpdate() which is called by update() entity manager function. Other callbacks are used as listeners for -*adding entites, tracking system lifetime and events handling.
-*Component is basicly small fraction of data which is considered to be used as whole. In best scenario every byte of component is used when it's refered to. -*In practice sometimes it's better to join data into one component even if it's can be accessed separetly.
-*Events are structures with data used to handle events. Event can contain for exmaple one floating point number used as damage dealt to entity.
-*Entity is group of components. In memory entity is only ID which makes it's possible to take it's components. Components are grouped into chunks, and -*grouped by component type so entity can be fracted in big memory chunk.
-* -*There is two types of update: -*
- update(): function used to call update pass. -*
- updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user. +Entity manager is responsible for everything. + +Entity manager can be in three states: + - registration: time between beginRegister() and endRegister() calls. + - update: time between being() and end() calls. + - default: when it's not in registration or update time + +Manager can be only in one state simultaneously. + +Manager must be initialized before use. There is global instance of EntityManager: EntityManager.instance or gEM as alias. + +Registration process consist of registration of passes, systems, entities and events. + +Pass is group of system which should be used inside one update() call. Passes are added as name (string) and can be referenced by name or id. +System is structure responsible for update of specified group of entities. System consist of EntitiesData structure which contain components used +by system and some callback. Main callback is onUpdate() which is called by update() entity manager function. Other callbacks are used as listeners for +adding entites, tracking system lifetime and events handling. + +Component is basicly small fraction of data which is considered to be used as whole. In best scenario every byte of component is used when it's refered to. +In practice sometimes it's better to join data into one component even if it's can be accessed separetly. +Events are structures with data used to handle events. Event can contain for exmaple one floating point number used as damage dealt to entity. +Entity is group of components. In memory entity is only ID which makes it's possible to take it's components. Components are grouped into chunks, and +grouped by component type so entity can be fracted in big memory chunk. + +There is two types of update: + - update(): function used to call update pass. + - updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user. */ export struct EntityManager { /************************************************************************************************************************ - *Initialize ECS. + Initialize ECS. */ export static void initialize(uint threads_count, uint page_size = 32768, uint block_pages_count = 128) @@ -81,7 +85,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Deinitialize and destroy ECS. This function release whole memory. + Deinitialize and destroy ECS. This function release whole memory. */ export static void destroy() { @@ -154,7 +158,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Begin registering process. Every register function should be called between beginRegister() and endRegister(). + Begin registering process. Every register function should be called between beginRegister() and endRegister(). */ export void beginRegister() nothrow @nogc { @@ -172,7 +176,7 @@ export struct EntityManager } /************************************************************************************************************************ - *End registering process. Every register function should be called between beginRegister() and endRegister(). + End registering process. Every register function should be called between beginRegister() and endRegister(). */ export void endRegister() { @@ -346,7 +350,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Same as "void registerSystem(Sys)(int priority, int pass = 0)" but use pass name instead of id. + Same as "void registerSystem(Sys)(int priority, int pass = 0)" but use pass name instead of id. */ void registerSystem(Sys)(int priority, const(char)[] pass_name) { @@ -359,13 +363,13 @@ export struct EntityManager } /************************************************************************************************************************ - *Register new System into EntityManager. This funcion generate glue between EntityManager and System. - *Systems can be registered from external dynamic library, and can be registered after adding entities too. - *System mustn't be registered before components which system want to use, in this case functions call assertion. - * - *Params: - *priority = system priority. Priority determines order of execution of systems updates - *pass = index of UpdatePass which sholud call system update + Register new System into EntityManager. This funcion generate glue between EntityManager and System. + Systems can be registered from external dynamic library, and can be registered after adding entities too. + System mustn't be registered before components which system want to use, in this case functions call assertion. + + Params: + priority = system priority. Priority determines order of execution of systems updates + pass = index of UpdatePass which sholud call system update */ void registerSystem(Sys)(int priority, ushort pass = 0) { @@ -1084,7 +1088,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return system ECS api by id + Return system ECS api by id */ export System* getSystem(ushort id) nothrow @nogc { @@ -1094,7 +1098,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return pointer to system registered in manager + Return pointer to system registered in manager */ Sys* getSystem(Sys)() nothrow @nogc { @@ -1115,7 +1119,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Register component into EntityManager. + Register component into EntityManager. */ void registerComponent(Comp)() { @@ -1230,7 +1234,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Same as "void update(int pass = 0)" but use pass name instead of id. + Same as "void update(int pass = 0)" but use pass name instead of id. */ export void update(const(char)[] pass_name) nothrow @nogc { @@ -1240,7 +1244,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Update systems. Should be called only between begin() and end(). + Update systems. Should be called only between begin() and end(). */ export void update(ushort pass = 0) nothrow @nogc { @@ -1268,7 +1272,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Same as "void updateMT(int pass = 0)" but use pass name instead of id. + Same as "void updateMT(int pass = 0)" but use pass name instead of id. */ export void updateMT(const(char)[] pass_name) nothrow @nogc { @@ -1431,7 +1435,7 @@ export struct EntityManager }*/ /************************************************************************************************************************ - *Return size of single page (block). Every entity data block has size of page. + Return size of single page (block). Every entity data block has size of page. */ uint pageSize() { @@ -1439,8 +1443,8 @@ export struct EntityManager } /************************************************************************************************************************ - *Return number of pages in single block allocation. Library allocate defined number of pages at once and assign it's - *for entities. + Return number of pages in single block allocation. Library allocate defined number of pages at once and assign it's + for entities. */ uint pagesInBlock() { @@ -1465,11 +1469,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Allocate EntityTemplate with all components from entity witch it's data and returns pointer to it. - * - *Params: - *entity_id = ID of entity from which should be created template - *fill_default = if true, components will be filled with default data, instead entity data will be taken + Allocate EntityTemplate with all components from entity witch it's data and returns pointer to it. + + Params: + entity_id = ID of entity from which should be created template + fill_default = if true, components will be filled with default data, instead entity data will be taken */ export EntityTemplate* allocateTemplate(EntityID entity_id, bool fill_default = false) { @@ -1505,10 +1509,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Allocate EntityTemplate with specifed components and returns pointer to it. - * - *Params: - *components_ids = array of components allocated with template + Allocate EntityTemplate with specifed components and returns pointer to it. + + Params: + components_ids = array of components allocated with template */ export EntityTemplate* allocateTemplate(ushort[] components_ids) { @@ -1551,10 +1555,13 @@ export struct EntityManager } /************************************************************************************************************************ - *Allocate EntityTemplate with specifed components and returns pointer to it. - * - *Params: - *components_ids = array of components allocated with template + Allocate EntityTemplate from basic Template with modifications by adding and removing some components and returns pointer to it. + Arrays of components needen't to be checked for repeated components, as function itself check if components exist in base template. + + Params: + base_tmpl = template from which components sould be copied + components_ids = array of new components to add + remove_components_ids = array of components to remove from base template */ export EntityTemplate* allocateTemplate(EntityTemplate* base_tmpl, ushort[] components_ids, ushort[] remove_components_ids = null) @@ -1624,10 +1631,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Returns entity type info. - * - *Params: - *ids = array of components + Returns entity type info. + + Params: + ids = array of components */ export EntityInfo* getEntityInfo(ushort[] ids) { @@ -1930,10 +1937,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Returns pointer to entity. - * - *Params: - *id = ID of entity + Returns pointer to entity. + + Params: + id = ID of entity */ export Entity* getEntity(EntityID id) nothrow @nogc { @@ -1941,11 +1948,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Remove components from entity by IDs. Components will be removed on end of frame. - * - *Params: - *entity_id = ID of entity - *del_ids = array of components IDs + Remove components from entity by IDs. Components will be removed on end of frame. + + Params: + entity_id = ID of entity + del_ids = array of components IDs */ export void removeComponents(EntityID entity_id, ushort[] del_ids) nothrow @nogc { @@ -2051,11 +2058,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Remove coponents from entity. - * - *Params: - *Components = components types to remove - *entity_id = ID of entity + Remove coponents from entity. + + Params: + Components = components types to remove + entity_id = ID of entity */ void removeComponents(Components...)(EntityID entity_id) { @@ -2204,11 +2211,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Add components to entity. Components will be added on end of frame. - * - *Params: - *entity_id = ID of entity to remove - *comps = components to add + Add components to entity. Components will be added on end of frame. + + Params: + entity_id = ID of entity to remove + comps = components to add */ void addComponents(Components...)(const EntityID entity_id, Components comps) nothrow @nogc { @@ -2239,10 +2246,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Free template memory. - * - *Params: - *template_ = pointer entity template allocated by EntityManager. + Free template memory. + + Params: + template_ = pointer entity template allocated by EntityManager. */ export void freeTemplate(EntityTemplate* template_) { @@ -2251,9 +2258,9 @@ export struct EntityManager } /************************************************************************************************************************ - *Add copy of entity to system and returns pointer to it. Added copy has same data as copied entity. 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. + Add copy of entity to system and returns pointer to it. Added copy has same data as copied entity. 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: *id = ID of entity to be copyied. @@ -2305,11 +2312,11 @@ export struct EntityManager } /************************************************************************************************************************ - *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. + 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. */ export Entity* addEntity(EntityTemplate* tmpl) { @@ -2355,7 +2362,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return block with free space for selected EntityInfo. + Return block with free space for selected EntityInfo. */ private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) nothrow @nogc { @@ -2383,7 +2390,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return block with free space for selected EntityInfo. Additional this function is multithread safe. + Return block with free space for selected EntityInfo. Additional this function is multithread safe. */ private EntitiesBlock* findBlockWithFreeSpaceMT(EntityInfo* info) { @@ -2427,10 +2434,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Remove entity by ID. Entity will be removed on frame end. - * - *Params: - *id = id of entity to remove + Remove entity by ID. Entity will be removed on frame end. + + Params: + id = id of entity to remove */ export void removeEntity(EntityID id) { @@ -2513,10 +2520,10 @@ export struct EntityManager } /************************************************************************************************************************ - *functions return MetaData of page. - * - *Params: - *pointer = pointer to any data of entity (i.e. component data pointer) + functions return MetaData of page. + + Params: + pointer = pointer to any data of entity (i.e. component data pointer) */ export EntitiesBlock* getMetaData(const void* pointer) nothrow @nogc { @@ -2750,7 +2757,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Begin of update process. Should be called before any update is called. + Begin of update process. Should be called before any update is called. */ export void begin() { @@ -2767,7 +2774,7 @@ export struct EntityManager } /************************************************************************************************************************ - *End of update process. Should be called after every update function. + End of update process. Should be called after every update function. */ export void end() { @@ -2917,7 +2924,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Component info; + Component info; */ struct ComponentInfo { @@ -2961,7 +2968,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Entity type info. + Entity type info. */ struct EntityInfo { @@ -3133,7 +3140,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Meta data of every block of entities (contained at the begining of block). + Meta data of every block of entities (contained at the begining of block). */ struct EntitiesBlock { @@ -3180,10 +3187,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Structure with data used to calling System calls. - * - *first_block, begin, end, blocks parameters are used - *to call partial info update + Structure with data used to calling System calls. + + first_block, begin, end, blocks parameters are used + to call partial info update */ struct CallData { diff --git a/source/ecs/std.d b/source/ecs/std.d index 6077ede..c027fd5 100644 --- a/source/ecs/std.d +++ b/source/ecs/std.d @@ -1,6 +1,9 @@ /************************************************************************************************************************ -*It's internal code! -*This module contain implementation of standard functionality. +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; diff --git a/source/ecs/system.d b/source/ecs/system.d index 35eb996..22a3955 100644 --- a/source/ecs/system.d +++ b/source/ecs/system.d @@ -1,5 +1,8 @@ /************************************************************************************************************************ -*System module. +System module. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.system; @@ -7,25 +10,27 @@ import ecs.entity; import ecs.manager; /************************************************************************************************************************ -*System contain data required to proper glue EntityManager with Systems. -*System callbacks: -*
-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); +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); +) */ struct System { /************************************************************************************************************************ - *Check if system is enabled. + Check if system is enabled. */ export bool enabled() nothrow @nogc { @@ -33,7 +38,7 @@ struct System } /************************************************************************************************************************ - *Enable system. If actually it is enabled function do nothing. + Enable system. If actually it is enabled function do nothing. */ export void enable() nothrow @nogc { @@ -43,7 +48,7 @@ struct System } /************************************************************************************************************************ - *Disable system. If actually it is disabled function do nothing. + Disable system. If actually it is disabled function do nothing. */ export void disable() nothrow @nogc { @@ -53,7 +58,7 @@ struct System } /************************************************************************************************************************ - *Get system priority. + Get system priority. */ export int priority() nothrow @nogc { @@ -61,7 +66,7 @@ struct System } /************************************************************************************************************************ - *Get system priority. + Get system priority. */ export bool execute() nothrow @nogc { @@ -69,7 +74,7 @@ struct System } /************************************************************************************************************************ - *Get system priority. + Get system priority. */ export ushort id() nothrow @nogc { @@ -77,7 +82,7 @@ struct System } /************************************************************************************************************************ - *Get system name. + Get system name. */ export const(char)[] name() nothrow @nogc { diff --git a/source/ecs/traits.d b/source/ecs/traits.d index b270b06..d19259f 100644 --- a/source/ecs/traits.d +++ b/source/ecs/traits.d @@ -15,7 +15,7 @@ unittest } /************************************************************************************************************************ -* Returns index of Component/Entity array in System's EntitiesData struct + Returns index of Component/Entity array in System's EntitiesData struct */ static long getIndexOfTypeInEntitiesData(EntitiesData, Type)() {