diff --git a/noengine-cpp-field-indexing/.bazelignore b/noengine-cpp-field-indexing/.bazelignore new file mode 100644 index 0000000..d436eab --- /dev/null +++ b/noengine-cpp-field-indexing/.bazelignore @@ -0,0 +1,2 @@ +.git + diff --git a/noengine-cpp-field-indexing/.bazelrc b/noengine-cpp-field-indexing/.bazelrc new file mode 100644 index 0000000..7f74da2 --- /dev/null +++ b/noengine-cpp-field-indexing/.bazelrc @@ -0,0 +1,20 @@ +startup --windows_enable_symlinks +common --enable_bzlmod +common --registry=https://raw.githubusercontent.com/ecsact-dev/bazel_registry/main +common --registry=https://raw.githubusercontent.com/bazelboost/registry/main +common --registry=https://raw.githubusercontent.com/zaucy/bazel-central-registry/add-curl-config2 # temporary +common --registry=https://bcr.bazel.build +build --enable_platform_specific_config +build --incompatible_enable_cc_toolchain_resolution +build --incompatible_strict_action_env +build --enable_runfiles +build --noincompatible_remove_rule_name_parameter +query --noincompatible_remove_rule_name_parameter + +build --@boost.dll//:use_std_fs +query --@boost.dll//:use_std_fs +build --@boost.process//:use_std_fs +query --@boost.process//:use_std_fs + +try-import %workspace%/user.bazelrc + diff --git a/noengine-cpp-field-indexing/.bazelversion b/noengine-cpp-field-indexing/.bazelversion new file mode 100644 index 0000000..40dffa7 --- /dev/null +++ b/noengine-cpp-field-indexing/.bazelversion @@ -0,0 +1,2 @@ +7.x + diff --git a/noengine-cpp-field-indexing/.clang-format b/noengine-cpp-field-indexing/.clang-format new file mode 100644 index 0000000..b090ef9 --- /dev/null +++ b/noengine-cpp-field-indexing/.clang-format @@ -0,0 +1,99 @@ +AccessModifierOffset: -2 +AlignAfterOpenBracket: BlockIndent +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Left +AlignOperands: DontAlign +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakAfterJavaFieldAnnotations: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakConstructorInitializersBeforeComma: false +BreakStringLiterals: true +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IncludeIsMainRegex: "(Test)?$" +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: true +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 400 +PenaltyReturnTypeOnItsOwnLine: 350 +PointerAlignment: Left +ReflowComments: true +RequiresClausePosition: OwnLine +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SeparateDefinitionBlocks: Always +Standard: Latest +TabWidth: 2 +UseTab: ForContinuationAndIndentation diff --git a/noengine-cpp-field-indexing/.editorconfig b/noengine-cpp-field-indexing/.editorconfig new file mode 100644 index 0000000..9b1aeae --- /dev/null +++ b/noengine-cpp-field-indexing/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true + diff --git a/noengine-cpp-field-indexing/.gitignore b/noengine-cpp-field-indexing/.gitignore new file mode 100644 index 0000000..54e7b5c --- /dev/null +++ b/noengine-cpp-field-indexing/.gitignore @@ -0,0 +1,6 @@ +bazel-* +*.bazel.lock +user.bazelrc +/external +/.cache + diff --git a/noengine-cpp-field-indexing/BUILD.bazel b/noengine-cpp-field-indexing/BUILD.bazel new file mode 100644 index 0000000..dfe6f5b --- /dev/null +++ b/noengine-cpp-field-indexing/BUILD.bazel @@ -0,0 +1,61 @@ +load("@bazel_skylib//lib:selects.bzl", "selects") +load("@rules_cc//cc:defs.bzl", "cc_binary") +load("@rules_ecsact//ecsact:defs.bzl", "ecsact_binary", "ecsact_codegen") + +ecsact_codegen( + name = "ecsact_cpp_sources", + srcs = glob(["src/**/*.ecsact"]), + output_directory = "generated", + plugins = [ + "@ecsact_lang_cpp//cpp_header_codegen", + "@ecsact_lang_cpp//cpp_systems_header_codegen", + "@ecsact_lang_cpp//systems_header_codegen", + "@ecsact_lang_cpp//cpp_systems_source_codegen", + ], +) + +ecsact_binary( + name = "ecsact_runtime", + srcs = glob(["src/**/*.ecsact"]), + recipes = [ + "@ecsact_rt_entt", + ], +) + +cc_binary( + name = "ecsact-examples-noengine-cpp-sdl2", + srcs = glob(["src/**/*.cc", "src/**/*.hh"]) + [ + ":ecsact_cpp_sources", + ], + copts = selects.with_or({ + "@rules_cc//cc/compiler:clang": [ + "-std=c++20", + "-fexperimental-library", + ], + ("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [ + "/std:c++20", + "/permissive-", + "/Zc:preprocessor", + ], + "//conditions:default": [ + "-std=c++20", + ], + }), + deps = [ + ":ecsact_runtime", + "@sdl2", + "@sdl2//:sdl2_main", + "@cute_c2", + "@imgui", + "@imgui//backends:sdlrenderer2", + "@imgui//backends:sdl2", + "@ecsact_runtime//:core", + "@ecsact_lang_cpp//:support", + "@ecsact_lang_cpp//:execution_context", + ], +) + +alias( + name = "example", + actual = ":ecsact-examples-noengine-cpp-sdl2", +) diff --git a/noengine-cpp-field-indexing/MODULE.bazel b/noengine-cpp-field-indexing/MODULE.bazel new file mode 100644 index 0000000..8e01b8a --- /dev/null +++ b/noengine-cpp-field-indexing/MODULE.bazel @@ -0,0 +1,66 @@ +module(name = "ecsact-examples-noengine-cpp-sdl2") + +bazel_dep(name = "rules_cc", version = "0.0.9") +bazel_dep(name = "sdl2") +bazel_dep(name = "toolchains_llvm", version = "1.0.0", dev_dependency = True) +bazel_dep(name = "hedron_compile_commands", dev_dependency = True) +bazel_dep(name = "ecsact_runtime", version = "0.6.2") +bazel_dep(name = "bazel_skylib", version = "1.6.1") +bazel_dep(name = "rules_ecsact", version = "0.5.2") +bazel_dep(name = "ecsact_cli", version = "0.3.7") +bazel_dep(name = "ecsact_lang_cpp", version = "0.4.1") +bazel_dep(name = "boost.dll", version = "1.83.0.bzl.2") +bazel_dep(name = "boost.process", version = "1.83.0.bzl.2") +bazel_dep(name = "platforms", version = "0.0.10") +bazel_dep(name = "ecsact_rt_entt", version = "0.3.2") +bazel_dep(name = "cute_c2") +bazel_dep(name = "imgui") + +archive_override( + module_name = "cute_c2", + integrity = "sha256-8RRKdXKWnbKziyQ6rKZcIJwsEuRk/q8WP9P1iFCOGfg=", + urls = ["https://github.com/seaube/bazel_cute_headers/releases/download/bazel-cute-headers-2024-05-16/bzlmod_cute_c2_1.10.tar.gz"], +) + +git_override( + module_name = "imgui", + commit = "975821a9513e230601b358aea829b9659de13d11", + remote = "https://github.com/zaucy/imgui.git", +) +git_override( + module_name = "sdl2", + commit = "2c8acf0e46f14ebbafe3ec9cc05d7ee4b6d7609f", + remote = "https://github.com/zaucy/sdl.git", +) +git_override( + module_name = "ecsact_rt_entt", + commit = "5ed2115146a9f187dca3c0ff9f8c66fbea09bf23", + remote = "https://github.com/ecsact-dev/ecsact_rt_entt.git", +) + +ecsact = use_extension("@rules_ecsact//ecsact:extensions.bzl", "ecsact", dev_dependency = True) +ecsact.toolchain(use_ecsact_cli = True) +use_repo(ecsact, "ecsact_toolchain") + +# TODO: https://github.com/bazelbuild/bazel-central-registry/pull/1916 +git_override( + module_name = "libarchive", + commit = "7c331f92acea5243c195cdc6fb46ecfa11ce1ce2", + remote = "https://github.com/zaucy/libarchive.git", +) + +git_override( + module_name = "hedron_compile_commands", + commit = "204aa593e002cbd177d30f11f54cff3559110bb9", + remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", +) + +llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) +llvm.toolchain(llvm_version = "17.0.6") +use_repo(llvm, "llvm_toolchain") + +register_toolchains( + "@llvm_toolchain//:all", + "@ecsact_toolchain//:all", + dev_dependency = True, +) diff --git a/noengine-cpp-field-indexing/README.md b/noengine-cpp-field-indexing/README.md new file mode 100644 index 0000000..da53fa8 --- /dev/null +++ b/noengine-cpp-field-indexing/README.md @@ -0,0 +1,10 @@ +Requirements for running this example: + +* [bazel](https://bazel.build/) (preferrably [bazelisk](https://github.com/bazelbuild/bazelisk)) + +## Running Example + +```sh +bazel run //:example -c opt +``` + diff --git a/noengine-cpp-field-indexing/WORKSPACE.bzlmod b/noengine-cpp-field-indexing/WORKSPACE.bzlmod new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/noengine-cpp-field-indexing/WORKSPACE.bzlmod @@ -0,0 +1 @@ + diff --git a/noengine-cpp-field-indexing/src/example.ecsact b/noengine-cpp-field-indexing/src/example.ecsact new file mode 100644 index 0000000..a0ae8d0 --- /dev/null +++ b/noengine-cpp-field-indexing/src/example.ecsact @@ -0,0 +1,107 @@ +main package example; + +component Health { f32 value; } +component Position { f32 x; f32 y; } + +component Region { i32 x1; i32 y1; i32 x2; i32 y2; } +component WithinRegion { + Region.x1 region_x1; + Region.y1 region_y1; + Region.x2 region_x2; + Region.y2 region_y2; +} + +component InfluenceZone { f32 size; } +component WithinInfluenceZone { + Position.x origin_x; + Position.y origin_y; + InfluenceZone.size radius; +} + +component Tree; +component Person { i32 seed; } +component EvacuationProbe; + +component OnFire { f32 fuel; } +component FireExtinguished; + +system SpreadFire { + readwrite OnFire; + readonly WithinInfluenceZone with (origin_x, origin_y, radius) { + adds OnFire; + } +} + +system FireDamage { + readwrite OnFire; + readwrite Health; +} + +system FireDiminish { + readwrite OnFire; + adds FireExtinguished; +} + +system FireExtinguish { + include FireExtinguished; + removes OnFire; +} + +action AddRegion { + i32 new_region_x1; + i32 new_region_y1; + i32 new_region_x2; + i32 new_region_y2; + + include EvacuationProbe; + readwrite Position; + + generates { + required Region; + } +} + +system PersonMovementBehaviour { + readwrite Position; +} + +system CalcWithinRegionParent(lazy) { + readonly Region; + system CalcWithinRegion { + readwrite WithinRegion; + readonly Position; + } +} + +action EvacuateRegion { + i32 mouse_x; + i32 mouse_y; + + include EvacuationProbe; + readwrite Position; +} + +system StartEvacuation { + include EvacuationProbe; + readonly Position; + readonly WithinRegion; + notify onchange WithinRegion; +} + +system Evacuate { + include EvacuationProbe; + readonly WithinRegion with (region_x1, region_y1, region_x2, region_y2) { + readwrite Position; + readwrite Health; + } +} + +system RenderPeople { + include Person; + readonly Position; +} + +system RenderEvacuationProbe { + include EvacuationProbe; + readonly Position; +} diff --git a/noengine-cpp-field-indexing/src/main.cc b/noengine-cpp-field-indexing/src/main.cc new file mode 100644 index 0000000..2f2f10f --- /dev/null +++ b/noengine-cpp-field-indexing/src/main.cc @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include +#include "imgui.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_sdlrenderer2.h" +#include "ecsact/runtime/core.hh" +#include "ecsact/runtime/dynamic.h" +#include "generated/example.ecsact.systems.hh" +#include "generated/example.ecsact.hh" +#include "simulation_config.hh" + +SDL_Renderer* renderer; +int viewport_width; +int viewport_height; + +static auto SDLCALL notify_system_execution_thread(void* data) -> int { + int cnt; + auto ev = SDL_Event{}; + ev.type = SDL_USEREVENT; + while(!*static_cast(data) /* done bool */) { + SDL_PushEvent(&ev); + SDL_Delay( + static_cast(example::simulation_config::fixed_delta_time * 1000.f) + ); + } + + return 0; +} + +static auto key_actions( // + SDL_KeyboardEvent ev, + ecsact::core::execution_options& exec_options +) -> void { + // NOTE: we need to maintain the lifecyle of these actions so we use global + // storage + static auto move = example::Move{}; + static auto jump = example::Jump{}; + static auto fire = example::Fire{}; + static auto reverse_gravity = example::ReverseGravity{}; + + const auto pressed = ev.state == SDL_PRESSED; + + if(pressed && ev.repeat > 0) { + return; + } + + if(ev.keysym.sym == SDLK_w) { + move.dir_y += pressed ? 1.0f : -1.0f; + return exec_options.push_action(&move); + } + + if(ev.keysym.sym == SDLK_a) { + move.dir_x -= pressed ? 1.0f : -1.0f; + return exec_options.push_action(&move); + } + + if(ev.keysym.sym == SDLK_s) { + move.dir_y -= pressed ? 1.0f : -1.0f; + return exec_options.push_action(&move); + } + + if(ev.keysym.sym == SDLK_d) { + move.dir_x += pressed ? 1.0f : -1.0f; + return exec_options.push_action(&move); + } + + if(pressed && ev.keysym.sym == SDLK_r) { + return exec_options.push_action(&reverse_gravity); + } + + if(pressed && ev.keysym.sym == SDLK_j) { + return exec_options.push_action(&fire); + } + + if(pressed && ev.keysym.sym == SDLK_SPACE) { + return exec_options.push_action(&jump); + } +} + +auto main(int argc, char* argv[]) -> int { + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) { + SDL_Log("Unable to initialize SDL: %s", SDL_GetError()); + return 1; + } + + auto done = false; + auto reg = ecsact::core::registry{"C++ Field Indexing"}; + auto win = SDL_CreateWindow( + "Ecsact Example - C++ Field Indexing", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 1280, + 720, + 0 + ); + + if(win == nullptr) { + SDL_Log("failed to create window: %s", SDL_GetError()); + return 1; + } + + renderer = SDL_CreateRenderer(win, -1, 0); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + auto& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + if(!ImGui_ImplSDL2_InitForSDLRenderer(win, renderer)) { + SDL_Log("failed to init imgui for sdlrenderer2"); + return 1; + } + if(!ImGui_ImplSDLRenderer2_Init(renderer)) { + SDL_Log("failed to init imgui sdlrenderer2"); + return 1; + } + + SDL_RegisterEvents(1); + auto exec_thread = SDL_CreateThread( + notify_system_execution_thread, + "Ecsact System Execution Ticker", + &done + ); + + auto exec_options = ecsact::core::execution_options{}; + auto evc = ecsact::core::execution_events_collector<>{}; + + auto create_entity_events_fired = 0; + auto destroy_entity_events_fired = 0; + auto init_events_fired = 0; + auto update_events_fired = 0; + auto remove_events_fired = 0; + + evc.set_any_init_callback([&](auto, const auto&) { init_events_fired += 1; }); + evc.set_any_update_callback([&](auto, const auto&) { + update_events_fired += 1; + }); + evc.set_any_remove_callback([&](auto, const auto&) { + remove_events_fired += 1; + }); + evc.set_entity_created_callback([&](auto, auto) { + create_entity_events_fired += 1; + }); + evc.set_entity_destroyed_callback([&](auto) { + destroy_entity_events_fired -= 1; + }); + + auto physics_singleton = reg.create_entity(); + reg.add_component(physics_singleton, example::Gravity{9810.f}); + + auto player_entity = reg.create_entity(); + reg.add_component(player_entity); + reg.add_component(player_entity, example::Moving{}); + reg.add_component(player_entity, example::Position{}); + reg.add_component(player_entity, example::Velocity{}); + + SDL_GetWindowSize(win, &viewport_width, &viewport_height); + + auto last_ticks = SDL_GetTicks64(); + auto execution_count = 0; + auto event = SDL_Event{}; + while(!done && SDL_WaitEvent(&event) == 1) { + ImGui_ImplSDL2_ProcessEvent(&event); + if(event.type == SDL_QUIT) { + done = true; + break; + } else if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { + key_actions(event.key, exec_options); + } else if(event.type == SDL_USEREVENT) { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + ImGui_ImplSDLRenderer2_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + ImGui::Begin("Details"); + ImGui::Text("Execution Count: %i", execution_count); + ImGui::Text("Entity Count (api): %i", reg.count_entities()); + ImGui::Text("Create Entity Events: %i", create_entity_events_fired); + ImGui::Text("Destroy Entity Events: %i", destroy_entity_events_fired); + ImGui::Text("Init Events: %i", init_events_fired); + ImGui::Text("Update Events: %i", update_events_fired); + ImGui::Text("Remove Events: %i", remove_events_fired); + + auto before_exec = SDL_GetTicks64(); + auto err = reg.execute_systems(std::array{exec_options}, evc); + auto after_exec = SDL_GetTicks64(); + ImGui::Text("Execute Systems Time: %llums", after_exec - before_exec); + execution_count += 1; + ImGui::End(); + if(err != ECSACT_EXEC_SYS_OK) { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "System Execution Failure %i", + err + ); + done = true; + break; + } + exec_options.clear(); + + ImGui::Render(); + ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); + SDL_RenderPresent(renderer); + } else if(event.type == SDL_WINDOWEVENT) { + if(event.window.type == SDL_WINDOWEVENT_RESIZED) { + SDL_GetWindowSize(win, &viewport_width, &viewport_height); + } + } + } + + SDL_WaitThread(exec_thread, nullptr); + ImGui_ImplSDLRenderer2_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + SDL_Quit(); + return 0; +} diff --git a/noengine-cpp-field-indexing/src/simulation_config.hh b/noengine-cpp-field-indexing/src/simulation_config.hh new file mode 100644 index 0000000..5ad74ac --- /dev/null +++ b/noengine-cpp-field-indexing/src/simulation_config.hh @@ -0,0 +1,5 @@ +#pragma once + +namespace example::simulation_config { +constexpr auto fixed_delta_time = 0.02f; +} diff --git a/noengine-cpp-field-indexing/src/system_impls.cc b/noengine-cpp-field-indexing/src/system_impls.cc new file mode 100644 index 0000000..66f2431 --- /dev/null +++ b/noengine-cpp-field-indexing/src/system_impls.cc @@ -0,0 +1,8 @@ +#include "generated/example.ecsact.systems.hh" + +#include +#include +#include +#include "simulation_config.hh" + +// TODO: fill in implementations