diff --git a/.gitignore b/.gitignore index f16c16d..ed4a29f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,1045 @@ -# Build directories -build/ -cmake-build-*/ -out/ +##### Windows +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db -# IDE files -.idea/ -.vscode/ -*.swp -*.swo +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +##### Linux *~ -# Compiled files +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +##### MacOS +# General +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +##### Android +# Built application files +*.apk +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +##### Backup +*.bak +*.gho +*.ori +*.orig +*.tmp + +##### GPG +secring.* + +##### Dropbox +# Dropbox settings and caches +.dropbox +.dropbox.attr +.dropbox.cache + +##### SynopsysVCS +# Waveform formats +*.vcd +*.vpd +*.evcd +*.fsdb + +# Default name of the simulation executable. A different name can be +# specified with this switch (the associated daidir database name is +# also taken from here): -o / +simv + +# Generated for Verilog and VHDL top configs +simv.daidir/ +simv.db.dir/ + +# Infrastructure necessary to co-simulate SystemC models with +# Verilog/VHDL models. An alternate directory may be specified with this +# switch: -Mdir= +csrc/ + +# Log file - the following switch allows to specify the file that will be +# used to write all messages from simulation: -l +*.log + +# Coverage results (generated with urg) and database location. The +# following switch can also be used: urg -dir .vdb +simv.vdb/ +urgReport/ + +# DVE and UCLI related files. +DVEfiles/ +ucli.key + +# When the design is elaborated for DirectC, the following file is created +# with declarations for C/C++ functions. +vc_hdrs.h + +##### SVN +.svn/ + +##### Mercurial +.hg/ +.hgignore +.hgsigs +.hgsub +.hgsubstate +.hgtags + +##### Bazaar +.bzr/ +.bzrignore + +##### CVS +/CVS/* +**/CVS/* +.cvsignore +*/.cvsignore + +##### TortoiseGit +# Project-level settings +/.tgitconfig + +##### PuTTY +# Private key +*.ppk + +##### Vim +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +##### Emacs +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + +##### SublimeText +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json +sftp-config-alt*.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +##### Notepad++ +# Notepad++ backups # +*.bak + +##### TextMate +*.tmproj +*.tmproject +tmtags + +##### VisualStudioCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +##### NetBeans +**/nbproject/private/ +**/nbproject/Makefile-*.mk +**/nbproject/Package-*.bash +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +##### JetBrains +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +##### Eclipse +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +##### Qt +# C++ objects and libs +*.slo +*.lo *.o *.a +*.la +*.lai *.so +*.so.* +*.dll *.dylib -# CMake generated files +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +##### VisualStudio +##### VisualStudio +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*[.json, .xml, .info] + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +##### Gradle +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +##### CMake +CMakeLists.txt.user CMakeCache.txt -CMakeFiles/ +CMakeFiles +CMakeScripts +Testing +Makefile cmake_install.cmake +install_manifest.txt compile_commands.json CTestTestfile.cmake -Testing/ +_deps -# OS files -.DS_Store -Thumbs.db +##### C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# C/C++ binary extension file +*.bin + +##### C +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Raspberry Pi Pico Object file +*.uf2 +# Raspberry Pi Pico disassembler file +*.dis \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7482775..ba01e11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,33 +1,64 @@ -cmake_minimum_required(VERSION 3.16) -project(factory_hole_core - VERSION 0.1.0 - DESCRIPTION "High-performance ECS-based factory game engine core" - LANGUAGES CXX -) +cmake_minimum_required(VERSION 3.20) +project(factory-hole-core LANGUAGES CXX) -# C++17 standard -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -# Options -option(FACTORY_CORE_BUILD_TESTS "Build tests" ON) -option(FACTORY_CORE_BUILD_EXAMPLES "Build examples" ON) - -# Export compile commands for IDE support set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# Include dependency management -include(cmake/FetchDependencies.cmake) +include(FetchContent) -# Add subdirectories -add_subdirectory(src) +# Flecs ECS +FetchContent_Declare( + flecs + GIT_REPOSITORY https://github.com/SanderMertens/flecs.git + GIT_TAG v4.1.4 + GIT_SHALLOW TRUE +) -if(FACTORY_CORE_BUILD_TESTS) - enable_testing() - add_subdirectory(tests) -endif() +# Doctest +FetchContent_Declare( + doctest + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG v2.4.12 + GIT_SHALLOW TRUE +) -if(FACTORY_CORE_BUILD_EXAMPLES) - add_subdirectory(examples) -endif() +FetchContent_MakeAvailable(flecs doctest) + +# Only compile sources needed for the core library +set(SOURCES + src/Components/Config/WorldConfig.cpp + src/Core/WorldInstance.cpp +) + +add_library(factory-hole-core ${SOURCES}) + +# Main executable +add_executable(factory-hole-app src/main.cpp) +target_link_libraries(factory-hole-app PRIVATE factory-hole-core) + +target_include_directories(factory-hole-core PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries(factory-hole-core PUBLIC + flecs::flecs_static + doctest::doctest +) + +# Tests +enable_testing() + +file(GLOB_RECURSE TEST_SOURCES tests/*.cpp) + +add_executable(factory-hole-tests ${TEST_SOURCES}) + +target_include_directories(factory-hole-tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries(factory-hole-tests PRIVATE + doctest::doctest_with_main +) + +add_test(NAME factory-hole-tests COMMAND factory-hole-tests) diff --git a/cmake/FetchDependencies.cmake b/cmake/FetchDependencies.cmake deleted file mode 100644 index 6e9959d..0000000 --- a/cmake/FetchDependencies.cmake +++ /dev/null @@ -1,20 +0,0 @@ -include(FetchContent) - -# Fetch flecs - high-performance ECS library -FetchContent_Declare( - flecs - GIT_REPOSITORY https://github.com/SanderMertens/flecs.git - GIT_TAG v4.0.4 - GIT_SHALLOW TRUE -) - -# Fetch doctest - testing framework (same as Godot uses) -FetchContent_Declare( - doctest - GIT_REPOSITORY https://github.com/doctest/doctest.git - GIT_TAG v2.4.11 - GIT_SHALLOW TRUE -) - -# Make dependencies available -FetchContent_MakeAvailable(flecs doctest) diff --git a/include/Components/Chute.h b/include/Components/Chute.h new file mode 100644 index 0000000..98870e9 --- /dev/null +++ b/include/Components/Chute.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "Types/Item.hpp" +#include "Util/SharedBuffer.h" + +struct Chute +{ + struct ChuteLink + { + int8_t RelativeX{}; + int8_t RelativeY{}; + uint16_t Tick{}; + }; + struct ChuteItem + { + Item Item{}; + uint16_t ChuteEntered{}; + }; + struct ChuteData + { + std::deque ItemsInChute{}; + }; + +public: + Chute() = default; + Chute(Vector2i position, const Vector& chuteLinks) + : Data{static_cast(chuteLinks.size()), ChuteData{}} + { + for (int i{}; i < chuteLinks.size(); ++i) + { + Data.GetData()[i].RelativeX = position.x - chuteLinks[i].x; + Data.GetData()[i].RelativeY = position.y - chuteLinks[i].y; + } + } + + +public: + SharedBuffer Data{}; +}; \ No newline at end of file diff --git a/include/Components/Configs/ItemConfig.hpp b/include/Components/Configs/ItemConfig.hpp new file mode 100644 index 0000000..7413128 --- /dev/null +++ b/include/Components/Configs/ItemConfig.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +#include "Types/Item.hpp" + +struct ItemConfig +{ + Item ItemID{}; + std::string Name{}; +}; diff --git a/include/Components/Configs/RecipeConfig.hpp b/include/Components/Configs/RecipeConfig.hpp new file mode 100644 index 0000000..c087937 --- /dev/null +++ b/include/Components/Configs/RecipeConfig.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "ItemConfig.hpp" + +struct RecipeConfig +{ + std::vector Ingredients{}; + std::vector Results{}; + float ProcessingTime{}; + uint32_t ID{ std::numeric_limits::max() }; +}; diff --git a/include/Components/Configs/WorldConfig.hpp b/include/Components/Configs/WorldConfig.hpp new file mode 100644 index 0000000..886c326 --- /dev/null +++ b/include/Components/Configs/WorldConfig.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include "flecs.h" + +#include "ItemConfig.hpp" +#include "RecipeConfig.hpp" +#include "Components/Misc.hpp" + +class WorldConfig +{ +public: + void RegisterItem(const std::string& name); + +private: + std::vector Items{}; + std::vector Recipes{}; + +}; + +inline void Flecs_Configs(flecs::world& world) +{ + // std::string opaque support + world.component() + .opaque(flecs::String) + .serialize([](const flecs::serializer *s, const std::string *data) { + const char *str = data->c_str(); + return s->value(flecs::String, &str); + }) + .assign_string([](std::string *data, const char *value) { + *data = value; + }); + + // std::vector opaque support + world.component>() + .opaque(std_vector_support); + + // ItemConfig + world.component() + .member("ItemID") + .member("Name"); + + // RecipeConfig + world.component() + .member>("Ingredients") + .member>("Results") + .member("ProcessingTime") + .member("ID"); +} diff --git a/include/Components/Inventory.hpp b/include/Components/Inventory.hpp new file mode 100644 index 0000000..f4b6168 --- /dev/null +++ b/include/Components/Inventory.hpp @@ -0,0 +1,218 @@ +#pragma once + +#include + +#include "flecs.h" + +#include "Types/Item.hpp" +#include "Types/Filter.h" +#include "Util/SharedBuffer.h" +#include "Util/Span.h" + +template +struct InventoryT +{ + struct InventoryMeta + { + IntegralType MaxSize{ std::numerics_limits::max() }; + }; + +public: + InventoryT() = default; + InventoryT(const Vector>& items) + { + Slots = { static_cast(items.size()), InventoryMeta{} }; + + for (int i{}; i < items.size(); ++i) + { + DEV_ASSERT(items[i]->Item.ItemID != Item::null); + } + } + +public: + IntegralType GetItemsAmount(uint16_t id) const { Assert(id); return Slots[id]; } + IntegralType GetItemsAmount(Item item) const { return GetItemsAmount(item.ItemID); } + + void RemoveItems(uint16_t id, IntegralType amount) { Assert(id); Slots[id]-= amount; } + void RemoveItems(Item id, IntegralType amount) { RemoveItems(id.ItemID, amount); } + template + void RemoveItems(ItemAmountT amount) { RemoveItems(amount.Item, amount.Amount); } + + void AddItems(uint16_t id, IntegralType amount) { Assert(id); Slots[id]+= amount; } + void AddItems(Item id, IntegralType amount) { AddItems(id.ItemID, amount); } + template + void AddItems(ItemAmountT item) { AddItems(item.Item.ItemID, item.Amount); } + template + void AddItems(const InventoryT other) + { + DEV_ASSERT(Slots.GetSize() == other.Slots.GetSize()); + for (uint32_t i{}; i < Slots.GetSize(); ++i) + Slots[i] += other.Slots[i]; + } + + void Clear() + { + for (IntegralType i{}; i < Slots.GetSize(); ++i) + Slots[i] = 0; + } + + void Assert(uint16_t id) + { + DEV_ASSERT(id < Slots.GetSize() && id != Item::null); + } + +public: + operator bool() const { return Slots; } + +public: + SharedBuffer Slots; +}; + +typedef InventoryT Inventory16; +typedef InventoryT Inventory32; +typedef InventoryT Inventory64; + +typedef Inventory32 Inventory; +typedef Inventory64 WorldInventory; + +struct FixedInventoryEntry +{ + FixedInventoryEntry() = default; + FixedInventoryEntry(Item item, uint16_t amount, uint16_t maxAmount) : Item{ item }, Amount{ amount }, MaxAmount{ maxAmount } {}; + + Item Item{}; + uint16_t Amount{}; + uint16_t MaxAmount{std::numeric_limits::max()}; +}; + +template +struct FixedInventoryBase +{ + FixedInventoryBase() = default; + + template + FixedInventoryBase(const It& it) + { + int counter{}; + for (const auto& val : it) + Data[counter++] = val; + } + + tcb::span GetInventoryData() + { + return {&Data[0], InventorySize); + } + tcb::span GetInventoryData() const + { + return {&Data[0], InventorySize}; + } + + uint8_t InventorySize{ Size }; + std::array Data{}; +}; + +typedef FixedInventoryBase<1> FixedInventory1; +typedef FixedInventoryBase<2> FixedInventory2; +typedef FixedInventoryBase<3> FixedInventory3; +typedef FixedInventoryBase<4> FixedInventory4; +typedef FixedInventoryBase<5> FixedInventory5; +typedef FixedInventoryBase<6> FixedInventory6; +typedef FixedInventoryBase<7> FixedInventory7; +typedef FixedInventoryBase<8> FixedInventory8; + +struct InventoryAreaOfEffect +{ + InventoryAreaOfEffect(bool isCircle, uint8_t size) + : IsCircle{ isCircle } + , Size{ size } + { + } + + uint8_t IsCircle : 1; + uint8_t Size : 7; +}; +static_assert(sizeof(InventoryAreaOfEffect) == 1); + +struct ItemProcessor +{ + ItemProcessor() = default; + ItemProcessor(uint32_t ticks) : ProcessedTicks{ ticks } {}; + + uint32_t ProcessedTicks; +}; + + +inline void Flecs_Inventory(flecs::world& world) +{ + world.component() + .member("Item") + .member("Amount") + .member("MaxAmount"); + + world.component() + .add(flecs::Inheritable) + .opaque(world.vector()) + .serialize([](const flecs::serializer *s, const FixedInventory1 *data) { + for (uint8_t i = 0; i < data->InventorySize; ++i) + s->value(data->Data[i]); + return 0; + }) + .count([](const FixedInventory1 *data) -> size_t { + return data->InventorySize; + }) + .ensure_element([](FixedInventory1 *data, size_t elem) -> FixedInventoryEntry* { + return &data->Data[elem]; + }); + + world.component().is_a(); + world.component().is_a(); + world.component().is_a(); + world.component().is_a(); + world.component().is_a(); + world.component().is_a(); + world.component().is_a(); + + world.component() + .opaque(world.vector()) + .serialize([](const flecs::serializer *s, const Inventory *data) { + if (!data->Slots) return 0; + for (uint32_t i = 0; i < data->Slots.GetSize(); ++i) + s->value(data->Slots[i]); + return 0; + }) + .count([](const Inventory *data) -> size_t { + if (!data->Slots) return 0; + return data->Slots.GetSize(); + }); + + world.component() + .add(flecs::Singleton) + .opaque(world.vector()) + .serialize([](const flecs::serializer *s, const Inventory *data) { + if (!data->Slots) return 0; + for (uint64_t i = 0; i < data->Slots.GetSize(); ++i) + s->value(data->Slots[i]); + return 0; + }) + .count([](const Inventory *data) -> size_t { + if (!data->Slots) return 0; + return data->Slots.GetSize(); + }); + + world.component() + .opaque(world.component() + .member("IsCircle") + .member("Size")) + .serialize([](const flecs::serializer *s, const InventoryAreaOfEffect *data) { + uint8_t isCircle = data->IsCircle; + uint8_t size = data->Size; + s->member("IsCircle"); + s->value(isCircle); + s->member("Size"); + s->value(size); + return 0; + }); + + world.component() + .member("ProcessedTicks"); +} \ No newline at end of file diff --git a/include/Components/Misc.hpp b/include/Components/Misc.hpp new file mode 100644 index 0000000..f99a2f2 --- /dev/null +++ b/include/Components/Misc.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "flecs.h" + +#include + +struct Vector2 +{ + Vector2() = default; + Vector2(int32_t x, int32_t y): X{ x }, Y{ y } {}; + + int32_t X{}, Y{}; + + Vector2 operator+(const Vector2& other) const { return { X + other.X, Y + other.Y }; } + Vector2 operator-(const Vector2& other) const { return { X - other.X, Y - other.Y }; } +}; + +struct TilePosition +{ + Vector2 Position; +}; + +struct Level +{ + Level() = default; + Level(uint8_t level) : Val{ level } {}; + + uint8_t Val; +}; + +template > +flecs::opaque std_vector_support(flecs::world& world) { + return flecs::opaque() + .as_type(world.vector()) + .serialize([](const flecs::serializer *s, const Vector *data) { + for (const auto& el : *data) + s->value(el); + return 0; + }) + .count([](const Vector *data) { + return data->size(); + }) + .resize([](Vector *data, size_t size) { + data->resize(size); + }) + .ensure_element([](Vector *data, size_t elem) { + if (data->size() <= elem) + data->resize(elem + 1); + return &data->data()[elem]; + }); +} + +inline void Flecs_Misc(flecs::world& world) +{ + world.component() + .member("x") + .member("y"); + + world.component() + .member("Position"); + + world.component() + .member("Val"); +} diff --git a/include/Components/Resource.hpp b/include/Components/Resource.hpp new file mode 100644 index 0000000..4e44605 --- /dev/null +++ b/include/Components/Resource.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "flecs.h" + +#include "Tick.hpp" + +struct ResourceInfo +{ + uint16_t ResourceID; +}; + +struct ResourceHealth +{ + uint16_t MaxHealth{}; + uint16_t Health{}; + uint16_t RenewalTicks{}; + uint16_t Renewal{}; +}; + +struct ResourceTick : public TickAccumulator +{}; + +struct Renewing +{}; + + +inline void Flecs_Resource(flecs::world& world) +{ + world.component() + .member("ResourceID"); + + world.component() + .member("MaxHealth") + .member("Health") + .member("RenewalTicks") + .member("Renewal"); + + world.component() + .is_a(); + + world.component() + .add(); + + world.system() + .without() + .each([](ResourceInfo) {}); + +} \ No newline at end of file diff --git a/include/Components/Support.h b/include/Components/Support.h new file mode 100644 index 0000000..bacd574 --- /dev/null +++ b/include/Components/Support.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +struct Support +{ + uint8_t MaxSupport : 5; + bool SupportsUp : 1; + bool SupportsRight : 1; + bool SupportsLeft : 1; + uint8_t SupportsAvailable : 5; + bool SupportedByBottom : 1; + bool SupportedByRight : 1; + bool SupportedByLeft : 1; +}; + +struct RequiresSupport +{ +}; \ No newline at end of file diff --git a/include/Components/Sync.h b/include/Components/Sync.h new file mode 100644 index 0000000..3f568f6 --- /dev/null +++ b/include/Components/Sync.h @@ -0,0 +1,33 @@ +// #pragma once +// #include "core/object/ref_counted.h" + +// struct Sync +// { }; + +// class FactoryEntity; + +// struct NodePtr +// { +// NodePtr() = default; +// NodePtr(FactoryEntity* node) : Node{ node } {} + +// template +// T* AsNode() const +// { +// static_assert(std::is_base_of_v); +// DEV_ASSERT(Object::cast_to(Node)); +// return static_cast(Node); +// } + +// FactoryEntity* Node{}; +// }; + +// class Archetype; + +// struct ArchetypePtr +// { +// ArchetypePtr() = default; +// ArchetypePtr(Ref& archetpye) : Archetype{ archetpye } {} + +// Ref Archetype; +// }; \ No newline at end of file diff --git a/include/Components/Tick.hpp b/include/Components/Tick.hpp new file mode 100644 index 0000000..be2f7ec --- /dev/null +++ b/include/Components/Tick.hpp @@ -0,0 +1,44 @@ +#pragma once +#include + +#include "flecs.h" + +struct TickAccumulator +{ + uint16_t MaxTick{1}; + uint16_t AccumulatedTick; + + constexpr bool Finished() const + { + return AccumulatedTick >= MaxTick; + } +}; + +struct Freezes +{}; + +inline void Flecs_Tick(flecs::world& world) +{ + world.component() + .member("MaxTick") + .member("AccumulatedTick") + .add(flecs::Inheritable); + + world.component(); + + world.system("Tick Increase") + .kind(flecs::PreUpdate) + .without() + .each([] (TickAccumulator& tick) + { + ++tick.AccumulatedTick; + }); + + world.system("Tick Reset") + .kind(flecs::PostUpdate) + .without() + .each([] (TickAccumulator& tick) + { + tick.AccumulatedTick -= tick.MaxTick * tick.Finished(); + }); +} \ No newline at end of file diff --git a/include/Core/Chunk.h b/include/Core/Chunk.h new file mode 100644 index 0000000..0cb2791 --- /dev/null +++ b/include/Core/Chunk.h @@ -0,0 +1,177 @@ +#pragma once + +#include "modules/factory/include/Data/Tile.h" +#include "core/math/vector2i.h" +#include +#include + +struct Chunk +{ +public: + typedef uint8_t CoordinateType; + static constexpr int ChunkSizePowerOfTwo = 6; + static constexpr int ChunkSize = 1 << ChunkSizePowerOfTwo; + static constexpr int TotalChunkTiles = ChunkSize * ChunkSize; + static constexpr int ChunkMask = (1 << ChunkSizePowerOfTwo) - 1; + static_assert(sizeof(CoordinateType) * 8 >= ChunkSizePowerOfTwo, "CoordinateType is too small to support chunk size"); + + static constexpr uint8_t WorldToLocal(int world) { return world & ChunkMask; } + + std::array Tiles; + +public: + static void Assert(int x, int y) { DEV_ASSERT(x < ChunkSize && x >= 0 && y < ChunkSize && y >= 0); } +public: + Tile GetTile(int x, int y) const { Assert(x, y); return Tiles[y * ChunkSize + x]; } + Tile GetTile(Vector2i pos) const { return GetTile(pos.x, pos.y); } + const Tile& GetTileRef(int x, int y) const { Assert(x, y); return Tiles[y * ChunkSize + x]; } + Tile& GetTile(int x, int y) { Assert(x, y); return Tiles[y * ChunkSize + x]; } + Tile& GetTile(Vector2i pos) { return GetTile(pos.x, pos.y); } +}; + +struct ChunkCoordinate +{ + ChunkCoordinate() = default; + ChunkCoordinate(int x, int y) : X{ static_cast(x) }, Y{ static_cast(y) } { DEV_ASSERT(x >= 0 && x < Chunk::ChunkSize && y >= 0 && y < Chunk::ChunkSize); } + ChunkCoordinate(Vector2i pos) : ChunkCoordinate{pos.x, pos.y} {} + + uint8_t X{}; + uint8_t Y{}; + + operator Vector2i() const { return Vector2i(X, Y); } +}; + +static_assert(sizeof(ChunkCoordinate) / 2 >= Chunk::ChunkSizePowerOfTwo / 8); + +struct EntityTile final +{ +public: + EntityTile() = default; + EntityTile(entt::entity entity, int worldX, int worldY) + : Entity{ entity } + , ChunkX{ Chunk::WorldToLocal(worldX) } + , ChunkY{ Chunk::WorldToLocal(worldY) } + {} + +public: + entt::entity Entity{}; + Chunk::CoordinateType ChunkX{}; + Chunk::CoordinateType ChunkY{}; +}; + +struct ChunkKey +{ +public: + static constexpr int16_t WorldToChunk(int pos) { return static_cast(pos >> Chunk::ChunkSizePowerOfTwo); } + static constexpr int32_t ChunkToWorld(int16_t pos) { return pos << Chunk::ChunkSizePowerOfTwo; } + +public: + int16_t X{}, Y{}; + +public: + ChunkKey() = default; + ChunkKey(int WorldX, int WorldY) : X{ WorldToChunk(WorldX) }, Y{ WorldToChunk(WorldY) } {} + +public: + uint64_t hash64() const { static_assert(sizeof(uint32_t) == sizeof(ChunkKey)); return std::hash{}(*reinterpret_cast(this)); } + uint32_t hash() const { return static_cast(hash64()); } + static uint32_t hash(ChunkKey key) { return key.hash(); } + + Rect2i GetBounds() const { return Rect2i{ChunkToWorld(X), ChunkToWorld(Y), 1 << Chunk::ChunkSizePowerOfTwo, 1 << Chunk::ChunkSizePowerOfTwo}; } + +public: + bool operator==(const ChunkKey& rhs) const { return hash() == rhs.hash(); } +}; + +static_assert(sizeof(ChunkKey) == 4); + +struct ChunkData +{ +public: + void MarkAsPersistant(entt::entity entity); + void RemovePersistance(entt::entity entity); + void Clear() + { + Chunk = {}; + Entities.resize(0); + } + +public: + std::unique_ptr Chunk{}; + std::vector Entities{}; + std::vector PersistantEntities{}; +}; + +struct ChunkCollection +{ +public: + const Chunk& GetChunk(int x, int y); + const Chunk& GetChunk(ChunkKey key); + Chunk const* TryGetChunk(int x, int y) const; + Chunk const* TryGetChunk(ChunkKey key) const; + ChunkData& GetChunkData(int x, int y); + ChunkData& GetChunkData(ChunkKey key); + + Tile GetTile(int x, int y); + Tile const* TryGetTile(int x, int y) const; + entt::entity GetEntity(int x, int y) const; + + void SetChunkTiles(int x, int y, std::unique_ptr&& chunk); + void SetChunkTiles(ChunkKey key, std::unique_ptr&& chunk); + void AddEntity(entt::entity entity, const Vector& claimedPositions); + void AddPersistantEntity(entt::entity entity, const Vector& claimedPositions); + + void MarkAsPersistant(entt::entity entity); + void RemovePersistance(entt::entity entity); + + void RemoveEntity(entt::entity entity); + void RemoveChunk(int x, int y); + void RemoveChunk(ChunkKey key); + +private: + int GetChunkIndex(int x, int y); + int GetChunkIndex(ChunkKey key); + int TryGetChunkIndex(int x, int y) const; + int TryGetChunkIndex(ChunkKey key) const; + Chunk& GetChunkInternal(int x, int y); + Chunk& GetChunkInternal(ChunkKey key); + Tile& GetTileInternal(int x, int y); + + void SetTile(Tile tile, int x, int y); + + void InvalidateCachedChunk(); + +private: + std::vector ChunkDatas; + HashMap ChunkMap{}; + ChunkKey CachedChunkKey{}; + int CachedChunk{-1}; +}; + +// struct LightValue +// { +// constexpr static uint8_t LightLevelBits = 5; +// constexpr static uint8_t PenetrationBits = 8 - LightLevelBits; +// constexpr static uint8_t MaxLightVal = (1 << LightLevelBits) - 1; +// constexpr static uint8_t MaxPenetration = (1 << PenetrationBits) - 1; + +// uint8_t Penetration : PenetrationBits; +// uint8_t Val : LightLevelBits; + +// LightValue() +// { +// Penetration = 1; +// Val = 0; +// } +// }; + +//static_assert(sizeof(LightValue) == 1); + +// struct LightChunk +// { +// std::array Tiles; + +// public: +// LightValue GetTile(int x, int y) const { Chunk::Assert(x, y); return Tiles[y * Chunk::ChunkSize + x]; } +// LightValue& GetTile(int x, int y) { Chunk::Assert(x, y); return Tiles[y * Chunk::ChunkSize + x]; } +// }; \ No newline at end of file diff --git a/include/Core/FactoryCommandQueue.h b/include/Core/FactoryCommandQueue.h new file mode 100644 index 0000000..0af7d1a --- /dev/null +++ b/include/Core/FactoryCommandQueue.h @@ -0,0 +1,197 @@ +#pragma once +#include "Util/StackAllocator.h" +#include "Util/Span.h" +#include "EnTT/entity/registry.hpp" +#include +#include +#include "Components/Sync.h" +#include "Core/FactoryWorld.h" + +struct FactoryCommand +{ + void* Data; + entt::entity Entity; + void(*Command)(FactoryWorld& world, entt::entity entity, void* data); +}; + +class FactoryCommandQueue final +{ + static constexpr int DefaultAllocatorSize = 1024 * 1024; + +public: + FactoryCommandQueue(size_t memorySize) + : Allocator{ memorySize } + {} + FactoryCommandQueue() + : Allocator{ DefaultAllocatorSize } + {} + +public: + + +public: + template + void SetComponentData(entt::entity entity, const T& component); + + template + void SetOrAddComponentData(entt::entity entity, const T& component); + + template + void AddIfNone(entt::entity entity, const T& component); + + template + void AddComponent(entt::entity entity, const T& component); + + template + void AddComponent(entt::entity entity); + + template + void RemoveComponent(entt::entity entity); + + //void RemoveEntity(entt::entity entity); + + void SyncEntity(entt::entity entity) { AddComponent(entity); } + void StopSyncingEntity(entt::entity entity) { RemoveComponent(entity); } + + void ExecuteAll(FactoryWorld& world); + + void Clear(); + + template + T* AllocateData() + { + static_assert(std::is_trivially_copyable_v); + return Allocator.allocate(1); + } + + template + T* AllocateData(Args... arguments) + { + static_assert(std::is_trivially_copyable_v); + T* data = AllocateData(); + new (data) T(arguments...); + return data; + } + + template + tcb::span* AllocateBuffer(uint32_t size) + { + static_assert(std::is_trivially_copyable_v); + auto spanData = Allocator.allocate>(); + auto data = Allocator.allocate(size); + for (uint32_t i{}; i < size; ++i) + data[i] = {}; + *spanData = tcb::span(data, size); + return spanData; + } + +private: + void ClearUnsafe() + { + Commands.clear(); + Allocator.reset(); + } + +private: + StackAllocator Allocator; + std::mutex Mutex; + std::vector Commands; +}; + +template +inline void FactoryCommandQueue::SetComponentData(entt::entity entity, const T& component) +{ + FactoryCommand command; + command.Entity = entity; + command.Data = AllocateData(component); + command.Command = [](entt::registry& registry, entt::entity entity, void* data) + { + registry.get(entity) = *static_cast(data); + }; + std::scoped_lock lock(Mutex); + Commands.push_back(command); +} + +template +inline void FactoryCommandQueue::SetOrAddComponentData(entt::entity entity, const T& component) +{ + FactoryCommand command; + command.Entity = entity; + command.Data = AllocateData(component); + command.Command = [](entt::registry& registry, entt::entity entity, void* data) + { + registry.emplace_or_replace(entity, *static_cast(data)); + }; + std::scoped_lock lock(Mutex); + Commands.push_back(command); +} + +template +inline void FactoryCommandQueue::AddIfNone(entt::entity entity, const T& component) +{ + FactoryCommand command; + command.Entity = entity; + command.Data = AllocateData(component); + command.Command = [](entt::registry& registry, entt::entity entity, void* data) + { + if (!registry.all_of(entity)) + registry.emplace(entity, *static_cast(data)); + }; + std::scoped_lock lock(Mutex); + Commands.push_back(command); +} + +template +inline void FactoryCommandQueue::AddComponent(entt::entity entity, const T& component) +{ + FactoryCommand command; + command.Entity = entity; + command.Data = AllocateData(component); + command.Command = [](entt::registry& registry, entt::entity entity, void* data) + { + registry.emplace(entity, *static_cast(data)); + }; + std::scoped_lock lock(Mutex); + Commands.push_back(command); +} + +template +inline void FactoryCommandQueue::AddComponent(entt::entity entity) +{ + FactoryCommand command; + command.Entity = entity; + command.Data = nullptr; + command.Command = [](entt::registry& registry, entt::entity entity, void* data) + { + registry.emplace(entity); + }; + std::scoped_lock lock(Mutex); + Commands.push_back(command); +} + +template +inline void FactoryCommandQueue::RemoveComponent(entt::entity entity) +{ + FactoryCommand command; + command.Entity = entity; + command.Data = nullptr; + command.Command = [](entt::registry& registry, entt::entity entity, void* data) + { + registry.remove(entity); + }; + std::scoped_lock lock(Mutex); + Commands.push_back(command); +} + +// inline void FactoryCommandQueue::RemoveEntity(entt::entity entity) +// { +// FactoryCommand command; +// command.Entity = entity; +// command.Data = nullptr; +// command.Command = [](entt::registry& registry, entt::entity entity, void* data) +// { +// registry.destroy(entity); +// }; +// std::scoped_lock lock(Mutex); +// Commands.push_back(command); +// } \ No newline at end of file diff --git a/include/Core/FactoryWorld.h b/include/Core/FactoryWorld.h new file mode 100644 index 0000000..de09f44 --- /dev/null +++ b/include/Core/FactoryWorld.h @@ -0,0 +1,159 @@ +#pragma once + +#include +#include "core/templates/vector.h" +#include "core/templates/hash_map.h" +#include "core/object/ref_counted.h" +#include "modules/noise/fastnoise_lite.h" +#include "core/io/resource.h" +#include "scene/2d/tile_map_layer.h" +#include "core/os/mutex.h" +#include "core/templates/hash_set.h" +#include "core/math/vector2i.h" + +#include "Components/Position.h" +#include "Components/Inventory.h" +#include "Components/Support.h" + +#include "Data/Tile.h" +#include "Util/ResourceAccess.h" +#include "Chunk.h" +#include "SystemBase.h" +#include "Data/WorldSettings.h" +#include "FactoryCommandQueue.h" +#include "WorldThreadData.h" + +class FactoryWorld; +class FactoryWorldInterface; + +// typedef ResourceAccess FactoryWorldAccess; + +enum FactoryError +{ + FACTORY_ERROR_NONE = 0, + FACTORY_ERROR_NOT_ENTITY, + FACTORY_ERROR_CANT_UPGRADE, + FACTORY_ERROR_FULLY_UPGRADED, + FACTORY_ERROR_NOT_ENOUGH_ITEMS, + FACTORY_ERROR_NO_SPACE, + FACTORY_ERROR_REQUIRES_SUPPORT, + FACTORY_ERROR_PATH_TOO_LONG, + FACTORY_ERROR_INVALID_PATH, + FACTORY_ERROR_INVALID_POS, + FACTORY_ERROR_MISC_ERROR, +}; + +struct ChunkUnlock final +{ + ChunkKey ChunkID{}; + Vector Items{}; +}; + +class FactoryWorld final +{ + friend class WorldSerializer; + friend class WorldLoader; +public: + FactoryWorld() = default; + ~FactoryWorld() = default; + +public: + void Tick(int amount = 1); + void Initialize(FactoryWorldInterface* worldInterface); + void Save(); + +public: + int32_t GetSeed() const { return Seed; } + + FactoryError CanPlaceEntity(int x, int y, Ref archetype); + FactoryError AddEntity(int x, int y, Ref archetype); + void RemoveEntity(FactoryEntity* node); + void RemoveEntity(int x, int y); + void RemoveEntity(entt::entity entity); + + // FactoryWorldAccess GetAccess() { return FactoryWorldAccess{ *this, WorldAccessMutex }; } + + entt::registry& GetRegistry() { return Registry; }; + const entt::registry& GetRegistry() const { return Registry; }; + auto& GetChunks() { return Chunks; } + const auto& GetChunks() const { return Chunks; } + auto& GetSettings() const { return WorldSettings; } + + bool IsValidCameraPos(Rect2i viewport) const; + +public: // Chunks + FactoryError TryUnlockChunk(ChunkKey chunk); + +private: + void RefreshUnlockedChunks(); + +private: + entt::entity CreateEntity(); + FactoryError AddEntity(int x, int y, Ref archetype, entt::entity entityID); + void InvalidateCachedChunk(); + +public: // UPGRADING + FactoryError TryUpgradeEntity(FactoryEntity* entity); + FactoryError TryUpgradeEntity(entt::entity entity); + FactoryError TryUpgradeEntity(const Vector2i& position); + + FactoryError CanUpgradeEntity(entt::entity entity) const; + FactoryError CanUpgradeEntity(FactoryEntity* entity) const; + FactoryError CanUpgradeEntity(const Vector2i& position); + +private: + void UpgradeEntity(entt::entity entity); + void UpgradeEntity(entt::entity entity, Ref archetype); + void SetEntityLevel(entt::entity entity, uint8_t level); + void SetEntityLevel(entt::entity entity, Ref archetype, uint8_t level); + +public: // QUERY + void HighlightUpgradableEntities(TileMapLayer* tilemap) const; + FactoryError FindChutePath(Vector& path, Vector2i startPos, Vector2i endPos) const; + Tile const* Raycast(Vector2i startPos, Vector2i endPos) const; + bool IsSupport(int x, int y) const; + bool IsSupport(entt::entity entity) const; + +public: // INVENTORY + void SetInventory(const Vector>& items); + Inventory& GetInventory() { return WorldInventory; } + const Inventory& GetInventory() const { return WorldInventory; } + Inventory GetWorldInventory(Vector2 position) const; + Inventory GetWorldInventory(entt::entity entity) const; + +public: // DATA + const Recipe& GetRecipe(Ref config) { return WorldInstanceData.GetRecipe(config); } + +public: // SUPPORT + // FactoryError CanPlaceSupport(int x, int y) const; + // FactoryError CanRemoveSupport(int x, int y) const; + // void RegisterSupport(int x, int y, Support& support); + // void RemoveSupport(int x, int y); + +private: + bool SupportCheckerHelper(entt::entity entity) const; + uint8_t SupportValueHelper(entt::entity entity) const; + uint8_t GetSupportValue(int x, int y) const; + bool CheckIfSupportHelper(entt::entity entity, Support& support) const; + + void ConnectSupports(Vector2i pos, Support& support, Vector2i direction); + +private: + Mutex WorldAccessMutex; + // std::shared_ptr CommandQueue = std::make_shared(); + // FactoryWorldInterface* Interface; + + ChunkCollection Chunks{}; + + entt::registry Registry{}; + + Ref WorldSettings{}; + + int32_t Seed{}; + int32_t LastDrawnFrame{}; + + Inventory64 WorldInventory; + WorldData WorldInstanceData; + Vector UnlockedChunks{ ChunkKey{} }; + Vector UnlockableChunks{}; +}; \ No newline at end of file diff --git a/include/Core/WorldGenerator.h b/include/Core/WorldGenerator.h new file mode 100644 index 0000000..841e3ac --- /dev/null +++ b/include/Core/WorldGenerator.h @@ -0,0 +1,95 @@ +#pragma once + +#include "Data/WorldSettings.h" +#include "Chunk.h" + +class FactoryWorld; + +// class WorldGenerator final +// { +// public: + + +// public: +// WorldGenerator() = default; +// WorldGenerator(Ref settings, int32_t seed); + +// public: +// // bool GenerateChunk(ChunkKey chunkKey, Chunk& chunk) const; +// // bool GenerateChunk(ChunkKey chunkKey, Chunk& chunk, Ref layer, Ref nextLayer = {}) const; + +// // Vector SpawnEntities(ChunkKey chunkKey, Chunk& chunk, const std::vector& persistantEntities = {}) const; +// // Vector SpawnEntities(ChunkKey chunkKey, Chunk& chunk, Ref layer, Ref nextLayer = {}, const std::vector& persistantEntities = {}) const; + +// // std::unique_ptr CreateChunkVisuals(ChunkKey chunkKey, Chunk& chunk); + +// public: +// void ThreadedGenerateChunk(ChunkKey chunkKey, std::function callback, const std::vector& persistantEntities = {}); + +// private: + +// public: +// Ref Settings{}; +// WorldGraph Graph{}; +// int32_t Seed{}; +// }; + +class ChunkGenerator final +{ +public: + struct SpawnedEntities + { + Ref Archetype{}; + Vector2i SpawnPosition{}; + Vector ClaimedPositions{}; + }; + + struct CreatedVisualsTile + { + CreatedVisualsTile() = default; + CreatedVisualsTile(uint16_t atlasCoordinateX, uint16_t atlasCoordinateY, uint16_t atlasIndex) : AtlasCoordinateX{ atlasCoordinateX }, AtlasCoordinateY{ atlasCoordinateY }, AtlasIndex{ atlasIndex } {}; + + uint16_t AtlasCoordinateX{}; + uint16_t AtlasCoordinateY{}; + uint16_t AtlasIndex{}; + }; + + + typedef std::array CreatedVisualsChunk; + typedef std::array ChunkShadowValues; + + typedef std::function&&)> CreatedChunkCallback; + typedef std::function&)> SpawnedEntitiesCallback; + typedef std::function VisualizedChunkCallback; + typedef std::function ShadowsCallback; + + ChunkGenerator() = default; +private: + ChunkGenerator(Ref settings, ChunkKey chunk, int32_t seed) : Settings{ settings }, ChunkInfo{ chunk }, Seed{ seed } {}; + +public: + static void GenerateChunk(Ref settings, ChunkKey chunkInfo, int seed, CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback = {}, VisualizedChunkCallback visualsCallback = {}, ShadowsCallback shadowsCallback = {}); + std::unique_ptr GenerateChunk(); + +private: + static void GenerateChunk(void* pData); + void GenerateChunkInternal(CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback, VisualizedChunkCallback visualsCallback, ShadowsCallback shadowsCallback); + + void GenerateChunkTiles() const; + Vector SpawnEntities(const std::vector& persistantEntities = {}) const; + std::unique_ptr CreateVisuals(); + std::unique_ptr CascadeShadows(); + + Pair, Ref> GetLayers() const; + void FillChunkCollection(int relativeX, int relativeY, ChunkCollection& collection) const; + void CascadeShadows_Recursive(std::array& values, int posX, int posY, int value); + + Tile GetTile(int x, int y) const; + bool InBounds(int x, int y) const; + +private: + Ref Settings{}; + ChunkKey ChunkInfo{}; + int32_t Seed{}; + std::unique_ptr TileArray{}; +}; \ No newline at end of file diff --git a/include/Core/WorldInstance.h b/include/Core/WorldInstance.h new file mode 100644 index 0000000..9ea1ffb --- /dev/null +++ b/include/Core/WorldInstance.h @@ -0,0 +1,20 @@ +#pragma once + +#include "flecs.h" + +#include "Components/Configs/WorldConfig.hpp" + + +class WorldInstance +{ +public: + WorldInstance() = default; + WorldInstance(const WorldConfig& worldConfig); + +private: + static void RegisterTypes(flecs::world& world); + + +private: + flecs::world EcsWorld{}; +}; \ No newline at end of file diff --git a/include/Types/Archetype.h b/include/Types/Archetype.h new file mode 100644 index 0000000..eacc0c8 --- /dev/null +++ b/include/Types/Archetype.h @@ -0,0 +1,155 @@ +// #pragma once + +// #include "core/io/resource.h" +// #include "scene/resources/packed_scene.h" +// #include "scene/resources/texture.h" +// #include "UpgradeLevel.h" +// #include "modules/factory/include/Data/Tile.h" +// #include "Core/Chunk.h" + + +// class SpawnDescription : public Resource +// { +// GDCLASS(SpawnDescription, Resource); +// public: +// static void _bind_methods(); + +// public: +// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) = 0; +// virtual void ClaimTiles(Vector& tiles, const ChunkCollection& chunk, Vector2i pos) {}; + +// public: +// int GetFilter() const { return Filter; } +// auto GetTile() const { return Tile; } + +// void SetFilter(int filter) { Filter = static_cast(filter); } +// void SetTile(Ref tile) { Tile = tile; } + +// protected: +// bool FilterTile(Tile tile) { return Tile.is_valid() ? (Tile->TileData == tile) : (tile.GetType() == Filter); } +// bool TryFilterTile(const ChunkCollection& chunks, int x, int y); +// bool TryFilterTile(const ChunkCollection& chunks, Vector2i pos); + +// protected: +// TILE_TYPE Filter{ TILE_NONE }; +// Ref Tile{}; +// }; + +// class SpawnNearby : public SpawnDescription +// { +// GDCLASS(SpawnNearby, SpawnDescription); +// public: +// static void _bind_methods(); + +// protected: +// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override; + +// public: +// int GetRange() const { return Range; } + +// void SetRange(int range) { Range = static_cast(range); } + +// private: +// int8_t Range{}; +// }; + +// class OccupiedTiles : public SpawnDescription +// { +// GDCLASS(OccupiedTiles, SpawnDescription); +// public: +// static void _bind_methods(); + +// protected: +// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override; +// virtual void ClaimTiles(Vector& tiles, const ChunkCollection& chunk, Vector2i pos) override; + +// public: +// TypedArray GetOffset() const { return VectorToTypedArrayVariant(Offsets); } + +// void SetOffset(TypedArray offsets) { Offsets = TypedArrayToVectorVariant(offsets); } + +// public: +// Vector Offsets; +// }; + +// class RequiredTiles : public SpawnDescription +// { +// GDCLASS(RequiredTiles, SpawnDescription); +// public: +// static void _bind_methods(); + +// protected: +// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override; + +// public: +// TypedArray GetOffset() const { return VectorToTypedArrayVariant(Offsets); } + +// void SetOffset(TypedArray offsets) { Offsets = TypedArrayToVectorVariant(offsets); } + +// public: +// Vector Offsets; +// }; + +// class LinkedTiles : public SpawnDescription +// { +// GDCLASS(LinkedTiles, SpawnDescription); +// public: +// static void _bind_methods(); + +// protected: +// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override; +// virtual void ClaimTiles(Vector& tiles, const ChunkCollection& chunk, Vector2i pos) override; + +// public: +// void ClaimTiles_Recursive(Vector& tiles, const ChunkCollection& chunk, Vector2i pos); + +// }; + +// class Archetype : public Resource +// { +// GDCLASS(Archetype, Resource); + +// public: +// static void _bind_methods(); + +// public: +// Ref GetScene() const { return Scene; } +// Ref GetPreviewScene() const { return PreviewScene; } +// Ref GetPreviewTexture() const { return PreviewTexture; } +// TypedArray GetOccupiedTiles() const { return VectorToTypedArray(SpawnConditions); } +// TypedArray GetUpgrades() const { return VectorToTypedArray(Upgrades); } +// int GetID() const { return ID; } + +// void SetScene(Ref scene) { Scene = scene; } +// void SetPreviewScene(Ref scene) { PreviewScene = scene; } +// void SetPreviewTetxture(Ref preview) { PreviewTexture = preview; } +// void SetOccupiedTiles(TypedArray tiles) { SpawnConditions = TypedArrayToVector(tiles); } +// void SetUpgrades(TypedArray upgrades) { Upgrades = TypedArrayToVector(upgrades); } +// void SetID(int id) { ID = id; } + +// public: +// Ref Scene{}; +// Ref PreviewScene{}; +// Ref PreviewTexture{}; +// Vector> Upgrades{}; +// Vector> SpawnConditions{}; +// int ID{ -1 }; +// }; + +// class PlaceableArchetype : public Resource +// { +// GDCLASS(PlaceableArchetype, Resource); +// public: +// static void _bind_methods(); + +// public: +// auto GetArchetype() const { return Archetype; } +// auto GetPlaceCosts() const { return VectorToTypedArray(PlaceCosts); } + +// void SetArchetype(Ref archetype) { Archetype = archetype; } +// void SetPlaceCosts(TypedArray costs) { PlaceCosts = TypedArrayToVector(costs); } + +// public: +// Ref Archetype{}; +// Vector> PlaceCosts{}; +// }; \ No newline at end of file diff --git a/include/Types/Filter.h b/include/Types/Filter.h new file mode 100644 index 0000000..beb8007 --- /dev/null +++ b/include/Types/Filter.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Item.hpp" + +struct alignas(4) ItemFilter final +{ + uint16_t FilterItem0; + uint16_t FilterItem1; + uint16_t FilterItem2; + //Item::ItemFlags FilterFlags; + +public: + inline bool ApplyFilter(Item item) + { + return //(item.Flags & FilterFlags) && + (FilterItem0 == 0 || FilterItem0 == item.ItemID) && + (FilterItem1 == 0 || FilterItem1 == item.ItemID) && + (FilterItem2 == 0 || FilterItem2 == item.ItemID); + } +}; \ No newline at end of file diff --git a/include/Types/Item.hpp b/include/Types/Item.hpp new file mode 100644 index 0000000..ce977de --- /dev/null +++ b/include/Types/Item.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include "flecs.h" + +#include "config.h" + +struct Item final +{ +public: + static constexpr UnderlyingItemT null = std::numeric_limits::max(); + +public: + UnderlyingItemT ItemID{ null }; + +public: + Item() = default; + Item(int id) + { + DEV_ASSERT(id != null); + ItemID = static_cast(id); + } + +}; + +template +struct ItemAmountT final +{ +public: + ItemAmountT(Item item, IntegralType amount) : ItemId{ item }, Amount{ amount } {} + ItemAmountT() = default; +public: + Item ItemId; + IntegralType Amount; +}; + +typedef ItemAmountT ItemAmount16; +typedef ItemAmountT ItemAmount32; +typedef ItemAmountT ItemAmount64; +typedef ItemAmount16 ItemAmount; + +inline void Flecs_Item(flecs::world& world) +{ + world.component() + .member("ItemID"); + + world.component() + .member("ItemRef") + .member("Amount"); +} \ No newline at end of file diff --git a/include/Types/LayerConfigs.h b/include/Types/LayerConfigs.h new file mode 100644 index 0000000..3d9a0d5 --- /dev/null +++ b/include/Types/LayerConfigs.h @@ -0,0 +1,152 @@ +#pragma once + +// #include "Tile.h" +// #include "core/object/ref_counted.h" +// #include "modules/noise/fastnoise_lite.h" +// #include "core/io/resource.h" +// #include "modules/factory/include/Data/Archetype.h" +// #include "modules/factory/include/Util/Helpers.h" +// #include "modules/factory/include/Data/WorldGraph/WorldGraphVisualNode.h" +// #include "scene/resources/image_texture.h" + +// struct LayerTileConfig final : public Resource +// { +// GDCLASS(LayerTileConfig, Resource); + +// public: +// static void _bind_methods(); + +// public: +// Ref GetTileGenerator() const { return TileGenerator; } +// Ref GetTile() const { return Tile; } +// Color GetPreviewColor() const { return PreviewColor; } + +// void SetTileGenerator(Ref gen) { TileGenerator = gen; } +// void SetTile(Ref tile) { Tile = tile; } +// void SetPreviewColor(Color color) { PreviewColor = color; } + +// public: +// Ref TileGenerator{}; +// Ref Tile{}; +// Color PreviewColor{}; +// }; + +// struct LayerConfig final : public Resource +// { +// GDCLASS(LayerConfig, Resource); + +// public: +// static void _bind_methods(); + +// public: +// int GetStartChunk() const { return StartChunk; } +// auto GetTiles() const { return VectorToTypedArray(Tiles); } +// auto GetRegisteredNodes() const { return VectorToTypedArray(AllNodes); } +// auto GetArchetypes() const { return VectorToTypedArray(SpawningArchetypes); } + +// void SetStartChunk(int height) { StartChunk = height; } +// void SetTiles(TypedArray tiles); +// void SetRegisteredNodes(TypedArray allNodes) { AllNodes = TypedArrayToVector(allNodes); } +// void SetArchetypes(TypedArray archetypes) { SpawningArchetypes = TypedArrayToVector(archetypes); } + +// void RegisterVisualNode(Ref node) { if (!HasVisualNode(node)) AllNodes.push_back(node); } +// void RemoveVisualNode(Ref node) { AllNodes.erase(node);} +// bool HasVisualNode(Ref node) const { return AllNodes.has(node); } + +// bool IsValid() const; +// bool CanConnect(Ref from, Ref to) const; + +// Ref CreateTexture(Vector2i chunk, int seed) const; + +// public: +// int StartChunk{}; +// Vector> Tiles{}; +// Vector> AllNodes{}; +// Vector> SpawningArchetypes{}; +// Vector> UnlockedItems{}; +// Vector> UnlockedBuildings{}; +// Vector> UnlockedRecipes{}; +// }; + +// struct LayerConfig_Filler final : public Resource +// { +// GDCLASS(LayerConfig_Filler, Resource); +// public: +// static void _bind_methods(); + +// public: +// float GetPassagewayWidthRatio() const { return PassagewayWidthRatio; } +// Ref GetNoise() const { return NoiseGenerator; } +// Ref GetTile() const { return Tile; } + +// void SetPassagewayWidthRatio(float ratio) { PassagewayWidthRatio = ratio; } +// void SetNoise(Ref noise) { NoiseGenerator = noise; } +// void SetTile(Ref tile) { Tile = tile; } + +// public: +// float PassagewayWidthRatio = 0.05f; +// Ref NoiseGenerator{}; +// Ref Tile{}; +// }; + +// struct LayerConfig_Ore final : public Resource +// { +// GDCLASS(LayerConfig_Ore, Resource); +// public: +// static void _bind_methods(); + +// public: +// float GetRatio() const { return Ratio; } +// Ref GetOre() { return OreArchetype; } +// Ref GetNoise() const { return NoiseGenerator; } + +// void SetRatio(float ratio) { Ratio = ratio; } +// void SetOre(Ref archetype) { OreArchetype = archetype; } +// void SetNoise(Ref noise) { NoiseGenerator = noise; } + +// public: +// float Ratio = 0.05f; +// Ref OreArchetype; +// Ref NoiseGenerator{}; +// }; + +// struct LayerConfig_Plant final : public Resource +// { +// GDCLASS(LayerConfig_Plant, Resource); +// public: +// static void _bind_methods(); + +// public: +// auto GetGrowTile() const { return GrowTileID; } +// float GetGrowChance() const { return GrowChance; } +// Ref GetPlantArchetype() const { return PlantArchetype; } + +// void SetGrowTile(const Ref& growTile) { GrowTileID = growTile; } +// void SetGrowChance(float chance) { GrowChance = chance; } +// void SetPlantArchetype(const Ref& archetype) { PlantArchetype = archetype; } + +// public: +// Ref GrowTileID{}; // Tile it can grow on +// float GrowChance{}; // Value from 0 to 1 +// Ref PlantArchetype{}; +// }; + +// struct LayerConfig_Liquid final : public Resource +// { +// GDCLASS(LayerConfig_Liquid, Resource); +// public: +// static void _bind_methods() {}; +// public: +// }; + +// struct LayerConfig_NPC final : public Resource +// { +// GDCLASS(LayerConfig_NPC, Resource); +// public: +// static void _bind_methods() {}; +// }; + +// template +// struct CompareLayerHeights { +// bool operator()(const Ref p_a, const Ref p_b) const { return p_b->MaxHeight < p_a->MaxHeight; } +// }; \ No newline at end of file diff --git a/include/Types/Recipe.h b/include/Types/Recipe.h new file mode 100644 index 0000000..7eb85d2 --- /dev/null +++ b/include/Types/Recipe.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Item.hpp" +#include "Util/SharedBuffer.h" +#include "Util/Span.h" + +struct RecipeConfig; + +struct Recipe final +{ + struct RecipeMeta final + { + uint8_t IngredientsAmount; + uint8_t ResultsAmount; + uint16_t ProcessingTime; + uint32_t RecipeID{}; + }; + +public: + Recipe() = default; + Recipe(const RecipeConfig& recipe); + +public: + tcb::span GetIngredients() const { assert(Data); return tcb::span(Data.Ptr(), Data.GetMetaData()->IngredientsAmount); }; + tcb::span GetResults() const { assert(Data); return tcb::span(Data.Ptr() + Data.GetMetaData()->IngredientsAmount, Data.GetMetaData()->ResultsAmount); }; + uint32_t GetRecipeID() const { return Data.GetMetaData()->RecipeID; } + uint16_t GetProcessingTime() const { return Data.GetMetaData()->ProcessingTime; } + +public: + operator bool() const { return Data; } + +public: + SharedBuffer Data; +}; + + + + + + + + + + + + diff --git a/include/Types/Tile.h b/include/Types/Tile.h new file mode 100644 index 0000000..0f21028 --- /dev/null +++ b/include/Types/Tile.h @@ -0,0 +1,176 @@ +#pragma once + +#include "core/string/string_name.h" +#include "core/io/resource.h" +#include "scene/resources/texture.h" +#include "modules/factory/thirdParty/EnTT/entity/entity.hpp" +#include "modules/factory/include/Util/Helpers.h" + +// If tile data uses all 4 bytes, it is an entity, +// if it uses only 2 bytes it is a tile +// if it is 0, it is air +// struct Tile final +// { +// private: +// union TileData +// { +// uint32_t Data; +// entt::entity Entity; +// uint16_t TileID; +// }; +// static_assert(sizeof(TileData) == sizeof(uint32_t)); +// TileData Data{}; + +// static constexpr uint32_t TileMask = 0x80000000; + +// public: +// Tile() = default; +// Tile(uint16_t id) { Data.Data = id | TileMask; } +// Tile(entt::entity entity) { DEV_ASSERT(IsValidEntity(entity)); Data.Entity = entity; } + +// public: +// constexpr bool IsAir() const { return Data.Data == 0; } +// constexpr bool IsTile() const { return (Data.Data & TileMask) != 0; } +// constexpr bool IsEntity() const { return !IsAir() && !IsTile(); } +// static constexpr bool IsValidEntity(entt::entity entity) { return (((uint32_t)entity) & TileMask) == 0; } + +// public: +// entt::entity GetEntity() const { DEV_ASSERT(IsEntity()); return Data.Entity; } +// uint16_t GetTileID() const { DEV_ASSERT(IsTile()); return Data.TileID; } +// }; + +enum TILE_TYPE : uint8_t +{ + TILE_AIR, + TILE_FILLER, + TILE_LIQUID, + TILE_ORE, + TILE_NPC, + TILE_PLANT, + TILE_MAX, + + TILE_NONE = 0b111, +}; + +VARIANT_ENUM_CAST(TILE_TYPE); +static_assert(TILE_MAX <= TILE_NONE); +const char* TileTypeEnumString = "Air,Filler,Liquid,Ore,Npc,Plant,,None"; + +struct Tile final +{ + typedef uint16_t TileDataType; + static constexpr int BytesID = 12; + static constexpr int BytesType = 3; + static constexpr int BytesEntity = 1; + static constexpr TileDataType MaskID = 0b0000111111111111; + static constexpr TileDataType MaskType = 0b0111000000000000; + static constexpr TileDataType MaskEntity = 0b1000000000000000; + static constexpr TileDataType InvalidID = MaskID; + +public: + Tile() = default; + explicit Tile(TileDataType id) + { + Data = id; + } + +public: + void FlagAsEntity() { Data |= MaskEntity; } + void RemoveEntityFlag() { Data &= ~MaskEntity; } + constexpr bool HasEntity() const { return Data & MaskEntity; } + + constexpr uint16_t GetID() const { return Data & MaskID; } + constexpr bool IsAir() const { return GetType() == TILE_AIR; } + constexpr bool IsFiller() const { return GetType() == TILE_FILLER; } + constexpr bool IsLiquid() const { return GetType() == TILE_LIQUID; } + constexpr bool IsOre() const { return GetType() == TILE_ORE; } + constexpr bool IsPlant() const { return GetType() == TILE_PLANT; } + constexpr bool IsNPC() const { return GetType() == TILE_NPC; } + + constexpr TILE_TYPE GetType() const { return static_cast((Data & MaskType) >> BytesID); } + constexpr bool IsType(TILE_TYPE type) const { return GetType() == type; } + + void SetID(uint16_t id) { Data = (Data & ~MaskID) | (id & MaskID); } + void SetAir() { SetType(TILE_AIR); } + void SetFiller() { SetType(TILE_FILLER); } + void SetLiquid() { SetType(TILE_LIQUID); } + void SetOre() { SetType(TILE_ORE); } + void SetPlant() { SetType(TILE_PLANT); } + void SetNPC() { SetType(TILE_NPC); } + + void SetType(TILE_TYPE type) { Data = (Data & ~MaskType) | (type << BytesID); } + + constexpr uint16_t AsInt() const { return Data; } + constexpr bool IsValid() const { return Data == MaskID; } + +private: + TileDataType Data{ MaskID }; +}; + +inline constexpr bool operator==(Tile lhs, Tile rhs) { return lhs.AsInt() == rhs.AsInt(); } +inline constexpr bool operator!=(Tile lhs, Tile rhs) { return lhs.AsInt() != rhs.AsInt(); } + +static_assert(sizeof(Tile) == 2); + +class TextureWeight final : public Resource +{ + GDCLASS(TextureWeight, Resource); + static void _bind_methods(); +public: + Ref GetTexture() const { return Texture; } + uint16_t GetWeight() const { return Weight; } + + void SetTexture(Ref texture) { Texture = texture; } + void SetWeight(uint16_t weight) { Weight = weight; } + +public: + Ref Texture{}; + uint16_t Weight{1}; + uint16_t AtlasIndex{}; + uint16_t AtlasX{}; + uint16_t AtlasY{}; +}; + +class TileConfig; + +class TileTransitionConfig final : public Resource +{ + GDCLASS(TileTransitionConfig, Resource); + static void _bind_methods(); +public: + auto GetNeighbor() const { return Neighbor; } + auto GetPossibleTextures() const { return VectorToTypedArray(PossibleTextures); } + + void SetNeighbor(Ref neighbor) { Neighbor = neighbor; } + void SetPossibleTextures(TypedArray textures) { PossibleTextures = TypedArrayToVector(textures); } + +public: + Ref Neighbor; + Vector> PossibleTextures; +}; + +class TileConfig final : public Resource +{ + GDCLASS(TileConfig, Resource); +public: + static void _bind_methods(); + +public: + uint16_t GetID() const { return TileData.GetID(); } + TILE_TYPE GetType() const { return TileData.GetType(); } + auto GetTextures() const { return VectorToTypedArray(PossibleTextures); } + auto GetTransitions() const { return VectorToTypedArray(NeighborTransitions); } + auto GetLightResistance() const { return VectorToTypedArray(NeighborTransitions); } + + void SetID(uint16_t id) { TileData.SetID(id); } + void SetTextures(TypedArray val) { PossibleTextures = TypedArrayToVector(val); } + void SetTransitions(TypedArray val) { NeighborTransitions = TypedArrayToVector(val); } + void SetType(TILE_TYPE type) { TileData.SetType(type); } + void SetLightResistance(int resistance) { LightResistance = resistance; } + +public: + Tile TileData{}; + // Vector> PossibleTextures; + // Vector> NeighborTransitions; + int LightResistance{}; +}; \ No newline at end of file diff --git a/include/Types/UpgradeLevel.h b/include/Types/UpgradeLevel.h new file mode 100644 index 0000000..369ac06 --- /dev/null +++ b/include/Types/UpgradeLevel.h @@ -0,0 +1,25 @@ +// #pragma once + +// #include "core/io/resource.h" +// #include "core/templates/vector.h" +// #include "core/variant/dictionary.h" +// #include "modules/factory/include/Util/Helpers.h" +// #include "Item.h" + +// class UpgradeLevelConfig : public Resource +// { +// GDCLASS(UpgradeLevelConfig, Resource); +// public: +// static void _bind_methods(); + +// public: +// TypedArray GetUpgradeCosts() const { return VectorToTypedArray(UpgradeCost); } +// Dictionary GetUpgradeResults() const { return UpgradeResults; } + +// void SetUpgradeCosts(TypedArray upgradeCosts) { UpgradeCost = TypedArrayToVector(upgradeCosts); } +// void SetUpgradeResults(Dictionary results) { UpgradeResults = results; } + +// public: +// Vector> UpgradeCost{}; +// Dictionary UpgradeResults{}; +// }; \ No newline at end of file diff --git a/include/Types/WorldGraph/WorldGraph.h b/include/Types/WorldGraph/WorldGraph.h new file mode 100644 index 0000000..465642c --- /dev/null +++ b/include/Types/WorldGraph/WorldGraph.h @@ -0,0 +1,30 @@ +#pragma once + +#include "WorldGraphVisualNode.h" + +class WorldGraph final +{ +public: + WorldGraph() = default; + WorldGraph(const Vector>& nodes); + + WorldGraph(const WorldGraph& other); + WorldGraph(WorldGraph&& other) noexcept = default; + + WorldGraph& operator=(const WorldGraph& other); + WorldGraph& operator=(WorldGraph&& other) noexcept = default; + +public: + Variant Execute(Ref node, const WorldNodeParameters& params) const; + WorldNodeBase* GetNode(Ref node) const; + +private: + void Compile(const Vector>& nodes); + std::unique_ptr CopyMemory(HashMap, WorldNodeBase*>& nodeMap) const; + +private: + int MemorySize{}; + std::unique_ptr CompiledData{}; + HashMap, WorldNodeBase*> NodeMap{}; +}; + diff --git a/include/Types/WorldGraph/WorldGraphAllocator.h b/include/Types/WorldGraph/WorldGraphAllocator.h new file mode 100644 index 0000000..0d95c1f --- /dev/null +++ b/include/Types/WorldGraph/WorldGraphAllocator.h @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include "modules/factory/include/Util/Span.h" + +struct WorldGraphAllocatorBase +{ + virtual ~WorldGraphAllocatorBase() = default; + + virtual void* Allocate(tcb::span data) = 0; + virtual void Clear() = 0; + + template + T* Allocate(const T& val) + { + return static_cast(Allocate(tcb::span(reinterpret_cast(&val), sizeof(T)))); + } +}; + +struct WorldGraphAllocator : public WorldGraphAllocatorBase +{ + virtual ~WorldGraphAllocator() = default; + WorldGraphAllocator() = default; + WorldGraphAllocator(uint32_t totalSize) : Data {std::make_unique(totalSize)}, Size{totalSize} {} + + virtual void* Allocate(tcb::span data) override + { + auto size = (data.size_bytes() + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*); // make sure aligment is 8/4 bytes for pointers + if (CurrentOffset + size > Size) + throw std::exception{}; + + std::memcpy(Data.get() + CurrentOffset, data.data(), data.size_bytes()); + CurrentOffset += size; + + return Data.get() + CurrentOffset - size; + } + + virtual void Clear() override + { + CurrentOffset = 0; + } + + void* GetCurrentAddress() const + { + return Data.get() + CurrentOffset; + } + + std::unique_ptr Data{}; + uint32_t Size{}; + uint32_t CurrentOffset{}; +}; + +struct WorldGraphSizeMeasurer : public WorldGraphAllocatorBase +{ + virtual ~WorldGraphSizeMeasurer() = default; + WorldGraphSizeMeasurer() = default; + + virtual void* Allocate(tcb::span data) override + { + TotalSize += (data.size_bytes() + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*); + return nullptr; + } + + virtual void Clear() override + { + TotalSize = 0; + } + + uint32_t TotalSize{}; +}; \ No newline at end of file diff --git a/include/Types/WorldGraph/WorldGraphNode.h b/include/Types/WorldGraph/WorldGraphNode.h new file mode 100644 index 0000000..fb4ff4b --- /dev/null +++ b/include/Types/WorldGraph/WorldGraphNode.h @@ -0,0 +1,665 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "WorldGraphAllocator.h" +#include "Util/FastNoiseLite.h" +#include "core/variant/variant.h" +#include "modules/factory/include/Core/Chunk.h" + +// // last bit determines if it is a boolean or a float +// // if it is a float, last bit of precision will be lost (not that bad) +// // if it is a bool, the bool value will be stored on the second bit +// // for floats the last bit is set +// // for bools the last bit is unset +// class BoolFloat +// { +// public: +// BoolFloat() = default; +// BoolFloat(bool val) {} +// BoolFloat(float val) {} + +// public: +// void SetFloat(float val) { Data = reinterpret_cast(val) | 0b1u; } +// void SetBool(bool val) { Data = (val << 1) & (~0b1u); } + +// constexpr float IsFloat() const { return Data & 0b1u; } +// constexpr float IsBool() const { return Data & (~0b1u); } + +// float GetFloat() const { _ASSERT(IsFloat()); return reinterpret_cast(Data); } +// float GetBool() const { _ASSERT(IsBool()); return Data; } + +// public: +// operator float() const { return GetFloat(); } + +// private: +// uint32_t Data{}; +// }; + +struct WorldNodeParameters; + +struct alignas(void*) WorldNodeBase +{ + virtual Variant Evaluate(const WorldNodeParameters& params) const = 0; + virtual Variant::Type GetReturnType() const = 0; + virtual Vector GetInputTypes() const = 0; + virtual bool IsValid() const = 0; + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const = 0; + virtual void SetInput(int index, WorldNodeBase* input) = 0; +}; + +struct WorldNodeParameters +{ + static constexpr int MaxQueryOffset = 4; + static constexpr int PaddedChunkSide = Chunk::ChunkSize + MaxQueryOffset * 2; + static constexpr int PaddedChunkSize = PaddedChunkSide * PaddedChunkSide; + + typedef std::array TileArray; + + int X{ }; + int Y{ }; + int Seed{ }; + float FinalValueSubstract{ }; + + ChunkKey ChunkInfo{ }; + TileArray* GeneratedTiles{ }; + + Tile GetTile(int x, int y) const + { + auto bounds = GetGenerationBounds(); + if (unlikely(!bounds.has_point(Vector2i{x, y}))) + return {}; + // DEV_ASSERT(bounds.has_point(Vector2i{x, y})); + + return (*GeneratedTiles)[(y - bounds.position.y) * PaddedChunkSide + (x - bounds.position.x)]; + } + + static int GetArrayIndex(int x, int y) + { + return (y + MaxQueryOffset) * PaddedChunkSide + (x + MaxQueryOffset); + } + + Rect2i GetGenerationBounds() const + { + return ChunkInfo.GetBounds().grow(MaxQueryOffset); + } +}; + +template +struct TtoVariant +{ + typedef Variant TVariant; +}; + +template +struct WorldNodeTemplated : public WorldNodeBase +{ + std::array InputNodes{}; + + virtual Variant::Type GetReturnType() const override { return Variant::get_type_t(); } + virtual Vector GetInputTypes() const override + { + return { Variant::get_type_t()... }; + }; + virtual bool IsValid() const override + { + bool valid{ true }; + for (auto input : InputNodes) + { + valid = valid && input; + } + return valid; + } + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override { Allocator->Allocate(*this); } + + virtual Variant Evaluate(const WorldNodeParameters& params) const override + { + std::array results{}; + for (int i{}; i < sizeof...(Inputs); ++i) + { + results[i] = InputNodes[i]->Evaluate(params); + } + + auto EvaluateFunctor = [this](typename TtoVariant::TVariant... args) -> Variant + { + return EvaluateT(args.get_unsafe_t()...); + }; + + return std::apply(EvaluateFunctor, results); + } + + virtual void SetInput(int index, WorldNodeBase* input) override + { + InputNodes[index] = input; + } + + + virtual Return EvaluateT(Inputs...) const = 0; +}; + +struct WorldNode_Add : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return v0 + v1; + } +}; + +struct WorldNode_Subtract : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return v0 - v1; + } +}; + +struct WorldNode_Multiply : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return v0 * v1; + } +}; + +struct WorldNode_Divide : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return v0 / v1; + } +}; + +struct WorldNode_Modulo : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return fmod(v0, v1); + } +}; + +struct WorldNode_Equal : public WorldNodeTemplated +{ + virtual bool EvaluateT(float v0, float v1) const override + { + return v0 == v1; + } +}; + +struct WorldNode_Smaller : public WorldNodeTemplated +{ + virtual bool EvaluateT(float v0, float v1) const override + { + return v0 < v1; + } +}; + +struct WorldNode_Greater : public WorldNodeTemplated +{ + virtual bool EvaluateT(float v0, float v1) const override + { + return v0 > v1; + } +}; + +struct WorldNode_SmallerEqual : public WorldNodeTemplated +{ + virtual bool EvaluateT(float v0, float v1) const override + { + return v0 <= v1; + } +}; + +struct WorldNode_GreaterEqual : public WorldNodeTemplated +{ + virtual bool EvaluateT(float v0, float v1) const override + { + return v0 >= v1; + } +}; + +struct WorldNode_Negate : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return -v; + } +}; + +struct WorldNode_Abs : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::abs(v); + } +}; + +struct WorldNode_Ceil : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::ceil(v); + } +}; + +struct WorldNode_Floor : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::floor(v); + } +}; + +struct WorldNode_Sin : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::sin(v); + } +}; + +struct WorldNode_Cos : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::cos(v); + } +}; + +struct WorldNode_Tan : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::tan(v); + } +}; + +struct WorldNode_Exp : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::exp(v); + } +}; + +struct WorldNode_Pow : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return std::pow(v0, v1); + } +}; + +struct WorldNode_Max : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return std::max(v0, v1); + } +}; + +struct WorldNode_Min : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1) const override + { + return std::min(v0, v1); + } +}; + +struct WorldNode_Clamp : public WorldNodeTemplated +{ + virtual float EvaluateT(float v0, float v1, float v2) const override + { + return std::clamp(v0, v1, v2); + } +}; + +struct WorldNode_Round : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::round(v); + } +}; + +struct WorldNode_Log : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return std::log(v); + } +}; + +struct WorldNode_Square : public WorldNodeTemplated +{ + virtual float EvaluateT(float v) const override + { + return v * v; + } +}; + +struct WorldNode_Lerp : public WorldNodeTemplated +{ + virtual float EvaluateT(float from, float to, float weight) const override + { + return Math::lerp(from, to, weight); + } +}; + +struct WorldNode_OneMinus : public WorldNodeTemplated +{ + virtual float EvaluateT(float val) const override + { + return 1 - val; + } +}; + +struct WorldNode_And : public WorldNodeTemplated +{ + virtual bool EvaluateT(bool val0, bool val1) const override + { + return val0 && val1; + } +}; + +struct WorldNode_Or : public WorldNodeTemplated +{ + virtual bool EvaluateT(bool val0, bool val1) const override + { + return val0 || val1; + } +}; + +struct WorldNode_Constant : public WorldNodeBase +{ + float Value{}; + virtual Variant Evaluate(const WorldNodeParameters& params) const override + { + return Value; + } + virtual Variant::Type GetReturnType() const override { return Variant::FLOAT; } + virtual Vector GetInputTypes() const override + { + return { }; + }; + virtual bool IsValid() const override + { + return !std::_Is_nan(Value); + } + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override + { + Allocator->Allocate(*this); + } + virtual void SetInput(int index, WorldNodeBase* input) override + { + DEV_ASSERT(false); + } +}; + +struct WorldNode_Branch : public WorldNodeBase +{ + WorldNodeBase* InputBool{}; + WorldNodeBase* InputTrue{}; + WorldNodeBase* InputFalse{}; + + virtual Variant Evaluate(const WorldNodeParameters& params) const override + { + bool condition = InputBool->Evaluate(params).get_unsafe_bool(); + return condition ? InputTrue->Evaluate(params) : InputFalse->Evaluate(params); + } + virtual Variant::Type GetReturnType() const override { return Variant::Type::FLOAT; } + virtual Vector GetInputTypes() const override + { + return { Variant::Type::BOOL, Variant::Type::FLOAT, Variant::Type::FLOAT }; + }; + virtual bool IsValid() const override + { + return InputBool && InputTrue && InputFalse; + } + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override + { + Allocator->Allocate(*this); + } + virtual void SetInput(int index, WorldNodeBase* input) override + { + switch (index) + { + case 0: InputBool = input; + case 1: InputTrue = input; + case 2: InputFalse = input; + } + } +}; + +struct WorldNode_NoiseBase : public WorldNodeBase +{ + float Frequency{ 1.f }; + + virtual Variant Evaluate(const WorldNodeParameters& params) const override + { + float x = params.X * Frequency; + float y = params.Y * Frequency; + return EvaluateNoise(params.Seed, x, y); + } + virtual Variant::Type GetReturnType() const override { return Variant::FLOAT; } + virtual Vector GetInputTypes() const override + { + return { }; + }; + virtual bool IsValid() const override + { + return Frequency != 0; + } + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override + { + Allocator->Allocate(*this); + } + + virtual void SetInput(int index, WorldNodeBase* input) override + { + DEV_ASSERT(false); + } + + virtual float EvaluateNoise(int seed, float x, float y) const = 0; +}; + +struct WorldNode_Simplex : public WorldNode_NoiseBase +{ + virtual float EvaluateNoise(int seed, float x, float y) const override + { + const float SQRT3 = (float)1.7320508075688772935274463415059; + const float F2 = 0.5f * (SQRT3 - 1); + float t = (x + y) * F2; + x += t; + y += t; + + return fastnoiselitestatic::SingleSimplex(seed, x, y); + } +}; + +struct WorldNode_OpenSimplex : public WorldNode_NoiseBase +{ + virtual float EvaluateNoise(int seed, float x, float y) const override + { + const float SQRT3 = (float)1.7320508075688772935274463415059; + const float F2 = 0.5f * (SQRT3 - 1); + float t = (x + y) * F2; + x += t; + y += t; + + return fastnoiselitestatic::SingleOpenSimplex2S(seed, x, y); + } +}; + +struct WorldNode_Perlin : public WorldNode_NoiseBase +{ + virtual float EvaluateNoise(int seed, float x, float y) const override + { + return fastnoiselitestatic::SinglePerlin(seed, x, y); + } +}; + +struct WorldNode_ValueCubic : public WorldNode_NoiseBase +{ + virtual float EvaluateNoise(int seed, float x, float y) const override + { + return fastnoiselitestatic::SingleValueCubic(seed, x, y); + } +}; + +struct WorldNode_Value : public WorldNode_NoiseBase +{ + virtual float EvaluateNoise(int seed, float x, float y) const override + { + return fastnoiselitestatic::SingleValue(seed, x, y); + } +}; + +struct WorldNode_IsTile : public WorldNodeBase +{ + int8_t RelativeX{}; + int8_t RelativeY{}; + TILE_TYPE TileType{}; + + virtual Variant Evaluate(const WorldNodeParameters& params) const override + { + return params.GetTile(params.X + RelativeX, params.Y + RelativeY).GetType() == TileType; + } + virtual Variant::Type GetReturnType() const override { return Variant::BOOL; } + virtual Vector GetInputTypes() const override + { + return { }; + }; + virtual bool IsValid() const override + { + return true; + } + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override + { + Allocator->Allocate(*this); + } + + virtual void SetInput(int index, WorldNodeBase* input) override + { + DEV_ASSERT(false); + } +}; + +struct WorldNode_TileDistance : public WorldNodeBase +{ + int8_t Range{}; + TILE_TYPE TileType{}; + +private: + struct SmallVectorI2{ + int8_t X; int8_t Y; + SmallVectorI2(int8_t x, int8_t y) : X{ x }, Y{ y } {}; + SmallVectorI2() = default; + }; + + static constexpr int ArraySize{(WorldNodeParameters::MaxQueryOffset * 2 + 1) * (WorldNodeParameters::MaxQueryOffset * 2 + 1) - 1}; + + static std::array CreateSortedOffsets() + { + std::array offsets{}; + int counter{}; + for (int y{-WorldNodeParameters::MaxQueryOffset}; y <= WorldNodeParameters::MaxQueryOffset; ++y) + for (int x{-WorldNodeParameters::MaxQueryOffset}; x <= WorldNodeParameters::MaxQueryOffset; ++x) + if (y != 0 && x != 0) + { + offsets[counter] = SmallVectorI2{ static_cast(x), static_cast(y) }; + ++counter; + } + std::sort(offsets.begin(), offsets.end(), [] (SmallVectorI2 lhs, SmallVectorI2 rhs) + { + return rhs.X * rhs.X + rhs.Y * rhs.Y > lhs.X * lhs.X + lhs.Y * lhs.Y; + }); + return offsets; + } + + inline static const std::array SortedOffsets{ CreateSortedOffsets() }; + + static std::array CreateRangeOffsets() + { + std::array offsets{}; + + for (int i{}; i < WorldNodeParameters::MaxQueryOffset; ++i) + { + offsets[i] = ArraySize - (((WorldNodeParameters::MaxQueryOffset - i) * 2 + 1) * ((WorldNodeParameters::MaxQueryOffset - i) * 2 + 1) - 1); + } + + return offsets; + } + + inline static const std::array RangeOffsets{ CreateRangeOffsets() }; + + // inline static const SmallVectorI2 SortedOffset[] = + // { + // {+3, +3}, {-3, -3}, {+3, -3}, {-3, +3}, // 3 + // {+2, +3}, {+3, +2}, {-2, -3}, {-3, -2}, {-2, +3}, {-3, +2}, {+2, -3}, {+3, -2}, + // {+1, +3}, {+3, +1}, {-1, -3}, {-3, -1}, {-1, +3}, {-3, +1}, {+1, -3}, {+3, -1}, + // {+3, +0}, {-3, -0}, {+0, -3}, {-0, +3}, + // {+2, +2}, {-2, -2}, {+2, -2}, {-2, +2}, // 2 + // {+1, +2}, {+2, +1}, {-1, -2}, {-2, -1}, {-1, +2}, {-2, +1}, {+1, -2}, {+2, -1}, + // {+2, +0}, {-2, -0}, {+0, -2}, {-0, +2}, + // {+1, +1}, {-1, -1}, {+1, -1}, {-1, +1}, // 1 + // {+1, +0}, {-1, -0}, {+0, -1}, {-0, +1}, + // }; + + // inline static const int8_t RangeOffsets[] = + // { + // 0, 24, 40 + // }; + +public: + virtual Variant Evaluate(const WorldNodeParameters& params) const override + { + if (!params.ChunkInfo.GetBounds().has_point(Vector2i{params.X, params.Y})) return 16'384.f; + + int maxRangeSQ = 16'384; + for (int i{RangeOffsets[Range]}; i < 48; ++i) + { + auto offset = SortedOffsets[i]; + if (params.GetTile(params.X + offset.X, params.Y + offset.Y).GetType() == TileType) + { + maxRangeSQ = offset.X * offset.X + offset.Y * offset.Y; + } + } + return sqrtf(static_cast(maxRangeSQ)); + } + virtual Variant::Type GetReturnType() const override { return Variant::FLOAT; } + virtual Vector GetInputTypes() const override + { + return { }; + }; + virtual bool IsValid() const override + { + return Range >= 1 && Range <= 3; + } + virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override + { + Allocator->Allocate(*this); + } + + virtual void SetInput(int index, WorldNodeBase* input) override + { + DEV_ASSERT(false); + } +}; + +// struct WorldNode_Cellular : public WorldNode_NoiseBase +// { +// virtual float EvaluateNoise(int seed, float x, float y) const override +// { +// const float SQRT3 = (float)1.7320508075688772935274463415059; +// const float F2 = 0.5f * (SQRT3 - 1); +// float t = (x + y) * F2; +// x += t; +// y += t; + +// return fastnoiselite::FastNoiseLite::SingleCellular(seed, x, y); +// } +// }; \ No newline at end of file diff --git a/include/Types/WorldGraph/WorldGraphVisualNode.h b/include/Types/WorldGraph/WorldGraphVisualNode.h new file mode 100644 index 0000000..b80028c --- /dev/null +++ b/include/Types/WorldGraph/WorldGraphVisualNode.h @@ -0,0 +1,174 @@ +#pragma once + +#include "WorldGraphNode.h" +#include "core/io/resource.h" +#include "modules/factory/include/Util/Helpers.h" + +class WorldGraphVisualNodeBase : public Resource +{ + GDCLASS(WorldGraphVisualNodeBase, Resource); +public: + static void _bind_methods(); + +public: + virtual ~WorldGraphVisualNodeBase() = default; + +public: + Vector2i GetPosition() const { return Position; } + TypedArray GetInputs() const + { + return VectorToTypedArray(InputNodes); + } + + void SetPosition(Vector2i pos) { Position = pos; } + void SetInputNodes(TypedArray inputNodes) + { + InputNodes = TypedArrayToVector(inputNodes); + RefreshInputs(); + } + + bool NodeIsValid() const { return IsValid(); } + TypedArray NodeGetInputTypes() const { return VectorToTypedArrayCast(GetInputTypes()); } + int NodeGetOutputType() const { return GetOutputType(); } + void NodeSetInput(int index, Ref input) { SetInput(index, input); } + bool HasInternalNode() const { return InternalNode.get(); }; + + bool CanExecuteNode(); + + void SetInternalNode(std::unique_ptr&& node); + WorldNodeBase* GetInternalNode() const { return InternalNode.get(); } + void RefreshInputs(); + +public: + virtual Vector GetInputTypes() const { return InternalNode ? InternalNode->GetInputTypes() : Vector{}; }; + virtual Variant::Type GetOutputType() const { return InternalNode ? InternalNode->GetReturnType() : Variant::Type{}; }; + virtual void SetInput(int index, Ref input) + { + InputNodes.set(index, input); + InternalNode->SetInput(index, input.is_valid() ? input->InternalNode.get() : nullptr); + } + virtual bool IsValid() const + { + if (!InternalNode) + print_error(String("No internal node for ") + get_class_name()); + if (!InternalNode->IsValid()) + print_error(String("node is invalid ") + get_class_name()); + return InternalNode && InternalNode->IsValid(); + } + virtual void RefreshValues() {}; + +public: + Vector2i Position{}; + Vector> InputNodes{}; +private: + std::unique_ptr InternalNode{}; +}; + +class WorldGraphVisualNode_Math : public WorldGraphVisualNodeBase +{ + GDCLASS(WorldGraphVisualNode_Math, WorldGraphVisualNodeBase); +public: + static void _bind_methods(); + +public: + WorldGraphVisualNode_Math(); + virtual ~WorldGraphVisualNode_Math() = default; + +public: + TypedArray GetNodeNames() const; + void SetNode(String nodeName); + String GetNode() const { return NodeID; } + +private: + String NodeID{}; + +}; + +class WorldGraphVisualNode_Constant : public WorldGraphVisualNodeBase +{ + GDCLASS(WorldGraphVisualNode_Constant, WorldGraphVisualNodeBase); +public: + static void _bind_methods(); + +public: + WorldGraphVisualNode_Constant(); + virtual ~WorldGraphVisualNode_Constant() = default; + +private: + void SetValue(float val); + float GetValue() const; +}; + +class WorldGraphVisualNode_If : public WorldGraphVisualNodeBase +{ + GDCLASS(WorldGraphVisualNode_If, WorldGraphVisualNodeBase); +public: + static void _bind_methods() {}; + +public: + WorldGraphVisualNode_If(); + virtual ~WorldGraphVisualNode_If() = default; +}; + +class WorldGraphVisualNode_Noise : public WorldGraphVisualNodeBase +{ + GDCLASS(WorldGraphVisualNode_Noise, WorldGraphVisualNodeBase); +public: + static void _bind_methods(); + +public: + void SetNoiseType(String noiseType); + String GetNoiseType() const { return NoiseType; } + float GetFrequency() const { return Frequency; } + + TypedArray GetNoiseTypes() const; + void SetFrequency(float val); + + virtual void RefreshValues() override; + +public: + WorldGraphVisualNode_Noise(); + virtual ~WorldGraphVisualNode_Noise() = default; + +private: + String NoiseType{}; + float Frequency{}; +}; + +class WorldGraphVisualNode_Tile : public WorldGraphVisualNodeBase +{ + GDCLASS(WorldGraphVisualNode_Tile, WorldGraphVisualNodeBase); +public: + static void _bind_methods(); + +public: + WorldGraphVisualNode_Tile(); + virtual ~WorldGraphVisualNode_Tile() = default; + +public: + void SetType(int type); + void SetRelativeX(int offset); + void SetRelativeY(int offset); + + int GetType() const; + int GetRelativeX() const; + int GetRelativeY() const; +}; + +class WorldGraphVisualNode_TileDistance : public WorldGraphVisualNodeBase +{ + GDCLASS(WorldGraphVisualNode_TileDistance, WorldGraphVisualNodeBase); +public: + static void _bind_methods(); + +public: + WorldGraphVisualNode_TileDistance(); + virtual ~WorldGraphVisualNode_TileDistance() = default; + +public: + void SetType(int type); + void SetRange(int range); + + int GetType() const; + int GetRange() const; +}; \ No newline at end of file diff --git a/include/Types/WorldSettings.h b/include/Types/WorldSettings.h new file mode 100644 index 0000000..82fae8d --- /dev/null +++ b/include/Types/WorldSettings.h @@ -0,0 +1,63 @@ +// #pragma once + +// #include "Data/Tile.h" +// #include "Data/Recipe.h" +// #include "core/object/ref_counted.h" +// #include "modules/noise/fastnoise_lite.h" +// #include "core/io/resource.h" +// #include "Data/Archetype.h" +// #include "LayerConfigs.h" +// #include "scene/resources/2d/tile_set.h" +// #include "Data/WorldGraph/WorldGraph.h" + +// struct FactoryWorldSettings : public Resource +// { +// public: +// GDCLASS(FactoryWorldSettings, Resource); + +// public: +// static void _bind_methods(); + +// public: +// void Merge(Ref settings); +// void Initialize(); + +// public: +// TypedArray GetRecipes() const { return VectorToTypedArray(Recipes); } +// TypedArray GetPlaceableArchetypes() const { return VectorToTypedArray(PlaceableArchetypes); } +// TypedArray GetTileConfigs() const { return VectorToTypedArray(TileConfigs); } +// TypedArray GetLayerConfigs() const { return VectorToTypedArray(LayerConfigs); } + +// void SetRecipes(TypedArray recipes) { Recipes = TypedArrayToVector(recipes); } +// void SetPlaceableArchetypes(TypedArray archetypes) { PlaceableArchetypes = TypedArrayToVector(archetypes); } +// void SetTileConfigs(TypedArray tiles) { TileConfigs = TypedArrayToVector(tiles); } +// void SetLayerConfigs(TypedArray layers) { LayerConfigs = TypedArrayToVector(layers); } + +// public: +// int GetStartHeight() const; +// Vector GetChunkUnlockCosts(int x, int y) const; +// Vector GetChunkUnlockCosts(ChunkKey chunk) const; +// Ref GetLayer(ChunkKey chunk) const; + +// private: +// void InitializeResources(); +// void InitializeLayers(); +// void InitializeWorldGenerators(); +// void InitializeTexturesSheets(); +// void InitializeItemGraph(); + +// public: +// Vector> TileConfigs{}; +// Vector> Recipes{}; +// Vector> Items{}; +// Vector> Archetypes{}; +// Vector> PlaceableArchetypes{}; +// Vector> LayerConfigs{}; +// HashMap,int> ItemComplexity{}; + +// Vector> TileSheets{}; +// Ref TileSet{}; + +// WorldGraph WorldGenerator{}; +// }; + diff --git a/include/Util/AStar.h b/include/Util/AStar.h new file mode 100644 index 0000000..a38071b --- /dev/null +++ b/include/Util/AStar.h @@ -0,0 +1,837 @@ +/* +A* Algorithm Implementation using STL is +Copyright (C)2001-2005 Justin Heyes-Jones + +Permission is given by the author to freely redistribute and +include this code in any program as long as this credit is +given where due. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE + OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED + CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL + DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE + OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER + THIS DISCLAIMER. + + Use at your own risk! + +*/ + +#ifndef STLASTAR_H +#define STLASTAR_H +// used for text debugging +#include +#include +//#include +#include + +// stl includes +#include +#include +#include +#include + +// fast fixed size memory allocator, used for fast node memory management +#include "fsa.h" + +// Fixed size memory allocator can be disabled to compare performance +// Uses std new and delete instead if you turn it off +#define USE_FSA_MEMORY 1 + +// disable warning that debugging information has lines that are truncated +// occurs in stl headers +#if defined(WIN32) && defined(_WINDOWS) +#pragma warning( disable : 4786 ) +#endif + +template class AStarState; + +// The AStar search class. UserState is the users state space type +template class AStarSearch +{ + +public: // data + + enum + { + SEARCH_STATE_NOT_INITIALISED, + SEARCH_STATE_SEARCHING, + SEARCH_STATE_SUCCEEDED, + SEARCH_STATE_FAILED, + SEARCH_STATE_OUT_OF_MEMORY, + SEARCH_STATE_INVALID + }; + + + // A node represents a possible state in the search + // The user provided state type is included inside this type + +public: + + class Node + { + public: + + Node* parent; // used during the search to record the parent of successor nodes + Node* child; // used after the search for the application to view the search in reverse + + float g; // cost of this node + its predecessors + float h; // heuristic estimate of distance to goal + float f; // sum of cumulative cost of predecessors and self and heuristic + + Node() : + parent(0), + child(0), + g(0.0f), + h(0.0f), + f(0.0f) + { + } + + bool operator==(const Node& otherNode) const + { + return m_UserState.IsSameState(otherNode.m_UserState); + } + + UserState m_UserState; + }; + + // For sorting the heap the STL needs compare function that lets us compare + // the f value of two nodes + + class HeapCompare_f + { + public: + + bool operator() (const Node* x, const Node* y) const + { + return x->f > y->f; + } + }; + + +public: // methods + + + // constructor just initialises private data + AStarSearch() : + m_State(SEARCH_STATE_NOT_INITIALISED), + m_CurrentSolutionNode(NULL), +#if USE_FSA_MEMORY + m_FixedSizeAllocator(1000), +#endif + m_AllocateNodeCount(0), + m_CancelRequest(false) + { + } + + AStarSearch(int MaxNodes) : + m_State(SEARCH_STATE_NOT_INITIALISED), + m_CurrentSolutionNode(NULL), +#if USE_FSA_MEMORY + m_FixedSizeAllocator(MaxNodes), +#endif + m_AllocateNodeCount(0), + m_CancelRequest(false) + { + } + + // call at any time to cancel the search and free up all the memory + void CancelSearch() + { + m_CancelRequest = true; + } + + // Set Start and goal states + void SetStartAndGoalStates(const UserState& Start, const UserState& Goal) + { + m_CancelRequest = false; + + m_Start = AllocateNode(); + m_Goal = AllocateNode(); + + assert((m_Start != NULL && m_Goal != NULL)); + + m_Start->m_UserState = Start; + m_Goal->m_UserState = Goal; + + m_State = SEARCH_STATE_SEARCHING; + + // Initialise the AStar specific parts of the Start Node + // The user only needs fill out the state information + + m_Start->g = 0; + m_Start->h = m_Start->m_UserState.GoalDistanceEstimate(m_Goal->m_UserState); + m_Start->f = m_Start->g + m_Start->h; + m_Start->parent = 0; + + // Push the start node on the Open list + + m_OpenList.push_back(m_Start); // heap now unsorted + + // Sort back element into heap + push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f()); + + // Initialise counter for search steps + m_Steps = 0; + } + + // Advances search one step + unsigned int SearchStep() + { + // Firstly break if the user has not initialised the search + assert((m_State > SEARCH_STATE_NOT_INITIALISED) && + (m_State < SEARCH_STATE_INVALID)); + + // Next I want it to be safe to do a searchstep once the search has succeeded... + if ((m_State == SEARCH_STATE_SUCCEEDED) || + (m_State == SEARCH_STATE_FAILED) + ) + { + return m_State; + } + + // Failure is defined as emptying the open list as there is nothing left to + // search... + // New: Allow user abort + if (m_OpenList.empty() || m_CancelRequest) + { + FreeAllNodes(); + m_State = SEARCH_STATE_FAILED; + return m_State; + } + + // Incremement step count + m_Steps++; + + // Pop the best node (the one with the lowest f) + Node* n = m_OpenList.front(); // get pointer to the node + pop_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f()); + m_OpenList.pop_back(); + + // Check for the goal, once we pop that we're done + if (n->m_UserState.IsGoal(m_Goal->m_UserState)) + { + // The user is going to use the Goal Node he passed in + // so copy the parent pointer of n + m_Goal->parent = n->parent; + m_Goal->g = n->g; + + // A special case is that the goal was passed in as the start state + // so handle that here + if (false == n->m_UserState.IsSameState(m_Start->m_UserState)) + { + FreeNode(n); + + // set the child pointers in each node (except Goal which has no child) + Node* nodeChild = m_Goal; + Node* nodeParent = m_Goal->parent; + + do + { + nodeParent->child = nodeChild; + + nodeChild = nodeParent; + nodeParent = nodeParent->parent; + + } while (nodeChild != m_Start); // Start is always the first node by definition + + } + + // delete nodes that aren't needed for the solution + FreeUnusedNodes(); + + m_State = SEARCH_STATE_SUCCEEDED; + + return m_State; + } + else // not goal + { + + // We now need to generate the successors of this node + // The user helps us to do this, and we keep the new nodes in + // m_Successors ... + + m_Successors.clear(); // empty vector of successor nodes to n + + // User provides this functions and uses AddSuccessor to add each successor of + // node 'n' to m_Successors + bool ret = n->m_UserState.GetSuccessors(this, n->parent ? &n->parent->m_UserState : NULL); + + if (!ret) + { + + typename std::vector::iterator successor; + + // free the nodes that may previously have been added + for (successor = m_Successors.begin(); successor != m_Successors.end(); successor++) + { + FreeNode((*successor)); + } + + m_Successors.clear(); // empty vector of successor nodes to n + + // free up everything else we allocated + FreeNode((n)); + FreeAllNodes(); + + m_State = SEARCH_STATE_OUT_OF_MEMORY; + return m_State; + } + + // Now handle each successor to the current node ... + for (typename std::vector::iterator successor = m_Successors.begin(); successor != m_Successors.end(); successor++) + { + // The g value for this successor ... + float newg = n->g + n->m_UserState.GetCost((*successor)->m_UserState); + + // Now we need to find whether the node is on the open or closed lists + // If it is but the node that is already on them is better (lower g) + // then we can forget about this successor + + // First linear search of open list to find node + + typename std::vector::iterator openlist_result; + + for (openlist_result = m_OpenList.begin(); openlist_result != m_OpenList.end(); openlist_result++) + { + if ((*openlist_result)->m_UserState.IsSameState((*successor)->m_UserState)) + { + break; + } + } + + if (openlist_result != m_OpenList.end()) + { + + // we found this state on open + + if ((*openlist_result)->g <= newg) + { + FreeNode((*successor)); + + // the one on Open is cheaper than this one + continue; + } + } + typename std::unordered_set::iterator closedlist_result; + + closedlist_result = m_ClosedList.find(*successor); + + if (closedlist_result != m_ClosedList.end()) + { + + // we found this state on closed + + if ((*closedlist_result)->g <= newg) + { + // the one on Closed is cheaper than this one + FreeNode((*successor)); + + continue; + } + } + + // This node is the best node so far with this particular state + // so lets keep it and set up its AStar specific data ... + + (*successor)->parent = n; + (*successor)->g = newg; + (*successor)->h = (*successor)->m_UserState.GoalDistanceEstimate(m_Goal->m_UserState); + (*successor)->f = (*successor)->g + (*successor)->h; + + // Successor in closed list + // 1 - Update old version of this node in closed list + // 2 - Move it from closed to open list + // 3 - Sort heap again in open list + + if (closedlist_result != m_ClosedList.end()) + { + // Update closed node with successor node AStar data + //*(*closedlist_result) = *(*successor); + (*closedlist_result)->parent = (*successor)->parent; + (*closedlist_result)->g = (*successor)->g; + (*closedlist_result)->h = (*successor)->h; + (*closedlist_result)->f = (*successor)->f; + + // Free successor node + FreeNode((*successor)); + + // Push closed node into open list + m_OpenList.push_back((*closedlist_result)); + + // Remove closed node from closed list + m_ClosedList.erase(closedlist_result); + + // Sort back element into heap + push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f()); + + // Fix thanks to ... + // Greg Douglas + // who noticed that this code path was incorrect + // Here we have found a new state which is already CLOSED + + } + + // Successor in open list + // 1 - Update old version of this node in open list + // 2 - sort heap again in open list + + else if (openlist_result != m_OpenList.end()) + { + // Update open node with successor node AStar data + //*(*openlist_result) = *(*successor); + (*openlist_result)->parent = (*successor)->parent; + (*openlist_result)->g = (*successor)->g; + (*openlist_result)->h = (*successor)->h; + (*openlist_result)->f = (*successor)->f; + + // Free successor node + FreeNode((*successor)); + + // re-make the heap + // make_heap rather than sort_heap is an essential bug fix + // thanks to Mike Ryynanen for pointing this out and then explaining + // it in detail. sort_heap called on an invalid heap does not work + make_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f()); + } + + // New successor + // 1 - Move it from successors to open list + // 2 - sort heap again in open list + + else + { + // Push successor node into open list + m_OpenList.push_back((*successor)); + + // Sort back element into heap + push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f()); + } + + } + + // push n onto Closed, as we have expanded it now + + m_ClosedList.insert(n); + + } // end else (not goal so expand) + + return m_State; // Succeeded bool is false at this point. + + } + + // User calls this to add a successor to a list of successors + // when expanding the search frontier + bool AddSuccessor(const UserState& State) + { + Node* node = AllocateNode(); + + if (node) + { + node->m_UserState = State; + + m_Successors.push_back(node); + + return true; + } + + return false; + } + + // Free the solution nodes + // This is done to clean up all used Node memory when you are done with the + // search + void FreeSolutionNodes() + { + Node* n = m_Start; + + if (m_Start->child) + { + do + { + Node* del = n; + n = n->child; + FreeNode(del); + + del = NULL; + + } while (n != m_Goal); + + FreeNode(n); // Delete the goal + + } + else + { + // if the start node is the solution we need to just delete the start and goal + // nodes + FreeNode(m_Start); + FreeNode(m_Goal); + } + + } + + // Functions for traversing the solution + + // Get start node + UserState* GetSolutionStart() + { + m_CurrentSolutionNode = m_Start; + if (m_Start) + { + return &m_Start->m_UserState; + } + else + { + return NULL; + } + } + + // Get next node + UserState* GetSolutionNext() + { + if (m_CurrentSolutionNode) + { + if (m_CurrentSolutionNode->child) + { + + Node* child = m_CurrentSolutionNode->child; + + m_CurrentSolutionNode = m_CurrentSolutionNode->child; + + return &child->m_UserState; + } + } + + return NULL; + } + + // Get end node + UserState* GetSolutionEnd() + { + m_CurrentSolutionNode = m_Goal; + if (m_Goal) + { + return &m_Goal->m_UserState; + } + else + { + return NULL; + } + } + + // Step solution iterator backwards + UserState* GetSolutionPrev() + { + if (m_CurrentSolutionNode) + { + if (m_CurrentSolutionNode->parent) + { + + Node* parent = m_CurrentSolutionNode->parent; + + m_CurrentSolutionNode = m_CurrentSolutionNode->parent; + + return &parent->m_UserState; + } + } + + return NULL; + } + + // Get final cost of solution + // Returns FLT_MAX if goal is not defined or there is no solution + float GetSolutionCost() + { + if (m_Goal && m_State == SEARCH_STATE_SUCCEEDED) + { + return m_Goal->g; + } + else + { + return FLT_MAX; + } + } + + // For educational use and debugging it is useful to be able to view + // the open and closed list at each step, here are two functions to allow that. + + UserState* GetOpenListStart() + { + float f, g, h; + return GetOpenListStart(f, g, h); + } + + UserState* GetOpenListStart(float& f, float& g, float& h) + { + iterDbgOpen = m_OpenList.begin(); + if (iterDbgOpen != m_OpenList.end()) + { + f = (*iterDbgOpen)->f; + g = (*iterDbgOpen)->g; + h = (*iterDbgOpen)->h; + return &(*iterDbgOpen)->m_UserState; + } + + return NULL; + } + + UserState* GetOpenListNext() + { + float f, g, h; + return GetOpenListNext(f, g, h); + } + + UserState* GetOpenListNext(float& f, float& g, float& h) + { + iterDbgOpen++; + if (iterDbgOpen != m_OpenList.end()) + { + f = (*iterDbgOpen)->f; + g = (*iterDbgOpen)->g; + h = (*iterDbgOpen)->h; + return &(*iterDbgOpen)->m_UserState; + } + + return NULL; + } + + UserState* GetClosedListStart() + { + float f, g, h; + return GetClosedListStart(f, g, h); + } + + UserState* GetClosedListStart(float& f, float& g, float& h) + { + iterDbgClosed = m_ClosedList.begin(); + if (iterDbgClosed != m_ClosedList.end()) + { + f = (*iterDbgClosed)->f; + g = (*iterDbgClosed)->g; + h = (*iterDbgClosed)->h; + + return &(*iterDbgClosed)->m_UserState; + } + + return NULL; + } + + UserState* GetClosedListNext() + { + float f, g, h; + return GetClosedListNext(f, g, h); + } + + UserState* GetClosedListNext(float& f, float& g, float& h) + { + iterDbgClosed++; + if (iterDbgClosed != m_ClosedList.end()) + { + f = (*iterDbgClosed)->f; + g = (*iterDbgClosed)->g; + h = (*iterDbgClosed)->h; + + return &(*iterDbgClosed)->m_UserState; + } + + return NULL; + } + + // Get the number of steps + + int GetStepCount() { return m_Steps; } + + void EnsureMemoryFreed() + { +#if USE_FSA_MEMORY + assert(m_AllocateNodeCount == 0); +#endif + + } + +private: // methods + + // This is called when a search fails or is cancelled to free all used + // memory + void FreeAllNodes() + { + // iterate open list and delete all nodes + typename std::vector::iterator iterOpen = m_OpenList.begin(); + + while (iterOpen != m_OpenList.end()) + { + Node* n = (*iterOpen); + FreeNode(n); + + iterOpen++; + } + + m_OpenList.clear(); + + // iterate closed list and delete unused nodes + typename std::unordered_set::iterator iterClosed; + + for (iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed++) + { + Node* n = (*iterClosed); + FreeNode(n); + } + + m_ClosedList.clear(); + + // delete the goal + + FreeNode(m_Goal); + } + + + // This call is made by the search class when the search ends. A lot of nodes may be + // created that are still present when the search ends. They will be deleted by this + // routine once the search ends + void FreeUnusedNodes() + { + // iterate open list and delete unused nodes + typename std::vector< Node* >::iterator iterOpen = m_OpenList.begin(); + + while (iterOpen != m_OpenList.end()) + { + Node* n = (*iterOpen); + + if (!n->child) + { + FreeNode(n); + + n = NULL; + } + + iterOpen++; + } + + m_OpenList.clear(); + + // iterate closed list and delete unused nodes + typename std::unordered_set::iterator iterClosed; + + for (iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed++) + { + Node* n = (*iterClosed); + + if (!n->child) + { + FreeNode(n); + n = NULL; + + } + } + + m_ClosedList.clear(); + + } + + // Node memory management + Node* AllocateNode() + { + +#if !USE_FSA_MEMORY + m_AllocateNodeCount++; + Node* p = new Node; + return p; +#else + Node* address = m_FixedSizeAllocator.alloc(); + + if (!address) + { + return NULL; + } + m_AllocateNodeCount++; + Node* p = new (address) Node; + return p; +#endif + } + + void FreeNode(Node* node) + { + + m_AllocateNodeCount--; + +#if !USE_FSA_MEMORY + delete node; +#else + node->~Node(); + m_FixedSizeAllocator.free(node); +#endif + } + +private: // data + + // Heap (simple vector but used as a heap, cf. Steve Rabin's game gems article) + std::vector m_OpenList; + + // Closed is an unordered_set + struct NodeHash { + size_t operator() (Node* const& n) const { + return n->m_UserState.Hash(); + } + }; + struct NodeEqual { + bool operator()(Node* a, Node* b) const { + return a->m_UserState.IsSameState(b->m_UserState); + } + }; + std::unordered_set m_ClosedList; + + + // Successors is a vector filled out by the user each type successors to a node + // are generated + std::vector< Node* > m_Successors; + + // State + unsigned int m_State; + + // Counts steps + int m_Steps; + + // Start and goal state pointers + Node* m_Start; + Node* m_Goal; + + Node* m_CurrentSolutionNode; + +#if USE_FSA_MEMORY + // Memory + FixedSizeAllocator m_FixedSizeAllocator; +#endif + + //Debug : need to keep these two iterators around + // for the user Dbg functions + typename std::vector< Node* >::iterator iterDbgOpen; + typename std::vector< Node* >::iterator iterDbgClosed; + + // debugging : count memory allocation and free's + int m_AllocateNodeCount; + + bool m_CancelRequest; + +}; + +template class AStarState +{ +public: + virtual ~AStarState() {} + virtual float GoalDistanceEstimate(T& nodeGoal) = 0; // Heuristic function which computes the estimated cost to the goal node + virtual bool IsGoal(T& nodeGoal) = 0; // Returns true if this node is the goal node + virtual bool GetSuccessors(AStarSearch* astarsearch, T* parent_node) = 0; // Retrieves all successors to this node and adds them via astarsearch.addSuccessor() + virtual float GetCost(T& successor) = 0; // Computes the cost of travelling from this node to the successor node + virtual bool IsSameState(T& rhs) = 0; // Returns true if this node is the same as the rhs node + virtual size_t Hash() = 0; // Returns a hash for the state +}; + +#endif + diff --git a/include/Util/FastNoiseLite.h b/include/Util/FastNoiseLite.h new file mode 100644 index 0000000..03ac939 --- /dev/null +++ b/include/Util/FastNoiseLite.h @@ -0,0 +1,1191 @@ +// MIT License +// +// Copyright(c) 2023 Jordan Peck (jordan.me2@gmail.com) +// Copyright(c) 2023 Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// .'',;:cldxkO00KKXXNNWWWNNXKOkxdollcc::::::;:::ccllloooolllllllllooollc:,'... ...........',;cldxkO000Okxdlc::;;;,,;;;::cclllllll +// ..',;:ldxO0KXXNNNNNNNNXXK0kxdolcc::::::;;;,,,,,,;;;;;;;;;;:::cclllllc:;'.... ...........',;:ldxO0KXXXK0Okxdolc::;;;;::cllodddddo +// ...',:loxO0KXNNNNNXXKK0Okxdolc::;::::::::;;;,,'''''.....''',;:clllllc:;,'............''''''''',;:loxO0KXNNNNNXK0Okxdollccccllodxxxxxxd +// ....';:ldkO0KXXXKK00Okxdolcc:;;;;;::cclllcc:;;,''..... ....',;clooddolcc:;;;;,,;;;;;::::;;;;;;:cloxk0KXNWWWWWWNXKK0Okxddoooddxxkkkkkxx +// .....';:ldxkOOOOOkxxdolcc:;;;,,,;;:cllooooolcc:;'... ..,:codxkkkxddooollloooooooollcc:::::clodkO0KXNWWWWWWNNXK00Okxxxxxxxxkkkkxxx +// . ....';:cloddddo___________,,,,;;:clooddddoolc:,... ..,:ldx__00OOOkkk___kkkkkkxxdollc::::cclodkO0KXXNNNNNNXXK0OOkxxxxxxxxxxxxddd +// .......',;:cccc:| |,,,;;:cclooddddoll:;'.. ..';cox| \KKK000| |KK00OOkxdocc___;::clldxxkO0KKKKK00Okkxdddddddddddddddoo +// .......'',,,,,''| ________|',,;;::cclloooooolc:;'......___:ldk| \KK000| |XKKK0Okxolc| |;;::cclodxxkkkkxxdoolllcclllooodddooooo +// ''......''''....| | ....'',,,,;;;::cclloooollc:;,''.'| |oxk| \OOO0| |KKK00Oxdoll|___|;;;;;::ccllllllcc::;;,,;;;:cclloooooooo +// ;;,''.......... | |_____',,;;;____:___cllo________.___| |___| \xkk| |KK_______ool___:::;________;;;_______...'',;;:ccclllloo +// c:;,''......... | |:::/ ' |lo/ | | \dx| |0/ \d| |cc/ |'/ \......',,;;:ccllo +// ol:;,'..........| _____|ll/ __ |o/ ______|____ ___| | \o| |/ ___ \| |o/ ______|/ ___ \ .......'',;:clo +// dlc;,...........| |::clooo| / | |x\___ \KXKKK0| |dol| |\ \| | | | | |d\___ \..| | / / ....',:cl +// xoc;'... .....'| |llodddd| \__| |_____\ \KKK0O| |lc:| |'\ | |___| | |_____\ \.| |_/___/... ...',;:c +// dlc;'... ....',;| |oddddddo\ | |Okkx| |::;| |..\ |\ /| | | \ |... ....',;:c +// ol:,'.......',:c|___|xxxddollc\_____,___|_________/ddoll|___|,,,|___|...\_____|:\ ______/l|___|_________/...\________|'........',;::cc +// c:;'.......';:codxxkkkkxxolc::;::clodxkOO0OOkkxdollc::;;,,''''',,,,''''''''''',,'''''',;:loxkkOOkxol:;,'''',,;:ccllcc:;,'''''',;::ccll +// ;,'.......',:codxkOO0OOkxdlc:;,,;;:cldxxkkxxdolc:;;,,''.....'',;;:::;;,,,'''''........,;cldkO0KK0Okdoc::;;::cloodddoolc:;;;;;::ccllooo +// .........',;:lodxOO0000Okdoc:,,',,;:clloddoolc:;,''.......'',;:clooollc:;;,,''.......',:ldkOKXNNXX0Oxdolllloddxxxxxxdolccccccllooodddd +// . .....';:cldxkO0000Okxol:;,''',,;::cccc:;,,'.......'',;:cldxxkkxxdolc:;;,'.......';coxOKXNWWWNXKOkxddddxxkkkkkkxdoollllooddxxxxkkk +// ....',;:codxkO000OOxdoc:;,''',,,;;;;,''.......',,;:clodkO00000Okxolc::;,,''..',;:ldxOKXNWWWNNK0OkkkkkkkkkkkxxddooooodxxkOOOOO000 +// ....',;;clodxkkOOOkkdolc:;,,,,,,,,'..........,;:clodxkO0KKXKK0Okxdolcc::;;,,,;;:codkO0XXNNNNXKK0OOOOOkkkkxxdoollloodxkO0KKKXXXXX +// +// VERSION: 1.1.0 +// https://github.com/Auburn/FastNoiseLite + +#ifndef FASTNOISELITESTATIC_H +#define FASTNOISELITESTATIC_H + +#include + +namespace fastnoiselitestatic { + + template + struct Arguments_must_be_floating_point_values; + + template + struct Lookup + { + static const T Gradients2D[]; + static const T Gradients3D[]; + static const T RandVecs2D[]; + static const T RandVecs3D[]; + }; + + static float FastMin(float a, float b) { return a < b ? a : b; } + + static float FastMax(float a, float b) { return a > b ? a : b; } + + static float FastAbs(float f) { return f < 0 ? -f : f; } + + static float FastSqrt(float f) { return sqrtf(f); } + + template + static int FastFloor(FNfloat f) { return f >= 0 ? (int)f : (int)f - 1; } + + template + static int FastRound(FNfloat f) { return f >= 0 ? (int)(f + 0.5f) : (int)(f - 0.5f); } + + static float Lerp(float a, float b, float t) { return a + t * (b - a); } + + static float InterpHermite(float t) { return t * t * (3 - 2 * t); } + + static float InterpQuintic(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } + + static float CubicLerp(float a, float b, float c, float d, float t) + { + float p = (d - c) - (a - b); + return t * t * t * p + t * t * ((a - b) - p) + t * (c - a) + b; + } + + static float PingPong(float t) + { + t -= (int)(t * 0.5f) * 2; + return t < 1 ? t : 2 - t; + } + + // Hashing + static constexpr int PrimeX = 501125321; + static constexpr int PrimeY = 1136930381; + static constexpr int PrimeZ = 1720413743; + + static int Hash(int seed, int xPrimed, int yPrimed) + { + int hash = seed ^ xPrimed ^ yPrimed; + + hash *= 0x27d4eb2d; + return hash; + } + + + static int Hash(int seed, int xPrimed, int yPrimed, int zPrimed) + { + int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed; + + hash *= 0x27d4eb2d; + return hash; + } + + + static float ValCoord(int seed, int xPrimed, int yPrimed) + { + int hash = Hash(seed, xPrimed, yPrimed); + + hash *= hash; + hash ^= hash << 19; + return hash * (1 / 2147483648.0f); + } + + + static float ValCoord(int seed, int xPrimed, int yPrimed, int zPrimed) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + + hash *= hash; + hash ^= hash << 19; + return hash * (1 / 2147483648.0f); + } + + + static float GradCoord(int seed, int xPrimed, int yPrimed, float xd, float yd) + { + int hash = Hash(seed, xPrimed, yPrimed); + hash ^= hash >> 15; + hash &= 127 << 1; + + float xg = Lookup::Gradients2D[hash]; + float yg = Lookup::Gradients2D[hash | 1]; + + return xd * xg + yd * yg; + } + + + static float GradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + hash ^= hash >> 15; + hash &= 63 << 2; + + float xg = Lookup::Gradients3D[hash]; + float yg = Lookup::Gradients3D[hash | 1]; + float zg = Lookup::Gradients3D[hash | 2]; + + return xd * xg + yd * yg + zd * zg; + } + + + static void GradCoordOut(int seed, int xPrimed, int yPrimed, float& xo, float& yo) + { + int hash = Hash(seed, xPrimed, yPrimed) & (255 << 1); + + xo = Lookup::RandVecs2D[hash]; + yo = Lookup::RandVecs2D[hash | 1]; + } + + + static void GradCoordOut(int seed, int xPrimed, int yPrimed, int zPrimed, float& xo, float& yo, float& zo) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed) & (255 << 2); + + xo = Lookup::RandVecs3D[hash]; + yo = Lookup::RandVecs3D[hash | 1]; + zo = Lookup::RandVecs3D[hash | 2]; + } + + + static void GradCoordDual(int seed, int xPrimed, int yPrimed, float xd, float yd, float& xo, float& yo) + { + int hash = Hash(seed, xPrimed, yPrimed); + int index1 = hash & (127 << 1); + int index2 = (hash >> 7) & (255 << 1); + + float xg = Lookup::Gradients2D[index1]; + float yg = Lookup::Gradients2D[index1 | 1]; + float value = xd * xg + yd * yg; + + float xgo = Lookup::RandVecs2D[index2]; + float ygo = Lookup::RandVecs2D[index2 | 1]; + + xo = value * xgo; + yo = value * ygo; + } + + + static void GradCoordDual(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd, float& xo, float& yo, float& zo) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + int index1 = hash & (63 << 2); + int index2 = (hash >> 6) & (255 << 2); + + float xg = Lookup::Gradients3D[index1]; + float yg = Lookup::Gradients3D[index1 | 1]; + float zg = Lookup::Gradients3D[index1 | 2]; + float value = xd * xg + yd * yg + zd * zg; + + float xgo = Lookup::RandVecs3D[index2]; + float ygo = Lookup::RandVecs3D[index2 | 1]; + float zgo = Lookup::RandVecs3D[index2 | 2]; + + xo = value * xgo; + yo = value * ygo; + zo = value * zgo; + } + + // Noise Coordinate Transforms (frequency, and possible skew or rotation) + + template + static void TransformNoiseCoordinate(FNfloat& x, FNfloat& y, FNfloat frequency) + { + x *= frequency; + y *= frequency; + } + + template + static void TransformNoiseCoordinate_OpenSimplex(FNfloat& x, FNfloat& y, FNfloat frequency) + { + TransformNoiseCoordinate(x, y, frequency); + + const FNfloat SQRT3 = (FNfloat)1.7320508075688772935274463415059; + const FNfloat F2 = 0.5f * (SQRT3 - 1); + FNfloat t = (x + y) * F2; + x += t; + y += t; + } + + template + static float SingleSimplex(int seed, FNfloat x, FNfloat y) + { + // 2D OpenSimplex2 case uses the same algorithm as ordinary Simplex. + + constexpr float SQRT3 = 1.7320508075688772935274463415059f; + constexpr float G2 = (3 - SQRT3) / 6; + + /* + * --- Skew moved to TransformNoiseCoordinate method --- + * const FNfloat F2 = 0.5f * (SQRT3 - 1); + * FNfloat s = (x + y) * F2; + * x += s; y += s; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + float xi = (float)(x - i); + float yi = (float)(y - j); + + float t = (xi + yi) * G2; + float x0 = (float)(xi - t); + float y0 = (float)(yi - t); + + i *= PrimeX; + j *= PrimeY; + + float n0, n1, n2; + + float a = 0.5f - x0 * x0 - y0 * y0; + if (a <= 0) n0 = 0; + else + { + n0 = (a * a) * (a * a) * GradCoord(seed, i, j, x0, y0); + } + + float c = (float)(2 * (1 - 2 * G2) * (1 / G2 - 2)) * t + ((float)(-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a); + if (c <= 0) n2 = 0; + else + { + float x2 = x0 + (2 * (float)G2 - 1); + float y2 = y0 + (2 * (float)G2 - 1); + n2 = (c * c) * (c * c) * GradCoord(seed, i + PrimeX, j + PrimeY, x2, y2); + } + + if (y0 > x0) + { + float x1 = x0 + (float)G2; + float y1 = y0 + ((float)G2 - 1); + float b = 0.5f - x1 * x1 - y1 * y1; + if (b <= 0) n1 = 0; + else + { + n1 = (b * b) * (b * b) * GradCoord(seed, i, j + PrimeY, x1, y1); + } + } + else + { + float x1 = x0 + ((float)G2 - 1); + float y1 = y0 + (float)G2; + float b = 0.5f - x1 * x1 - y1 * y1; + if (b <= 0) n1 = 0; + else + { + n1 = (b * b) * (b * b) * GradCoord(seed, i + PrimeX, j, x1, y1); + } + } + + return (n0 + n1 + n2) * 99.83685446303647f; + } + + template + static float SingleOpenSimplex2(int seed, FNfloat x, FNfloat y, FNfloat z) + { + // 3D OpenSimplex2 case uses two offset rotated cube grids. + + /* + * --- Rotation moved to TransformNoiseCoordinate method --- + * const FNfloat R3 = (FNfloat)(2.0 / 3.0); + * FNfloat r = (x + y + z) * R3; // Rotation, not skew + * x = r - x; y = r - y; z = r - z; + */ + + int i = FastRound(x); + int j = FastRound(y); + int k = FastRound(z); + float x0 = (float)(x - i); + float y0 = (float)(y - j); + float z0 = (float)(z - k); + + int xNSign = (int)(-1.0f - x0) | 1; + int yNSign = (int)(-1.0f - y0) | 1; + int zNSign = (int)(-1.0f - z0) | 1; + + float ax0 = xNSign * -x0; + float ay0 = yNSign * -y0; + float az0 = zNSign * -z0; + + i *= PrimeX; + j *= PrimeY; + k *= PrimeZ; + + float value = 0; + float a = (0.6f - x0 * x0) - (y0 * y0 + z0 * z0); + + for (int l = 0; ; l++) + { + if (a > 0) + { + value += (a * a) * (a * a) * GradCoord(seed, i, j, k, x0, y0, z0); + } + + float b = a + 1; + int i1 = i; + int j1 = j; + int k1 = k; + float x1 = x0; + float y1 = y0; + float z1 = z0; + + if (ax0 >= ay0 && ax0 >= az0) + { + x1 += xNSign; + b -= xNSign * 2 * x1; + i1 -= xNSign * PrimeX; + } + else if (ay0 > ax0 && ay0 >= az0) + { + y1 += yNSign; + b -= yNSign * 2 * y1; + j1 -= yNSign * PrimeY; + } + else + { + z1 += zNSign; + b -= zNSign * 2 * z1; + k1 -= zNSign * PrimeZ; + } + + if (b > 0) + { + value += (b * b) * (b * b) * GradCoord(seed, i1, j1, k1, x1, y1, z1); + } + + if (l == 1) break; + + ax0 = 0.5f - ax0; + ay0 = 0.5f - ay0; + az0 = 0.5f - az0; + + x0 = xNSign * ax0; + y0 = yNSign * ay0; + z0 = zNSign * az0; + + a += (0.75f - ax0) - (ay0 + az0); + + i += (xNSign >> 1) & PrimeX; + j += (yNSign >> 1) & PrimeY; + k += (zNSign >> 1) & PrimeZ; + + xNSign = -xNSign; + yNSign = -yNSign; + zNSign = -zNSign; + + seed = ~seed; + } + + return value * 32.69428253173828125f; + } + + + // OpenSimplex2S Noise + + template + static float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y) + { + // 2D OpenSimplex2S case is a modified 2D simplex noise. + + const FNfloat SQRT3 = (FNfloat)1.7320508075688772935274463415059; + const FNfloat G2 = (3 - SQRT3) / 6; + + /* + * --- Skew moved to TransformNoiseCoordinate method --- + * const FNfloat F2 = 0.5f * (SQRT3 - 1); + * FNfloat s = (x + y) * F2; + * x += s; y += s; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + float xi = (float)(x - i); + float yi = (float)(y - j); + + i *= PrimeX; + j *= PrimeY; + int i1 = i + PrimeX; + int j1 = j + PrimeY; + + float t = (xi + yi) * (float)G2; + float x0 = xi - t; + float y0 = yi - t; + + float a0 = (2.0f / 3.0f) - x0 * x0 - y0 * y0; + float value = (a0 * a0) * (a0 * a0) * GradCoord(seed, i, j, x0, y0); + + float a1 = (float)(2 * (1 - 2 * G2) * (1 / G2 - 2)) * t + ((float)(-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a0); + float x1 = x0 - (float)(1 - 2 * G2); + float y1 = y0 - (float)(1 - 2 * G2); + value += (a1 * a1) * (a1 * a1) * GradCoord(seed, i1, j1, x1, y1); + + // Nested conditionals were faster than compact bit logic/arithmetic. + float xmyi = xi - yi; + if (t > G2) + { + if (xi + xmyi > 1) + { + float x2 = x0 + (float)(3 * G2 - 2); + float y2 = y0 + (float)(3 * G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i + (PrimeX << 1), j + PrimeY, x2, y2); + } + } + else + { + float x2 = x0 + (float)G2; + float y2 = y0 + (float)(G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i, j + PrimeY, x2, y2); + } + } + + if (yi - xmyi > 1) + { + float x3 = x0 + (float)(3 * G2 - 1); + float y3 = y0 + (float)(3 * G2 - 2); + float a3 = (2.0f / 3.0f) - x3 * x3 - y3 * y3; + if (a3 > 0) + { + value += (a3 * a3) * (a3 * a3) * GradCoord(seed, i + PrimeX, j + (PrimeY << 1), x3, y3); + } + } + else + { + float x3 = x0 + (float)(G2 - 1); + float y3 = y0 + (float)G2; + float a3 = (2.0f / 3.0f) - x3 * x3 - y3 * y3; + if (a3 > 0) + { + value += (a3 * a3) * (a3 * a3) * GradCoord(seed, i + PrimeX, j, x3, y3); + } + } + } + else + { + if (xi + xmyi < 0) + { + float x2 = x0 + (float)(1 - G2); + float y2 = y0 - (float)G2; + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i - PrimeX, j, x2, y2); + } + } + else + { + float x2 = x0 + (float)(G2 - 1); + float y2 = y0 + (float)G2; + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i + PrimeX, j, x2, y2); + } + } + + if (yi < xmyi) + { + float x2 = x0 - (float)G2; + float y2 = y0 - (float)(G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i, j - PrimeY, x2, y2); + } + } + else + { + float x2 = x0 + (float)G2; + float y2 = y0 + (float)(G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i, j + PrimeY, x2, y2); + } + } + } + + return value * 18.24196194486065f; + } + + template + static float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y, FNfloat z) + { + // 3D OpenSimplex2S case uses two offset rotated cube grids. + + /* + * --- Rotation moved to TransformNoiseCoordinate method --- + * const FNfloat R3 = (FNfloat)(2.0 / 3.0); + * FNfloat r = (x + y + z) * R3; // Rotation, not skew + * x = r - x; y = r - y; z = r - z; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + int k = FastFloor(z); + float xi = (float)(x - i); + float yi = (float)(y - j); + float zi = (float)(z - k); + + i *= PrimeX; + j *= PrimeY; + k *= PrimeZ; + int seed2 = seed + 1293373; + + int xNMask = (int)(-0.5f - xi); + int yNMask = (int)(-0.5f - yi); + int zNMask = (int)(-0.5f - zi); + + float x0 = xi + xNMask; + float y0 = yi + yNMask; + float z0 = zi + zNMask; + float a0 = 0.75f - x0 * x0 - y0 * y0 - z0 * z0; + float value = (a0 * a0) * (a0 * a0) * GradCoord(seed, + i + (xNMask & PrimeX), j + (yNMask & PrimeY), k + (zNMask & PrimeZ), x0, y0, z0); + + float x1 = xi - 0.5f; + float y1 = yi - 0.5f; + float z1 = zi - 0.5f; + float a1 = 0.75f - x1 * x1 - y1 * y1 - z1 * z1; + value += (a1 * a1) * (a1 * a1) * GradCoord(seed2, + i + PrimeX, j + PrimeY, k + PrimeZ, x1, y1, z1); + + float xAFlipMask0 = ((xNMask | 1) << 1) * x1; + float yAFlipMask0 = ((yNMask | 1) << 1) * y1; + float zAFlipMask0 = ((zNMask | 1) << 1) * z1; + float xAFlipMask1 = (-2 - (xNMask << 2)) * x1 - 1.0f; + float yAFlipMask1 = (-2 - (yNMask << 2)) * y1 - 1.0f; + float zAFlipMask1 = (-2 - (zNMask << 2)) * z1 - 1.0f; + + bool skip5 = false; + float a2 = xAFlipMask0 + a0; + if (a2 > 0) + { + float x2 = x0 - (xNMask | 1); + float y2 = y0; + float z2 = z0; + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, + i + (~xNMask & PrimeX), j + (yNMask & PrimeY), k + (zNMask & PrimeZ), x2, y2, z2); + } + else + { + float a3 = yAFlipMask0 + zAFlipMask0 + a0; + if (a3 > 0) + { + float x3 = x0; + float y3 = y0 - (yNMask | 1); + float z3 = z0 - (zNMask | 1); + value += (a3 * a3) * (a3 * a3) * GradCoord(seed, + i + (xNMask & PrimeX), j + (~yNMask & PrimeY), k + (~zNMask & PrimeZ), x3, y3, z3); + } + + float a4 = xAFlipMask1 + a1; + if (a4 > 0) + { + float x4 = (xNMask | 1) + x1; + float y4 = y1; + float z4 = z1; + value += (a4 * a4) * (a4 * a4) * GradCoord(seed2, + i + (xNMask & (PrimeX * 2)), j + PrimeY, k + PrimeZ, x4, y4, z4); + skip5 = true; + } + } + + bool skip9 = false; + float a6 = yAFlipMask0 + a0; + if (a6 > 0) + { + float x6 = x0; + float y6 = y0 - (yNMask | 1); + float z6 = z0; + value += (a6 * a6) * (a6 * a6) * GradCoord(seed, + i + (xNMask & PrimeX), j + (~yNMask & PrimeY), k + (zNMask & PrimeZ), x6, y6, z6); + } + else + { + float a7 = xAFlipMask0 + zAFlipMask0 + a0; + if (a7 > 0) + { + float x7 = x0 - (xNMask | 1); + float y7 = y0; + float z7 = z0 - (zNMask | 1); + value += (a7 * a7) * (a7 * a7) * GradCoord(seed, + i + (~xNMask & PrimeX), j + (yNMask & PrimeY), k + (~zNMask & PrimeZ), x7, y7, z7); + } + + float a8 = yAFlipMask1 + a1; + if (a8 > 0) + { + float x8 = x1; + float y8 = (yNMask | 1) + y1; + float z8 = z1; + value += (a8 * a8) * (a8 * a8) * GradCoord(seed2, + i + PrimeX, j + (yNMask & (PrimeY << 1)), k + PrimeZ, x8, y8, z8); + skip9 = true; + } + } + + bool skipD = false; + float aA = zAFlipMask0 + a0; + if (aA > 0) + { + float xA = x0; + float yA = y0; + float zA = z0 - (zNMask | 1); + value += (aA * aA) * (aA * aA) * GradCoord(seed, + i + (xNMask & PrimeX), j + (yNMask & PrimeY), k + (~zNMask & PrimeZ), xA, yA, zA); + } + else + { + float aB = xAFlipMask0 + yAFlipMask0 + a0; + if (aB > 0) + { + float xB = x0 - (xNMask | 1); + float yB = y0 - (yNMask | 1); + float zB = z0; + value += (aB * aB) * (aB * aB) * GradCoord(seed, + i + (~xNMask & PrimeX), j + (~yNMask & PrimeY), k + (zNMask & PrimeZ), xB, yB, zB); + } + + float aC = zAFlipMask1 + a1; + if (aC > 0) + { + float xC = x1; + float yC = y1; + float zC = (zNMask | 1) + z1; + value += (aC * aC) * (aC * aC) * GradCoord(seed2, + i + PrimeX, j + PrimeY, k + (zNMask & (PrimeZ << 1)), xC, yC, zC); + skipD = true; + } + } + + if (!skip5) + { + float a5 = yAFlipMask1 + zAFlipMask1 + a1; + if (a5 > 0) + { + float x5 = x1; + float y5 = (yNMask | 1) + y1; + float z5 = (zNMask | 1) + z1; + value += (a5 * a5) * (a5 * a5) * GradCoord(seed2, + i + PrimeX, j + (yNMask & (PrimeY << 1)), k + (zNMask & (PrimeZ << 1)), x5, y5, z5); + } + } + + if (!skip9) + { + float a9 = xAFlipMask1 + zAFlipMask1 + a1; + if (a9 > 0) + { + float x9 = (xNMask | 1) + x1; + float y9 = y1; + float z9 = (zNMask | 1) + z1; + value += (a9 * a9) * (a9 * a9) * GradCoord(seed2, + i + (xNMask & (PrimeX * 2)), j + PrimeY, k + (zNMask & (PrimeZ << 1)), x9, y9, z9); + } + } + + if (!skipD) + { + float aD = xAFlipMask1 + yAFlipMask1 + a1; + if (aD > 0) + { + float xD = (xNMask | 1) + x1; + float yD = (yNMask | 1) + y1; + float zD = z1; + value += (aD * aD) * (aD * aD) * GradCoord(seed2, + i + (xNMask & (PrimeX << 1)), j + (yNMask & (PrimeY << 1)), k + PrimeZ, xD, yD, zD); + } + } + + return value * 9.046026385208288f; + } + + + // Cellular Noise + + template + static float SingleCellular(int seed, int x, int y) + { + float distance = 1e10f; + constexpr float cellularJitter = 0.43701595f /** mCellularJitterModifier*/; + + constexpr int xPrimed = (x - 1) * PrimeX; + constexpr int yPrimedBase = (y - 1) * PrimeY; + + for (int xi = x - 1; xi <= x + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = y - 1; yi <= y + 1; yi++) + { + int hash = Hash(seed, xPrimed, yPrimed); + int idx = hash & (255 << 1); + + float vecX = (float)(xi - x) + Lookup::RandVecs2D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs2D[idx | 1] * cellularJitter; + + float newDistance = vecX * vecX + vecY * vecY; + + if (newDistance < distance) + { + distance = newDistance; + //closestHash = hash; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + + distance = FastSqrt(distance); + + return distance; + } + +// GCC raises warnings when integer overflows occur, which are needed for hashing here. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations" +#endif + + template + float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z) + { + float distance = 1e10f; + int closestHash = 0; + + float cellularJitter = 0.39614353f; + + int xPrimed = (x - 1) * PrimeX; + int yPrimedBase = (y - 1) * PrimeY; + int zPrimedBase = (z - 1) * PrimeZ; + + for (int xi = x - 1; xi <= x + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = y - 1; yi <= y + 1; yi++) + { + int zPrimed = zPrimedBase; + + for (int zi = z - 1; zi <= z + 1; zi++) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + int idx = hash & (255 << 2); + + float vecX = (float)(xi - x) + Lookup::RandVecs3D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs3D[idx | 1] * cellularJitter; + float vecZ = (float)(zi - z) + Lookup::RandVecs3D[idx | 2] * cellularJitter; + + float newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ; + + if (newDistance < distance) + { + distance = newDistance; + closestHash = hash; + } + zPrimed += PrimeZ; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + + distance = FastSqrt(distance); + + return distance - 1; + } + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + // Perlin Noise + + template + static float SinglePerlin(int seed, FNfloat x, FNfloat y) + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + + float xd0 = (float)(x - x0); + float yd0 = (float)(y - y0); + float xd1 = xd0 - 1; + float yd1 = yd0 - 1; + + float xs = InterpQuintic(xd0); + float ys = InterpQuintic(yd0); + + x0 *= PrimeX; + y0 *= PrimeY; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + + float xf0 = Lerp(GradCoord(seed, x0, y0, xd0, yd0), GradCoord(seed, x1, y0, xd1, yd0), xs); + float xf1 = Lerp(GradCoord(seed, x0, y1, xd0, yd1), GradCoord(seed, x1, y1, xd1, yd1), xs); + + return Lerp(xf0, xf1, ys) * 1.4247691104677813f; + } + + template + static float SinglePerlin(int seed, FNfloat x, FNfloat y, FNfloat z) + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + int z0 = FastFloor(z); + + float xd0 = (float)(x - x0); + float yd0 = (float)(y - y0); + float zd0 = (float)(z - z0); + float xd1 = xd0 - 1; + float yd1 = yd0 - 1; + float zd1 = zd0 - 1; + + float xs = InterpQuintic(xd0); + float ys = InterpQuintic(yd0); + float zs = InterpQuintic(zd0); + + x0 *= PrimeX; + y0 *= PrimeY; + z0 *= PrimeZ; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + int z1 = z0 + PrimeZ; + + float xf00 = Lerp(GradCoord(seed, x0, y0, z0, xd0, yd0, zd0), GradCoord(seed, x1, y0, z0, xd1, yd0, zd0), xs); + float xf10 = Lerp(GradCoord(seed, x0, y1, z0, xd0, yd1, zd0), GradCoord(seed, x1, y1, z0, xd1, yd1, zd0), xs); + float xf01 = Lerp(GradCoord(seed, x0, y0, z1, xd0, yd0, zd1), GradCoord(seed, x1, y0, z1, xd1, yd0, zd1), xs); + float xf11 = Lerp(GradCoord(seed, x0, y1, z1, xd0, yd1, zd1), GradCoord(seed, x1, y1, z1, xd1, yd1, zd1), xs); + + float yf0 = Lerp(xf00, xf10, ys); + float yf1 = Lerp(xf01, xf11, ys); + + return Lerp(yf0, yf1, zs) * 0.964921414852142333984375f; + } + + + // Value Cubic Noise + + template + static float SingleValueCubic(int seed, FNfloat x, FNfloat y) + { + int x1 = FastFloor(x); + int y1 = FastFloor(y); + + float xs = (float)(x - x1); + float ys = (float)(y - y1); + + x1 *= PrimeX; + y1 *= PrimeY; + int x0 = x1 - PrimeX; + int y0 = y1 - PrimeY; + int x2 = x1 + PrimeX; + int y2 = y1 + PrimeY; + int x3 = x1 + (int)((long)PrimeX << 1); + int y3 = y1 + (int)((long)PrimeY << 1); + + return CubicLerp( + CubicLerp(ValCoord(seed, x0, y0), ValCoord(seed, x1, y0), ValCoord(seed, x2, y0), ValCoord(seed, x3, y0), + xs), + CubicLerp(ValCoord(seed, x0, y1), ValCoord(seed, x1, y1), ValCoord(seed, x2, y1), ValCoord(seed, x3, y1), + xs), + CubicLerp(ValCoord(seed, x0, y2), ValCoord(seed, x1, y2), ValCoord(seed, x2, y2), ValCoord(seed, x3, y2), + xs), + CubicLerp(ValCoord(seed, x0, y3), ValCoord(seed, x1, y3), ValCoord(seed, x2, y3), ValCoord(seed, x3, y3), + xs), + ys) * (1 / (1.5f * 1.5f)); + } + + template + static float SingleValueCubic(int seed, FNfloat x, FNfloat y, FNfloat z) + { + int x1 = FastFloor(x); + int y1 = FastFloor(y); + int z1 = FastFloor(z); + + float xs = (float)(x - x1); + float ys = (float)(y - y1); + float zs = (float)(z - z1); + + x1 *= PrimeX; + y1 *= PrimeY; + z1 *= PrimeZ; + + int x0 = x1 - PrimeX; + int y0 = y1 - PrimeY; + int z0 = z1 - PrimeZ; + int x2 = x1 + PrimeX; + int y2 = y1 + PrimeY; + int z2 = z1 + PrimeZ; + int x3 = x1 + (int)((long)PrimeX << 1); + int y3 = y1 + (int)((long)PrimeY << 1); + int z3 = z1 + (int)((long)PrimeZ << 1); + + + return CubicLerp( + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z0), ValCoord(seed, x1, y0, z0), ValCoord(seed, x2, y0, z0), ValCoord(seed, x3, y0, z0), xs), + CubicLerp(ValCoord(seed, x0, y1, z0), ValCoord(seed, x1, y1, z0), ValCoord(seed, x2, y1, z0), ValCoord(seed, x3, y1, z0), xs), + CubicLerp(ValCoord(seed, x0, y2, z0), ValCoord(seed, x1, y2, z0), ValCoord(seed, x2, y2, z0), ValCoord(seed, x3, y2, z0), xs), + CubicLerp(ValCoord(seed, x0, y3, z0), ValCoord(seed, x1, y3, z0), ValCoord(seed, x2, y3, z0), ValCoord(seed, x3, y3, z0), xs), + ys), + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z1), ValCoord(seed, x1, y0, z1), ValCoord(seed, x2, y0, z1), ValCoord(seed, x3, y0, z1), xs), + CubicLerp(ValCoord(seed, x0, y1, z1), ValCoord(seed, x1, y1, z1), ValCoord(seed, x2, y1, z1), ValCoord(seed, x3, y1, z1), xs), + CubicLerp(ValCoord(seed, x0, y2, z1), ValCoord(seed, x1, y2, z1), ValCoord(seed, x2, y2, z1), ValCoord(seed, x3, y2, z1), xs), + CubicLerp(ValCoord(seed, x0, y3, z1), ValCoord(seed, x1, y3, z1), ValCoord(seed, x2, y3, z1), ValCoord(seed, x3, y3, z1), xs), + ys), + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z2), ValCoord(seed, x1, y0, z2), ValCoord(seed, x2, y0, z2), ValCoord(seed, x3, y0, z2), xs), + CubicLerp(ValCoord(seed, x0, y1, z2), ValCoord(seed, x1, y1, z2), ValCoord(seed, x2, y1, z2), ValCoord(seed, x3, y1, z2), xs), + CubicLerp(ValCoord(seed, x0, y2, z2), ValCoord(seed, x1, y2, z2), ValCoord(seed, x2, y2, z2), ValCoord(seed, x3, y2, z2), xs), + CubicLerp(ValCoord(seed, x0, y3, z2), ValCoord(seed, x1, y3, z2), ValCoord(seed, x2, y3, z2), ValCoord(seed, x3, y3, z2), xs), + ys), + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z3), ValCoord(seed, x1, y0, z3), ValCoord(seed, x2, y0, z3), ValCoord(seed, x3, y0, z3), xs), + CubicLerp(ValCoord(seed, x0, y1, z3), ValCoord(seed, x1, y1, z3), ValCoord(seed, x2, y1, z3), ValCoord(seed, x3, y1, z3), xs), + CubicLerp(ValCoord(seed, x0, y2, z3), ValCoord(seed, x1, y2, z3), ValCoord(seed, x2, y2, z3), ValCoord(seed, x3, y2, z3), xs), + CubicLerp(ValCoord(seed, x0, y3, z3), ValCoord(seed, x1, y3, z3), ValCoord(seed, x2, y3, z3), ValCoord(seed, x3, y3, z3), xs), + ys), + zs) * (1 / (1.5f * 1.5f * 1.5f)); + } + + + // Value Noise + + template + static float SingleValue(int seed, FNfloat x, FNfloat y) + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + + float xs = InterpHermite((float)(x - x0)); + float ys = InterpHermite((float)(y - y0)); + + x0 *= PrimeX; + y0 *= PrimeY; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + + float xf0 = Lerp(ValCoord(seed, x0, y0), ValCoord(seed, x1, y0), xs); + float xf1 = Lerp(ValCoord(seed, x0, y1), ValCoord(seed, x1, y1), xs); + + return Lerp(xf0, xf1, ys); + } + + template + static float SingleValue(int seed, FNfloat x, FNfloat y, FNfloat z) + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + int z0 = FastFloor(z); + + float xs = InterpHermite((float)(x - x0)); + float ys = InterpHermite((float)(y - y0)); + float zs = InterpHermite((float)(z - z0)); + + x0 *= PrimeX; + y0 *= PrimeY; + z0 *= PrimeZ; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + int z1 = z0 + PrimeZ; + + float xf00 = Lerp(ValCoord(seed, x0, y0, z0), ValCoord(seed, x1, y0, z0), xs); + float xf10 = Lerp(ValCoord(seed, x0, y1, z0), ValCoord(seed, x1, y1, z0), xs); + float xf01 = Lerp(ValCoord(seed, x0, y0, z1), ValCoord(seed, x1, y0, z1), xs); + float xf11 = Lerp(ValCoord(seed, x0, y1, z1), ValCoord(seed, x1, y1, z1), xs); + + float yf0 = Lerp(xf00, xf10, ys); + float yf1 = Lerp(xf01, xf11, ys); + + return Lerp(yf0, yf1, zs); + } + + + // Domain Warp + +template <> +struct Arguments_must_be_floating_point_values {}; +template <> +struct Arguments_must_be_floating_point_values {}; +template <> +struct Arguments_must_be_floating_point_values {}; + +template +const T Lookup::Gradients2D[] = +{ + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.38268343236509f, 0.923879532511287f, 0.923879532511287f, 0.38268343236509f, 0.923879532511287f, -0.38268343236509f, 0.38268343236509f, -0.923879532511287f, + -0.38268343236509f, -0.923879532511287f, -0.923879532511287f, -0.38268343236509f, -0.923879532511287f, 0.38268343236509f, -0.38268343236509f, 0.923879532511287f, +}; + +template +const T Lookup::RandVecs2D[] = +{ + -0.2700222198f, -0.9628540911f, 0.3863092627f, -0.9223693152f, 0.04444859006f, -0.999011673f, -0.5992523158f, -0.8005602176f, -0.7819280288f, 0.6233687174f, 0.9464672271f, 0.3227999196f, -0.6514146797f, -0.7587218957f, 0.9378472289f, 0.347048376f, + -0.8497875957f, -0.5271252623f, -0.879042592f, 0.4767432447f, -0.892300288f, -0.4514423508f, -0.379844434f, -0.9250503802f, -0.9951650832f, 0.0982163789f, 0.7724397808f, -0.6350880136f, 0.7573283322f, -0.6530343002f, -0.9928004525f, -0.119780055f, + -0.0532665713f, 0.9985803285f, 0.9754253726f, -0.2203300762f, -0.7665018163f, 0.6422421394f, 0.991636706f, 0.1290606184f, -0.994696838f, 0.1028503788f, -0.5379205513f, -0.84299554f, 0.5022815471f, -0.8647041387f, 0.4559821461f, -0.8899889226f, + -0.8659131224f, -0.5001944266f, 0.0879458407f, -0.9961252577f, -0.5051684983f, 0.8630207346f, 0.7753185226f, -0.6315704146f, -0.6921944612f, 0.7217110418f, -0.5191659449f, -0.8546734591f, 0.8978622882f, -0.4402764035f, -0.1706774107f, 0.9853269617f, + -0.9353430106f, -0.3537420705f, -0.9992404798f, 0.03896746794f, -0.2882064021f, -0.9575683108f, -0.9663811329f, 0.2571137995f, -0.8759714238f, -0.4823630009f, -0.8303123018f, -0.5572983775f, 0.05110133755f, -0.9986934731f, -0.8558373281f, -0.5172450752f, + 0.09887025282f, 0.9951003332f, 0.9189016087f, 0.3944867976f, -0.2439375892f, -0.9697909324f, -0.8121409387f, -0.5834613061f, -0.9910431363f, 0.1335421355f, 0.8492423985f, -0.5280031709f, -0.9717838994f, -0.2358729591f, 0.9949457207f, 0.1004142068f, + 0.6241065508f, -0.7813392434f, 0.662910307f, 0.7486988212f, -0.7197418176f, 0.6942418282f, -0.8143370775f, -0.5803922158f, 0.104521054f, -0.9945226741f, -0.1065926113f, -0.9943027784f, 0.445799684f, -0.8951327509f, 0.105547406f, 0.9944142724f, + -0.992790267f, 0.1198644477f, -0.8334366408f, 0.552615025f, 0.9115561563f, -0.4111755999f, 0.8285544909f, -0.5599084351f, 0.7217097654f, -0.6921957921f, 0.4940492677f, -0.8694339084f, -0.3652321272f, -0.9309164803f, -0.9696606758f, 0.2444548501f, + 0.08925509731f, -0.996008799f, 0.5354071276f, -0.8445941083f, -0.1053576186f, 0.9944343981f, -0.9890284586f, 0.1477251101f, 0.004856104961f, 0.9999882091f, 0.9885598478f, 0.1508291331f, 0.9286129562f, -0.3710498316f, -0.5832393863f, -0.8123003252f, + 0.3015207509f, 0.9534596146f, -0.9575110528f, 0.2883965738f, 0.9715802154f, -0.2367105511f, 0.229981792f, 0.9731949318f, 0.955763816f, -0.2941352207f, 0.740956116f, 0.6715534485f, -0.9971513787f, -0.07542630764f, 0.6905710663f, -0.7232645452f, + -0.290713703f, -0.9568100872f, 0.5912777791f, -0.8064679708f, -0.9454592212f, -0.325740481f, 0.6664455681f, 0.74555369f, 0.6236134912f, 0.7817328275f, 0.9126993851f, -0.4086316587f, -0.8191762011f, 0.5735419353f, -0.8812745759f, -0.4726046147f, + 0.9953313627f, 0.09651672651f, 0.9855650846f, -0.1692969699f, -0.8495980887f, 0.5274306472f, 0.6174853946f, -0.7865823463f, 0.8508156371f, 0.52546432f, 0.9985032451f, -0.05469249926f, 0.1971371563f, -0.9803759185f, 0.6607855748f, -0.7505747292f, + -0.03097494063f, 0.9995201614f, -0.6731660801f, 0.739491331f, -0.7195018362f, -0.6944905383f, 0.9727511689f, 0.2318515979f, 0.9997059088f, -0.0242506907f, 0.4421787429f, -0.8969269532f, 0.9981350961f, -0.061043673f, -0.9173660799f, -0.3980445648f, + -0.8150056635f, -0.5794529907f, -0.8789331304f, 0.4769450202f, 0.0158605829f, 0.999874213f, -0.8095464474f, 0.5870558317f, -0.9165898907f, -0.3998286786f, -0.8023542565f, 0.5968480938f, -0.5176737917f, 0.8555780767f, -0.8154407307f, -0.5788405779f, + 0.4022010347f, -0.9155513791f, -0.9052556868f, -0.4248672045f, 0.7317445619f, 0.6815789728f, -0.5647632201f, -0.8252529947f, -0.8403276335f, -0.5420788397f, -0.9314281527f, 0.363925262f, 0.5238198472f, 0.8518290719f, 0.7432803869f, -0.6689800195f, + -0.985371561f, -0.1704197369f, 0.4601468731f, 0.88784281f, 0.825855404f, 0.5638819483f, 0.6182366099f, 0.7859920446f, 0.8331502863f, -0.553046653f, 0.1500307506f, 0.9886813308f, -0.662330369f, -0.7492119075f, -0.668598664f, 0.743623444f, + 0.7025606278f, 0.7116238924f, -0.5419389763f, -0.8404178401f, -0.3388616456f, 0.9408362159f, 0.8331530315f, 0.5530425174f, -0.2989720662f, -0.9542618632f, 0.2638522993f, 0.9645630949f, 0.124108739f, -0.9922686234f, -0.7282649308f, -0.6852956957f, + 0.6962500149f, 0.7177993569f, -0.9183535368f, 0.3957610156f, -0.6326102274f, -0.7744703352f, -0.9331891859f, -0.359385508f, -0.1153779357f, -0.9933216659f, 0.9514974788f, -0.3076565421f, -0.08987977445f, -0.9959526224f, 0.6678496916f, 0.7442961705f, + 0.7952400393f, -0.6062947138f, -0.6462007402f, -0.7631674805f, -0.2733598753f, 0.9619118351f, 0.9669590226f, -0.254931851f, -0.9792894595f, 0.2024651934f, -0.5369502995f, -0.8436138784f, -0.270036471f, -0.9628500944f, -0.6400277131f, 0.7683518247f, + -0.7854537493f, -0.6189203566f, 0.06005905383f, -0.9981948257f, -0.02455770378f, 0.9996984141f, -0.65983623f, 0.751409442f, -0.6253894466f, -0.7803127835f, -0.6210408851f, -0.7837781695f, 0.8348888491f, 0.5504185768f, -0.1592275245f, 0.9872419133f, + 0.8367622488f, 0.5475663786f, -0.8675753916f, -0.4973056806f, -0.2022662628f, -0.9793305667f, 0.9399189937f, 0.3413975472f, 0.9877404807f, -0.1561049093f, -0.9034455656f, 0.4287028224f, 0.1269804218f, -0.9919052235f, -0.3819600854f, 0.924178821f, + 0.9754625894f, 0.2201652486f, -0.3204015856f, -0.9472818081f, -0.9874760884f, 0.1577687387f, 0.02535348474f, -0.9996785487f, 0.4835130794f, -0.8753371362f, -0.2850799925f, -0.9585037287f, -0.06805516006f, -0.99768156f, -0.7885244045f, -0.6150034663f, + 0.3185392127f, -0.9479096845f, 0.8880043089f, 0.4598351306f, 0.6476921488f, -0.7619021462f, 0.9820241299f, 0.1887554194f, 0.9357275128f, -0.3527237187f, -0.8894895414f, 0.4569555293f, 0.7922791302f, 0.6101588153f, 0.7483818261f, 0.6632681526f, + -0.7288929755f, -0.6846276581f, 0.8729032783f, -0.4878932944f, 0.8288345784f, 0.5594937369f, 0.08074567077f, 0.9967347374f, 0.9799148216f, -0.1994165048f, -0.580730673f, -0.8140957471f, -0.4700049791f, -0.8826637636f, 0.2409492979f, 0.9705377045f, + 0.9437816757f, -0.3305694308f, -0.8927998638f, -0.4504535528f, -0.8069622304f, 0.5906030467f, 0.06258973166f, 0.9980393407f, -0.9312597469f, 0.3643559849f, 0.5777449785f, 0.8162173362f, -0.3360095855f, -0.941858566f, 0.697932075f, -0.7161639607f, + -0.002008157227f, -0.9999979837f, -0.1827294312f, -0.9831632392f, -0.6523911722f, 0.7578824173f, -0.4302626911f, -0.9027037258f, -0.9985126289f, -0.05452091251f, -0.01028102172f, -0.9999471489f, -0.4946071129f, 0.8691166802f, -0.2999350194f, 0.9539596344f, + 0.8165471961f, 0.5772786819f, 0.2697460475f, 0.962931498f, -0.7306287391f, -0.6827749597f, -0.7590952064f, -0.6509796216f, -0.907053853f, 0.4210146171f, -0.5104861064f, -0.8598860013f, 0.8613350597f, 0.5080373165f, 0.5007881595f, -0.8655698812f, + -0.654158152f, 0.7563577938f, -0.8382755311f, -0.545246856f, 0.6940070834f, 0.7199681717f, 0.06950936031f, 0.9975812994f, 0.1702942185f, -0.9853932612f, 0.2695973274f, 0.9629731466f, 0.5519612192f, -0.8338697815f, 0.225657487f, -0.9742067022f, + 0.4215262855f, -0.9068161835f, 0.4881873305f, -0.8727388672f, -0.3683854996f, -0.9296731273f, -0.9825390578f, 0.1860564427f, 0.81256471f, 0.5828709909f, 0.3196460933f, -0.9475370046f, 0.9570913859f, 0.2897862643f, -0.6876655497f, -0.7260276109f, + -0.9988770922f, -0.047376731f, -0.1250179027f, 0.992154486f, -0.8280133617f, 0.560708367f, 0.9324863769f, -0.3612051451f, 0.6394653183f, 0.7688199442f, -0.01623847064f, -0.9998681473f, -0.9955014666f, -0.09474613458f, -0.81453315f, 0.580117012f, + 0.4037327978f, -0.9148769469f, 0.9944263371f, 0.1054336766f, -0.1624711654f, 0.9867132919f, -0.9949487814f, -0.100383875f, -0.6995302564f, 0.7146029809f, 0.5263414922f, -0.85027327f, -0.5395221479f, 0.841971408f, 0.6579370318f, 0.7530729462f, + 0.01426758847f, -0.9998982128f, -0.6734383991f, 0.7392433447f, 0.639412098f, -0.7688642071f, 0.9211571421f, 0.3891908523f, -0.146637214f, -0.9891903394f, -0.782318098f, 0.6228791163f, -0.5039610839f, -0.8637263605f, -0.7743120191f, -0.6328039957f, +}; + +template +const T Lookup::Gradients3D[] = +{ + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 1, 1, 0, 0, 0,-1, 1, 0, -1, 1, 0, 0, 0,-1,-1, 0 +}; + +template +const T Lookup::RandVecs3D[] = +{ + -0.7292736885f, -0.6618439697f, 0.1735581948f, 0, 0.790292081f, -0.5480887466f, -0.2739291014f, 0, 0.7217578935f, 0.6226212466f, -0.3023380997f, 0, 0.565683137f, -0.8208298145f, -0.0790000257f, 0, 0.760049034f, -0.5555979497f, -0.3370999617f, 0, 0.3713945616f, 0.5011264475f, 0.7816254623f, 0, -0.1277062463f, -0.4254438999f, -0.8959289049f, 0, -0.2881560924f, -0.5815838982f, 0.7607405838f, 0, + 0.5849561111f, -0.662820239f, -0.4674352136f, 0, 0.3307171178f, 0.0391653737f, 0.94291689f, 0, 0.8712121778f, -0.4113374369f, -0.2679381538f, 0, 0.580981015f, 0.7021915846f, 0.4115677815f, 0, 0.503756873f, 0.6330056931f, -0.5878203852f, 0, 0.4493712205f, 0.601390195f, 0.6606022552f, 0, -0.6878403724f, 0.09018890807f, -0.7202371714f, 0, -0.5958956522f, -0.6469350577f, 0.475797649f, 0, + -0.5127052122f, 0.1946921978f, -0.8361987284f, 0, -0.9911507142f, -0.05410276466f, -0.1212153153f, 0, -0.2149721042f, 0.9720882117f, -0.09397607749f, 0, -0.7518650936f, -0.5428057603f, 0.3742469607f, 0, 0.5237068895f, 0.8516377189f, -0.02107817834f, 0, 0.6333504779f, 0.1926167129f, -0.7495104896f, 0, -0.06788241606f, 0.3998305789f, 0.9140719259f, 0, -0.5538628599f, -0.4729896695f, -0.6852128902f, 0, + -0.7261455366f, -0.5911990757f, 0.3509933228f, 0, -0.9229274737f, -0.1782808786f, 0.3412049336f, 0, -0.6968815002f, 0.6511274338f, 0.3006480328f, 0, 0.9608044783f, -0.2098363234f, -0.1811724921f, 0, 0.06817146062f, -0.9743405129f, 0.2145069156f, 0, -0.3577285196f, -0.6697087264f, -0.6507845481f, 0, -0.1868621131f, 0.7648617052f, -0.6164974636f, 0, -0.6541697588f, 0.3967914832f, 0.6439087246f, 0, + 0.6993340405f, -0.6164538506f, 0.3618239211f, 0, -0.1546665739f, 0.6291283928f, 0.7617583057f, 0, -0.6841612949f, -0.2580482182f, -0.6821542638f, 0, 0.5383980957f, 0.4258654885f, 0.7271630328f, 0, -0.5026987823f, -0.7939832935f, -0.3418836993f, 0, 0.3202971715f, 0.2834415347f, 0.9039195862f, 0, 0.8683227101f, -0.0003762656404f, -0.4959995258f, 0, 0.791120031f, -0.08511045745f, 0.6057105799f, 0, + -0.04011016052f, -0.4397248749f, 0.8972364289f, 0, 0.9145119872f, 0.3579346169f, -0.1885487608f, 0, -0.9612039066f, -0.2756484276f, 0.01024666929f, 0, 0.6510361721f, -0.2877799159f, -0.7023778346f, 0, -0.2041786351f, 0.7365237271f, 0.644859585f, 0, -0.7718263711f, 0.3790626912f, 0.5104855816f, 0, -0.3060082741f, -0.7692987727f, 0.5608371729f, 0, 0.454007341f, -0.5024843065f, 0.7357899537f, 0, + 0.4816795475f, 0.6021208291f, -0.6367380315f, 0, 0.6961980369f, -0.3222197429f, 0.641469197f, 0, -0.6532160499f, -0.6781148932f, 0.3368515753f, 0, 0.5089301236f, -0.6154662304f, -0.6018234363f, 0, -0.1635919754f, -0.9133604627f, -0.372840892f, 0, 0.52408019f, -0.8437664109f, 0.1157505864f, 0, 0.5902587356f, 0.4983817807f, -0.6349883666f, 0, 0.5863227872f, 0.494764745f, 0.6414307729f, 0, + 0.6779335087f, 0.2341345225f, 0.6968408593f, 0, 0.7177054546f, -0.6858979348f, 0.120178631f, 0, -0.5328819713f, -0.5205125012f, 0.6671608058f, 0, -0.8654874251f, -0.0700727088f, -0.4960053754f, 0, -0.2861810166f, 0.7952089234f, 0.5345495242f, 0, -0.04849529634f, 0.9810836427f, -0.1874115585f, 0, -0.6358521667f, 0.6058348682f, 0.4781800233f, 0, 0.6254794696f, -0.2861619734f, 0.7258696564f, 0, + -0.2585259868f, 0.5061949264f, -0.8227581726f, 0, 0.02136306781f, 0.5064016808f, -0.8620330371f, 0, 0.200111773f, 0.8599263484f, 0.4695550591f, 0, 0.4743561372f, 0.6014985084f, -0.6427953014f, 0, 0.6622993731f, -0.5202474575f, -0.5391679918f, 0, 0.08084972818f, -0.6532720452f, 0.7527940996f, 0, -0.6893687501f, 0.0592860349f, 0.7219805347f, 0, -0.1121887082f, -0.9673185067f, 0.2273952515f, 0, + 0.7344116094f, 0.5979668656f, -0.3210532909f, 0, 0.5789393465f, -0.2488849713f, 0.7764570201f, 0, 0.6988182827f, 0.3557169806f, -0.6205791146f, 0, -0.8636845529f, -0.2748771249f, -0.4224826141f, 0, -0.4247027957f, -0.4640880967f, 0.777335046f, 0, 0.5257722489f, -0.8427017621f, 0.1158329937f, 0, 0.9343830603f, 0.316302472f, -0.1639543925f, 0, -0.1016836419f, -0.8057303073f, -0.5834887393f, 0, + -0.6529238969f, 0.50602126f, -0.5635892736f, 0, -0.2465286165f, -0.9668205684f, -0.06694497494f, 0, -0.9776897119f, -0.2099250524f, -0.007368825344f, 0, 0.7736893337f, 0.5734244712f, 0.2694238123f, 0, -0.6095087895f, 0.4995678998f, 0.6155736747f, 0, 0.5794535482f, 0.7434546771f, 0.3339292269f, 0, -0.8226211154f, 0.08142581855f, 0.5627293636f, 0, -0.510385483f, 0.4703667658f, 0.7199039967f, 0, + -0.5764971849f, -0.07231656274f, -0.8138926898f, 0, 0.7250628871f, 0.3949971505f, -0.5641463116f, 0, -0.1525424005f, 0.4860840828f, -0.8604958341f, 0, -0.5550976208f, -0.4957820792f, 0.667882296f, 0, -0.1883614327f, 0.9145869398f, 0.357841725f, 0, 0.7625556724f, -0.5414408243f, -0.3540489801f, 0, -0.5870231946f, -0.3226498013f, -0.7424963803f, 0, 0.3051124198f, 0.2262544068f, -0.9250488391f, 0, + 0.6379576059f, 0.577242424f, -0.5097070502f, 0, -0.5966775796f, 0.1454852398f, -0.7891830656f, 0, -0.658330573f, 0.6555487542f, -0.3699414651f, 0, 0.7434892426f, 0.2351084581f, 0.6260573129f, 0, 0.5562114096f, 0.8264360377f, -0.0873632843f, 0, -0.3028940016f, -0.8251527185f, 0.4768419182f, 0, 0.1129343818f, -0.985888439f, -0.1235710781f, 0, 0.5937652891f, -0.5896813806f, 0.5474656618f, 0, + 0.6757964092f, -0.5835758614f, -0.4502648413f, 0, 0.7242302609f, -0.1152719764f, 0.6798550586f, 0, -0.9511914166f, 0.0753623979f, -0.2992580792f, 0, 0.2539470961f, -0.1886339355f, 0.9486454084f, 0, 0.571433621f, -0.1679450851f, -0.8032795685f, 0, -0.06778234979f, 0.3978269256f, 0.9149531629f, 0, 0.6074972649f, 0.733060024f, -0.3058922593f, 0, -0.5435478392f, 0.1675822484f, 0.8224791405f, 0, + -0.5876678086f, -0.3380045064f, -0.7351186982f, 0, -0.7967562402f, 0.04097822706f, -0.6029098428f, 0, -0.1996350917f, 0.8706294745f, 0.4496111079f, 0, -0.02787660336f, -0.9106232682f, -0.4122962022f, 0, -0.7797625996f, -0.6257634692f, 0.01975775581f, 0, -0.5211232846f, 0.7401644346f, -0.4249554471f, 0, 0.8575424857f, 0.4053272873f, -0.3167501783f, 0, 0.1045223322f, 0.8390195772f, -0.5339674439f, 0, + 0.3501822831f, 0.9242524096f, -0.1520850155f, 0, 0.1987849858f, 0.07647613266f, 0.9770547224f, 0, 0.7845996363f, 0.6066256811f, -0.1280964233f, 0, 0.09006737436f, -0.9750989929f, -0.2026569073f, 0, -0.8274343547f, -0.542299559f, 0.1458203587f, 0, -0.3485797732f, -0.415802277f, 0.840000362f, 0, -0.2471778936f, -0.7304819962f, -0.6366310879f, 0, -0.3700154943f, 0.8577948156f, 0.3567584454f, 0, + 0.5913394901f, -0.548311967f, -0.5913303597f, 0, 0.1204873514f, -0.7626472379f, -0.6354935001f, 0, 0.616959265f, 0.03079647928f, 0.7863922953f, 0, 0.1258156836f, -0.6640829889f, -0.7369967419f, 0, -0.6477565124f, -0.1740147258f, -0.7417077429f, 0, 0.6217889313f, -0.7804430448f, -0.06547655076f, 0, 0.6589943422f, -0.6096987708f, 0.4404473475f, 0, -0.2689837504f, -0.6732403169f, -0.6887635427f, 0, + -0.3849775103f, 0.5676542638f, 0.7277093879f, 0, 0.5754444408f, 0.8110471154f, -0.1051963504f, 0, 0.9141593684f, 0.3832947817f, 0.131900567f, 0, -0.107925319f, 0.9245493968f, 0.3654593525f, 0, 0.377977089f, 0.3043148782f, 0.8743716458f, 0, -0.2142885215f, -0.8259286236f, 0.5214617324f, 0, 0.5802544474f, 0.4148098596f, -0.7008834116f, 0, -0.1982660881f, 0.8567161266f, -0.4761596756f, 0, + -0.03381553704f, 0.3773180787f, -0.9254661404f, 0, -0.6867922841f, -0.6656597827f, 0.2919133642f, 0, 0.7731742607f, -0.2875793547f, -0.5652430251f, 0, -0.09655941928f, 0.9193708367f, -0.3813575004f, 0, 0.2715702457f, -0.9577909544f, -0.09426605581f, 0, 0.2451015704f, -0.6917998565f, -0.6792188003f, 0, 0.977700782f, -0.1753855374f, 0.1155036542f, 0, -0.5224739938f, 0.8521606816f, 0.02903615945f, 0, + -0.7734880599f, -0.5261292347f, 0.3534179531f, 0, -0.7134492443f, -0.269547243f, 0.6467878011f, 0, 0.1644037271f, 0.5105846203f, -0.8439637196f, 0, 0.6494635788f, 0.05585611296f, 0.7583384168f, 0, -0.4711970882f, 0.5017280509f, -0.7254255765f, 0, -0.6335764307f, -0.2381686273f, -0.7361091029f, 0, -0.9021533097f, -0.270947803f, -0.3357181763f, 0, -0.3793711033f, 0.872258117f, 0.3086152025f, 0, + -0.6855598966f, -0.3250143309f, 0.6514394162f, 0, 0.2900942212f, -0.7799057743f, -0.5546100667f, 0, -0.2098319339f, 0.85037073f, 0.4825351604f, 0, -0.4592603758f, 0.6598504336f, -0.5947077538f, 0, 0.8715945488f, 0.09616365406f, -0.4807031248f, 0, -0.6776666319f, 0.7118504878f, -0.1844907016f, 0, 0.7044377633f, 0.312427597f, 0.637304036f, 0, -0.7052318886f, -0.2401093292f, -0.6670798253f, 0, + 0.081921007f, -0.7207336136f, -0.6883545647f, 0, -0.6993680906f, -0.5875763221f, -0.4069869034f, 0, -0.1281454481f, 0.6419895885f, 0.7559286424f, 0, -0.6337388239f, -0.6785471501f, -0.3714146849f, 0, 0.5565051903f, -0.2168887573f, -0.8020356851f, 0, -0.5791554484f, 0.7244372011f, -0.3738578718f, 0, 0.1175779076f, -0.7096451073f, 0.6946792478f, 0, -0.6134619607f, 0.1323631078f, 0.7785527795f, 0, + 0.6984635305f, -0.02980516237f, -0.715024719f, 0, 0.8318082963f, -0.3930171956f, 0.3919597455f, 0, 0.1469576422f, 0.05541651717f, -0.9875892167f, 0, 0.708868575f, -0.2690503865f, 0.6520101478f, 0, 0.2726053183f, 0.67369766f, -0.68688995f, 0, -0.6591295371f, 0.3035458599f, -0.6880466294f, 0, 0.4815131379f, -0.7528270071f, 0.4487723203f, 0, 0.9430009463f, 0.1675647412f, -0.2875261255f, 0, + 0.434802957f, 0.7695304522f, -0.4677277752f, 0, 0.3931996188f, 0.594473625f, 0.7014236729f, 0, 0.7254336655f, -0.603925654f, 0.3301814672f, 0, 0.7590235227f, -0.6506083235f, 0.02433313207f, 0, -0.8552768592f, -0.3430042733f, 0.3883935666f, 0, -0.6139746835f, 0.6981725247f, 0.3682257648f, 0, -0.7465905486f, -0.5752009504f, 0.3342849376f, 0, 0.5730065677f, 0.810555537f, -0.1210916791f, 0, + -0.9225877367f, -0.3475211012f, -0.167514036f, 0, -0.7105816789f, -0.4719692027f, -0.5218416899f, 0, -0.08564609717f, 0.3583001386f, 0.929669703f, 0, -0.8279697606f, -0.2043157126f, 0.5222271202f, 0, 0.427944023f, 0.278165994f, 0.8599346446f, 0, 0.5399079671f, -0.7857120652f, -0.3019204161f, 0, 0.5678404253f, -0.5495413974f, -0.6128307303f, 0, -0.9896071041f, 0.1365639107f, -0.04503418428f, 0, + -0.6154342638f, -0.6440875597f, 0.4543037336f, 0, 0.1074204368f, -0.7946340692f, 0.5975094525f, 0, -0.3595449969f, -0.8885529948f, 0.28495784f, 0, -0.2180405296f, 0.1529888965f, 0.9638738118f, 0, -0.7277432317f, -0.6164050508f, -0.3007234646f, 0, 0.7249729114f, -0.00669719484f, 0.6887448187f, 0, -0.5553659455f, -0.5336586252f, 0.6377908264f, 0, 0.5137558015f, 0.7976208196f, -0.3160000073f, 0, + -0.3794024848f, 0.9245608561f, -0.03522751494f, 0, 0.8229248658f, 0.2745365933f, -0.4974176556f, 0, -0.5404114394f, 0.6091141441f, 0.5804613989f, 0, 0.8036581901f, -0.2703029469f, 0.5301601931f, 0, 0.6044318879f, 0.6832968393f, 0.4095943388f, 0, 0.06389988817f, 0.9658208605f, -0.2512108074f, 0, 0.1087113286f, 0.7402471173f, -0.6634877936f, 0, -0.713427712f, -0.6926784018f, 0.1059128479f, 0, + 0.6458897819f, -0.5724548511f, -0.5050958653f, 0, -0.6553931414f, 0.7381471625f, 0.159995615f, 0, 0.3910961323f, 0.9188871375f, -0.05186755998f, 0, -0.4879022471f, -0.5904376907f, 0.6429111375f, 0, 0.6014790094f, 0.7707441366f, -0.2101820095f, 0, -0.5677173047f, 0.7511360995f, 0.3368851762f, 0, 0.7858573506f, 0.226674665f, 0.5753666838f, 0, -0.4520345543f, -0.604222686f, -0.6561857263f, 0, + 0.002272116345f, 0.4132844051f, -0.9105991643f, 0, -0.5815751419f, -0.5162925989f, 0.6286591339f, 0, -0.03703704785f, 0.8273785755f, 0.5604221175f, 0, -0.5119692504f, 0.7953543429f, -0.3244980058f, 0, -0.2682417366f, -0.9572290247f, -0.1084387619f, 0, -0.2322482736f, -0.9679131102f, -0.09594243324f, 0, 0.3554328906f, -0.8881505545f, 0.2913006227f, 0, 0.7346520519f, -0.4371373164f, 0.5188422971f, 0, + 0.9985120116f, 0.04659011161f, -0.02833944577f, 0, -0.3727687496f, -0.9082481361f, 0.1900757285f, 0, 0.91737377f, -0.3483642108f, 0.1925298489f, 0, 0.2714911074f, 0.4147529736f, -0.8684886582f, 0, 0.5131763485f, -0.7116334161f, 0.4798207128f, 0, -0.8737353606f, 0.18886992f, -0.4482350644f, 0, 0.8460043821f, -0.3725217914f, 0.3814499973f, 0, 0.8978727456f, -0.1780209141f, -0.4026575304f, 0, + 0.2178065647f, -0.9698322841f, -0.1094789531f, 0, -0.1518031304f, -0.7788918132f, -0.6085091231f, 0, -0.2600384876f, -0.4755398075f, -0.8403819825f, 0, 0.572313509f, -0.7474340931f, -0.3373418503f, 0, -0.7174141009f, 0.1699017182f, -0.6756111411f, 0, -0.684180784f, 0.02145707593f, -0.7289967412f, 0, -0.2007447902f, 0.06555605789f, -0.9774476623f, 0, -0.1148803697f, -0.8044887315f, 0.5827524187f, 0, + -0.7870349638f, 0.03447489231f, 0.6159443543f, 0, -0.2015596421f, 0.6859872284f, 0.6991389226f, 0, -0.08581082512f, -0.10920836f, -0.9903080513f, 0, 0.5532693395f, 0.7325250401f, -0.396610771f, 0, -0.1842489331f, -0.9777375055f, -0.1004076743f, 0, 0.0775473789f, -0.9111505856f, 0.4047110257f, 0, 0.1399838409f, 0.7601631212f, -0.6344734459f, 0, 0.4484419361f, -0.845289248f, 0.2904925424f, 0 +}; + +} // namespace FASTNOISELITESTATIC_H + +#endif diff --git a/include/Util/Helpers.h b/include/Util/Helpers.h new file mode 100644 index 0000000..48394f2 --- /dev/null +++ b/include/Util/Helpers.h @@ -0,0 +1,208 @@ +#pragma once + +#include "core/templates/vector.h" +#include "core/object/ref_counted.h" +#include "core/variant/variant.h" +#include "core/variant/typed_array.h" + +template +TypedArray VectorToTypedArray(const Vector>& vector) +{ + TypedArray arr; + arr.resize(vector.size()); + + for (int i{}; i < vector.size(); ++i) + { + arr[i] = Variant(vector[i]); + } + + return arr; +} + +template +Vector> TypedArrayToVector(const TypedArray& arr) +{ + Vector> vector; + vector.resize(arr.size()); + + for (int i{}; i < vector.size(); ++i) + { + vector.set(i, Ref(arr[i])); + } + + return vector; +} + +template +TypedArray VectorToTypedArrayVariant(const Vector& vector) +{ + TypedArray arr; + arr.resize(vector.size()); + + for (int i{}; i < vector.size(); ++i) + { + arr[i] = Variant(vector[i]); + } + + return arr; +} + +template +Vector TypedArrayToVectorVariant(const TypedArray& arr) +{ + Vector vector; + vector.resize(arr.size()); + + for (int i{}; i < vector.size(); ++i) + { + vector.set(i, arr[i]); + } + + return vector; +} + +template +TypedArray VectorToTypedArrayCast(const Vector& vector) +{ + TypedArray arr; + arr.resize(vector.size()); + + for (int i{}; i < vector.size(); ++i) + { + arr[i] = Variant(static_cast(vector[i])); + } + + return arr; +} + + + + // template + // static Type get_type_t(); + + // template <> static Type get_type_t() { return Type::BOOL; } + // template <> static Type get_type_t() { return Type::INT; } + // template <> static Type get_type_t() { return Type::FLOAT; } + // template <> static Type get_type_t() { return Type::STRING; } + + // template <> static Type get_type_t() { return Type::VECTOR2; } + // template <> static Type get_type_t() { return Type::VECTOR2I; } + // template <> static Type get_type_t() { return Type::RECT2; } + // template <> static Type get_type_t() { return Type::RECT2I; } + // template <> static Type get_type_t() { return Type::VECTOR3; } + // template <> static Type get_type_t() { return Type::VECTOR3I; } + // template <> static Type get_type_t() { return Type::TRANSFORM2D; } + // template <> static Type get_type_t() { return Type::VECTOR4; } + // template <> static Type get_type_t() { return Type::VECTOR4I; } + // template <> static Type get_type_t() { return Type::PLANE; } + // template <> static Type get_type_t() { return Type::QUATERNION; } + // template <> static Type get_type_t<::AABB>() { return Type::AABB; } + // template <> static Type get_type_t() { return Type::BASIS; } + // template <> static Type get_type_t() { return Type::TRANSFORM3D; } + // template <> static Type get_type_t() { return Type::PROJECTION; } + + // template <> static Type get_type_t() { return Type::COLOR; } + // template <> static Type get_type_t() { return Type::STRING_NAME; } + // template <> static Type get_type_t() { return Type::NODE_PATH; } + // template <> static Type get_type_t<::RID>() { return Type::RID; } + // template <> static Type get_type_t() { return Type::OBJECT; } + // template <> static Type get_type_t() { return Type::CALLABLE; } + // template <> static Type get_type_t() { return Type::SIGNAL; } + // template <> static Type get_type_t() { return Type::DICTIONARY; } + // template <> static Type get_type_t() { return Type::ARRAY; } + + // template <> static Type get_type_t() { return Type::PACKED_BYTE_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_INT32_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_INT64_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_FLOAT32_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_FLOAT64_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_STRING_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_VECTOR2_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_VECTOR3_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_COLOR_ARRAY; } + // template <> static Type get_type_t() { return Type::PACKED_VECTOR4_ARRAY; } + + // //template static Type get_type_t() { return Type::NIL; } + + // bool get_unsafe_bool() const { DEV_ASSERT(type == Type::BOOL); return _data._bool; } + // int get_unsafe_int() const { DEV_ASSERT(type == Type::INT); return _data._int; } + // float get_unsafe_float() const { DEV_ASSERT(type == Type::FLOAT); return _data._float; } + // String get_unsafe_string() const { DEV_ASSERT(type == Type::STRING); return *reinterpret_cast(_data._mem); } + + // Vector2 get_unsafe_vector2() const { DEV_ASSERT(type == Type::VECTOR2); return *reinterpret_cast(_data._mem); } + // Vector2i get_unsafe_vector2i() const { DEV_ASSERT(type == Type::VECTOR2I); return *reinterpret_cast(_data._mem); } + // Vector3 get_unsafe_vector3() const { DEV_ASSERT(type == Type::VECTOR3); return *reinterpret_cast(_data._mem); } + // Vector3i get_unsafe_vector3i() const { DEV_ASSERT(type == Type::VECTOR3I); return *reinterpret_cast(_data._mem); } + // Transform2D get_unsafe_transform2d() const { DEV_ASSERT(type == Type::TRANSFORM2D); return *reinterpret_cast(_data._mem); } + // Vector4 get_unsafe_vector4() const { DEV_ASSERT(type == Type::VECTOR4); return *reinterpret_cast(_data._mem); } + // Vector4i get_unsafe_vector4i() const { DEV_ASSERT(type == Type::VECTOR4I); return *reinterpret_cast(_data._mem); } + // Plane get_unsafe_plane() const { DEV_ASSERT(type == Type::PLANE); return *reinterpret_cast(_data._mem); } + // Quaternion get_unsafe_quaternion() const { DEV_ASSERT(type == Type::QUATERNION); return *reinterpret_cast(_data._mem); } + // ::AABB get_unsafe_aabb() const { DEV_ASSERT(type == Type::AABB); return *reinterpret_cast(_data._mem); } + // Basis get_unsafe_basis() const { DEV_ASSERT(type == Type::BASIS); return *reinterpret_cast(_data._mem); } + // Transform3D get_unsafe_transform3d() const { DEV_ASSERT(type == Type::TRANSFORM3D); return *reinterpret_cast(_data._mem); } + // Projection get_unsafe_projection() const { DEV_ASSERT(type == Type::PROJECTION); return *reinterpret_cast(_data._mem); } + + // Color get_unsafe_color() const { DEV_ASSERT(type == Type::COLOR); return *reinterpret_cast(_data._mem); } + // StringName get_unsafe_string_name() const { DEV_ASSERT(type == Type::STRING_NAME); return *reinterpret_cast(_data._mem); } + // NodePath get_unsafe_node_path() const { DEV_ASSERT(type == Type::NODE_PATH); return *reinterpret_cast(_data._mem); } + // ::RID get_unsafe_rid() const { DEV_ASSERT(type == Type::RID); return *reinterpret_cast(_data._mem); } + // Object* get_unsafe_object() const { DEV_ASSERT(type == Type::OBJECT); return reinterpret_cast(&_data._mem[0])->obj; } + // Callable get_unsafe_callable() const { DEV_ASSERT(type == Type::CALLABLE); return *reinterpret_cast(_data._mem); } + // Signal get_unsafe_signal() const { DEV_ASSERT(type == Type::SIGNAL); return *reinterpret_cast(_data._mem); } + // Dictionary get_unsafe_dictionary() const { DEV_ASSERT(type == Type::DICTIONARY); return *reinterpret_cast(_data._mem); } + // Array get_unsafe_array() const { DEV_ASSERT(type == Type::ARRAY); return *reinterpret_cast(_data._mem); } + + // PackedByteArray get_unsafe_packed_byte_array() const { DEV_ASSERT(type == Type::PACKED_BYTE_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedInt32Array get_unsafe_packed_int32_array() const { DEV_ASSERT(type == Type::PACKED_INT32_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedInt64Array get_unsafe_packed_int64_array() const { DEV_ASSERT(type == Type::PACKED_INT64_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedFloat32Array get_unsafe_packed_float32_array() const { DEV_ASSERT(type == Type::PACKED_FLOAT32_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedFloat64Array get_unsafe_packed_float64_array() const { DEV_ASSERT(type == Type::PACKED_FLOAT64_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedStringArray get_unsafe_packed_string_array() const { DEV_ASSERT(type == Type::PACKED_STRING_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedVector2Array get_unsafe_packed_vector2_array() const { DEV_ASSERT(type == Type::PACKED_VECTOR2_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedVector3Array get_unsafe_packed_vector3_array() const { DEV_ASSERT(type == Type::PACKED_VECTOR3_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedColorArray get_unsafe_packed_color_array() const { DEV_ASSERT(type == Type::PACKED_COLOR_ARRAY); return static_cast *>(_data.packed_array)->array; } + // PackedVector4Array get_unsafe_packed_vector4_array() const { DEV_ASSERT(type == Type::PACKED_VECTOR4_ARRAY); return static_cast *>(_data.packed_array)->array; } + + // template + // T get_unsafe_t() const; + + // template <> bool get_unsafe_t() const { return get_unsafe_bool(); } + // template <> int get_unsafe_t() const { return get_unsafe_int(); } + // template <> float get_unsafe_t() const { return get_unsafe_float(); } + // template <> String get_unsafe_t() const { return get_unsafe_string(); } + + // template <> Vector2 get_unsafe_t() const { return get_unsafe_vector2(); } + // template <> Vector2i get_unsafe_t() const { return get_unsafe_vector2i(); } + // template <> Vector3 get_unsafe_t() const { return get_unsafe_vector3(); } + // template <> Vector3i get_unsafe_t() const { return get_unsafe_vector3i(); } + // template <> Transform2D get_unsafe_t() const { return get_unsafe_transform2d(); } + // template <> Vector4 get_unsafe_t() const { return get_unsafe_vector4(); } + // template <> Vector4i get_unsafe_t() const { return get_unsafe_vector4i(); } + // template <> Plane get_unsafe_t() const { return get_unsafe_plane(); } + // template <> Quaternion get_unsafe_t() const { return get_unsafe_quaternion(); } + // template <> ::AABB get_unsafe_t<::AABB>() const { return get_unsafe_aabb(); } + // template <> Basis get_unsafe_t() const { return get_unsafe_basis(); } + // template <> Transform3D get_unsafe_t() const { return get_unsafe_transform3d(); } + // template <> Projection get_unsafe_t() const { return get_unsafe_projection(); } + + // template <> Color get_unsafe_t() const { return get_unsafe_color(); } + // template <> StringName get_unsafe_t() const { return get_unsafe_string_name(); } + // template <> NodePath get_unsafe_t() const { return get_unsafe_node_path(); } + // template <> ::RID get_unsafe_t<::RID>() const { return get_unsafe_rid(); } + // template <> Object* get_unsafe_t() const { return get_unsafe_object(); } + // template <> Callable get_unsafe_t() const { return get_unsafe_callable(); } + // template <> Signal get_unsafe_t() const { return get_unsafe_signal(); } + // template <> Dictionary get_unsafe_t() const { return get_unsafe_dictionary(); } + // template <> Array get_unsafe_t() const { return get_unsafe_array(); } + + // template <> PackedByteArray get_unsafe_t() const { return get_unsafe_packed_byte_array(); } + // template <> PackedInt32Array get_unsafe_t() const { return get_unsafe_packed_int32_array(); } + // template <> PackedInt64Array get_unsafe_t() const { return get_unsafe_packed_int64_array(); } + // template <> PackedFloat32Array get_unsafe_t() const { return get_unsafe_packed_float32_array(); } + // template <> PackedFloat64Array get_unsafe_t() const { return get_unsafe_packed_float64_array(); } + // template <> PackedStringArray get_unsafe_t() const { return get_unsafe_packed_string_array(); } + // template <> PackedVector2Array get_unsafe_t() const { return get_unsafe_packed_vector2_array(); } + // template <> PackedVector3Array get_unsafe_t() const { return get_unsafe_packed_vector3_array(); } + // template <> PackedColorArray get_unsafe_t() const { return get_unsafe_packed_color_array(); } + // template <> PackedVector4Array get_unsafe_t() const { return get_unsafe_packed_vector4_array(); } \ No newline at end of file diff --git a/include/Util/InplaceVector.h b/include/Util/InplaceVector.h new file mode 100644 index 0000000..173107e --- /dev/null +++ b/include/Util/InplaceVector.h @@ -0,0 +1,732 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ +// Original: https://github.com/TedLyngmo/inplace_vector + +// NOLINTNEXTLINE(llvm-header-guard) +#ifndef LYNIPV_F4BA9AA8_99CD_11EF_8916_90B11C0C0FF8 +#define LYNIPV_F4BA9AA8_99CD_11EF_8916_90B11C0C0FF8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __cplusplus >= 202002L +# include +#endif +#include +#include +#include + +#if __cplusplus >= 201402L +# define LYNIPV_CXX14_CONSTEXPR constexpr +#else +# define LYNIPV_CXX14_CONSTEXPR +#endif + +#if __cplusplus >= 202002L +# define LYNIPV_CXX20_CONSTEXPR constexpr +# define LYNIPV_CONSTRUCT_AT(p, ...) std::construct_at(p __VA_OPT__(, ) __VA_ARGS__) +#else +# define LYNIPV_CXX20_CONSTEXPR +# define LYNIPV_CONSTRUCT_AT(p, ...) ::new(static_cast(p)) T(__VA_ARGS__) +#endif + +namespace lyn { + + template + class inplace_vector; + namespace lyn_inplace_vector_detail { +#if __cplusplus >= 201703L + using std::is_nothrow_swappable; +#else + template + struct is_nothrow_swappable : std::integral_constant(), std::declval()))> {}; +#endif +#if __cplusplus >= 202002L + template + concept container_compatiblel_range = std::ranges::input_range && std::convertible_to, T>; +#endif + template + struct aligned_storage_non_trivial { + constexpr aligned_storage_non_trivial() noexcept {} + + using value_type = typename std::remove_const::type; + using size_type = std::size_t; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = value_type*; + using const_pointer = value_type const*; + + // destructor + LYNIPV_CXX20_CONSTEXPR ~aligned_storage_non_trivial() noexcept { static_cast*>(this)->clear(); } + + LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type idx) noexcept { return std::addressof(m_data[idx].data); } + LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type idx) const noexcept { return std::addressof(m_data[idx].data); } + LYNIPV_CXX14_CONSTEXPR reference ref(size_type idx) noexcept { return m_data[idx].data; } + LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type idx) const noexcept { return m_data[idx].data; } + + template + LYNIPV_CXX20_CONSTEXPR reference construct(size_type idx, Args&&... args) { + return *LYNIPV_CONSTRUCT_AT(ptr(idx), std::forward(args)...); + } + LYNIPV_CXX14_CONSTEXPR void destroy(size_type idx) noexcept { ref(idx).~T(); } + + LYNIPV_CXX14_CONSTEXPR reference operator[](size_type idx) noexcept { return ref(idx); } + constexpr const_reference operator[](size_type idx) const noexcept { return ref(idx); } + + constexpr size_type size() const noexcept { return m_size; } + + LYNIPV_CXX14_CONSTEXPR size_type inc() noexcept { return ++m_size; } + LYNIPV_CXX14_CONSTEXPR size_type dec(size_type count = 1) noexcept { return m_size -= count; } + + private: + union raw { + LYNIPV_CXX20_CONSTEXPR ~raw() {} + char dummy{}; + value_type data; + }; + std::array m_data; + static_assert(sizeof m_data == sizeof(T[N]), "erroneous size"); + size_type m_size = 0; + }; + + template + struct aligned_storage_trivial { + static_assert(std::is_trivially_destructible::value, "T must be trivially destructible"); + constexpr aligned_storage_trivial() noexcept {} + + using value_type = typename std::remove_const::type; + using size_type = std::size_t; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = value_type*; + using const_pointer = value_type const*; + + LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type idx) noexcept { return std::addressof(m_data[idx].data); } + LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type idx) const noexcept { return std::addressof(m_data[idx].data); } + LYNIPV_CXX14_CONSTEXPR reference ref(size_type idx) noexcept { return m_data[idx].data; } + LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type idx) const noexcept { return m_data[idx].data; } + + template + LYNIPV_CXX20_CONSTEXPR reference construct(size_type idx, Args&&... args) { + return *LYNIPV_CONSTRUCT_AT(ptr(idx), std::forward(args)...); + } + LYNIPV_CXX14_CONSTEXPR void destroy(size_type idx) noexcept { ref(idx).~T(); } + + LYNIPV_CXX14_CONSTEXPR reference operator[](size_type idx) noexcept { return ref(idx); } + constexpr const_reference operator[](size_type idx) const noexcept { return ref(idx); } + + constexpr size_type size() const noexcept { return m_size; } + + LYNIPV_CXX14_CONSTEXPR size_type inc() noexcept { return ++m_size; } + LYNIPV_CXX14_CONSTEXPR size_type dec(size_type count = 1) noexcept { return m_size -= count; } + + private: + union raw { + constexpr raw() : dummy{} {} + char dummy; + value_type data; + }; + std::array m_data; + static_assert(sizeof m_data == sizeof(T[N]), "erroneous size"); + size_type m_size = 0; + }; + + template + struct aligned_storage_empty { // specialization for 0 elements + using value_type = typename std::remove_const::type; + using size_type = std::size_t; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = value_type*; + using const_pointer = value_type const*; + + LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type) { return nullptr; } + LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type) const { return nullptr; } + LYNIPV_CXX14_CONSTEXPR reference ref(size_type) { return *ptr(0); } + LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type) const { return *ptr(0); } + + template + LYNIPV_CXX20_CONSTEXPR reference construct(size_type, Args&&...) { + return *ptr(0); + } + LYNIPV_CXX14_CONSTEXPR void destroy(size_type) {} + + LYNIPV_CXX14_CONSTEXPR reference operator[](size_type) { return *ptr(0); } + constexpr const_reference operator[](size_type) const { return *ptr(0); } + + constexpr size_type size() const noexcept { return 0; } + + LYNIPV_CXX14_CONSTEXPR size_type inc() { return 0; } + LYNIPV_CXX14_CONSTEXPR size_type dec(size_type = 1) { return 0; } + }; + + template + struct base_selector { + using type = + typename std::conditional, + typename std::conditional::value, aligned_storage_trivial, + aligned_storage_non_trivial>::type>::type; + }; + } // namespace lyn_inplace_vector_detail + + template + class inplace_vector : public lyn_inplace_vector_detail::base_selector::type { + static_assert(std::is_nothrow_destructible::value, + "inplace_vector: classes with potentially throwing destructors are prohibited"); + using base = typename lyn_inplace_vector_detail::base_selector::type; + using base::construct; + using base::destroy; + using base::ptr; + using base::ref; + + public: + using base::size; + using base::operator[]; + + using value_type = T; + using size_type = std::size_t; + using reference = T&; + using const_reference = T const&; + using pointer = T*; + using const_pointer = T const*; + using iterator = T*; + using const_iterator = T const*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = typename std::iterator_traits::difference_type; + + private: + LYNIPV_CXX14_CONSTEXPR void shrink_to(const size_type count) noexcept { + while (count != size()) { + pop_back(); + } + } + + public: + // constructors + constexpr inplace_vector() noexcept = default; + + template::value, typename std::enable_if::type = 0> + LYNIPV_CXX14_CONSTEXPR explicit inplace_vector(size_type count) { + if (count > N) throw std::bad_alloc(); + while (count != size()) unchecked_emplace_back(); + } + + template::value, typename std::enable_if::type = 0> + LYNIPV_CXX14_CONSTEXPR inplace_vector(size_type count, const T& value) { + if (count > N) throw std::bad_alloc(); + while (count != size()) unchecked_push_back(value); + } + + template::value_type>::value, int>::type = 0> + LYNIPV_CXX14_CONSTEXPR inplace_vector(InputIt first, InputIt last) { + std::copy(first, last, std::back_inserter(*this)); + } + + LYNIPV_CXX14_CONSTEXPR inplace_vector(const inplace_vector& other) = default; // for trivial types + + template::value && + not std::is_trivially_copy_constructible::type>::value, + int>::type = 0> + LYNIPV_CXX14_CONSTEXPR inplace_vector(const inplace_vector& other) { + for (size_type idx = 0; idx != other.size(); ++idx) { + unchecked_push_back(other[idx]); + } + } + + LYNIPV_CXX14_CONSTEXPR inplace_vector(inplace_vector&& other) noexcept = default; // for trivial types + + template::value && + not std::is_trivially_move_constructible::type>::value, + int>::type = 0> + LYNIPV_CXX14_CONSTEXPR inplace_vector(inplace_vector&& other) noexcept(N == 0 || std::is_nothrow_move_constructible::value) { + for (size_type idx = 0; idx != other.size(); ++idx) { + unchecked_push_back(std::move(other[idx])); + } + other.clear(); + } + + template::value, typename std::enable_if::type = 0> + constexpr inplace_vector(std::initializer_list init) : inplace_vector(init.begin(), init.end()) {} + +#if __cplusplus >= 202302L && defined(__cpp_lib_containers_ranges) + template R> + constexpr inplace_vector(std::from_range_t, R&& rg) { + if constexpr (std::ranges::sized_range) { + if (std::ranges::size(rg) > N) throw std::bad_alloc(); + for (auto&& val : rg) unchecked_emplace_back(std::forward(val)); + } + else { + for (auto&& val : rg) emplace_back(std::forward(val)); + } + } +#endif + + // assignment + LYNIPV_CXX14_CONSTEXPR inplace_vector& operator=(const inplace_vector& other) = default; // for trivial types + + template + LYNIPV_CXX14_CONSTEXPR auto operator=(const inplace_vector& other) -> + typename std::enable_if::value&& std::is_trivially_copy_constructible::value&& + std::is_trivially_copy_assignable::value), + inplace_vector&>::type { + assign(other.begin(), other.end()); + return *this; + } + + LYNIPV_CXX14_CONSTEXPR inplace_vector& operator=(inplace_vector&& other) noexcept( + N == 0 || (std::is_nothrow_move_assignable::value)) = default; // for trivial types + + template + LYNIPV_CXX14_CONSTEXPR auto operator=(inplace_vector&& other) noexcept( + N == 0 || (std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value && + not std::is_trivially_copyable::type>::value)) -> + typename std::enable_if::value&& std::is_trivially_move_constructible::value&& + std::is_trivially_move_assignable::value), + inplace_vector&>::type { + clear(); + std::move(other.begin(), other.end(), std::back_inserter(*this)); + other.clear(); + return *this; + } + + template + LYNIPV_CXX14_CONSTEXPR auto operator=(std::initializer_list init) -> + typename std::enable_if::value, inplace_vector&>::type { + if (init.size() > capacity()) throw std::bad_alloc(); + assign(init.begin(), init.end()); + return *this; + } + + template + LYNIPV_CXX14_CONSTEXPR auto assign(size_type count, const T& value) -> + typename std::enable_if::value>::type { + if (count > capacity()) throw std::bad_alloc(); + clear(); + while (count != size()) push_back(value); + } + + template + LYNIPV_CXX14_CONSTEXPR auto assign(InputIt first, InputIt last) -> + typename std::enable_if::value_type>::value>::type { + clear(); + std::copy(first, last, std::back_inserter(*this)); + } + + template + LYNIPV_CXX14_CONSTEXPR auto assign(std::initializer_list ilist) -> + typename std::enable_if::value>::type { + if (ilist.size() > capacity()) throw std::bad_alloc(); + clear(); + std::copy(ilist.begin(), ilist.end(), std::back_inserter(*this)); + } + +#if __cplusplus >= 202002L + template R> + constexpr void assign_range(R&& rg) + requires std::constructible_from> + { + clear(); + append_range(std::forward(rg)); + } + + template R> + constexpr void append_range(R&& rg) + requires std::constructible_from> + { + if constexpr (std::ranges::sized_range) { + if (size() + std::ranges::size(rg) > capacity()) throw std::bad_alloc(); + for (auto&& val : rg) { + unchecked_emplace_back(std::forward(val)); + } + } + else { + for (auto&& val : rg) { + emplace_back(std::forward(val)); + } + } + } + + template R> + constexpr std::ranges::borrowed_iterator_t try_append_range(R&& rg) + requires std::constructible_from> + { + auto it = std::ranges::begin(rg); + for (auto end = std::ranges::end(rg); it != end; std::ranges::advance(it, 1)) { + if (size() == capacity()) break; + unchecked_emplace_back(*it); + } + return it; + } +#endif + + // element access + LYNIPV_CXX14_CONSTEXPR reference at(size_type idx) { + if (idx >= size()) throw std::out_of_range(""); + return ref(idx); + } + LYNIPV_CXX14_CONSTEXPR const_reference at(size_type idx) const { + if (idx >= size()) throw std::out_of_range(""); + return ref(idx); + } + LYNIPV_CXX14_CONSTEXPR reference front() noexcept { return ref(0); } + constexpr const_reference front() const noexcept { return ref(0); } + LYNIPV_CXX14_CONSTEXPR reference back() noexcept { return ref(size() - 1); } + constexpr const_reference back() const noexcept { return ref(size() - 1); } + + LYNIPV_CXX14_CONSTEXPR pointer data() noexcept { return ptr(0); } + LYNIPV_CXX14_CONSTEXPR const_pointer data() const noexcept { return ptr(0); } + + // iterators + constexpr const_iterator cbegin() const noexcept { return data(); } + constexpr const_iterator cend() const noexcept { return std::next(cbegin(), static_cast(size())); } + constexpr const_iterator begin() const noexcept { return cbegin(); } + constexpr const_iterator end() const noexcept { return cend(); } + LYNIPV_CXX14_CONSTEXPR iterator begin() noexcept { return data(); } + LYNIPV_CXX14_CONSTEXPR iterator end() noexcept { return std::next(begin(), static_cast(size())); } + + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } + constexpr const_reverse_iterator rbegin() const noexcept { return crbegin(); } + constexpr const_reverse_iterator rend() const noexcept { return crend(); } + LYNIPV_CXX14_CONSTEXPR reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + LYNIPV_CXX14_CONSTEXPR reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + + // size and capacity + constexpr bool empty() const noexcept { return size() == 0; } + static constexpr size_type max_size() noexcept { return N; } + static constexpr size_type capacity() noexcept { return N; } + + private: + template + LYNIPV_CXX14_CONSTEXPR auto unchecked_resize(size_type count) -> + typename std::enable_if::value>::type { + if (count < size()) { + shrink_to(count); + } + else { + while (count != size()) { + unchecked_emplace_back(); + } + } + } + + template + LYNIPV_CXX14_CONSTEXPR auto unchecked_resize(size_type count, const value_type& value) -> + typename std::enable_if::value>::type { + if (count < size()) { + shrink_to(count); + } + else { + while (count != size()) { + unchecked_push_back(value); + } + } + } + + public: + template + LYNIPV_CXX14_CONSTEXPR auto resize(size_type count) -> typename std::enable_if::value>::type { + if (count > capacity()) throw std::bad_alloc(); + unchecked_resize(count); + } + + template + LYNIPV_CXX14_CONSTEXPR auto resize(size_type count, const value_type& value) -> + typename std::enable_if::value>::type { + if (count > capacity()) throw std::bad_alloc(); + unchecked_resize(count, value); + } + + static LYNIPV_CXX14_CONSTEXPR void reserve(size_type new_cap) { + if (new_cap > capacity()) throw std::bad_alloc(); + } + static LYNIPV_CXX14_CONSTEXPR void shrink_to_fit() noexcept {} + + // modifiers + private: + /* + // optimization idea for all insert() functions to get away from constructing and rotating: + LYNIPV_CXX14_CONSTEXPR size_type make_room_at(const_iterator pos, size_type count) { + // - move construct some T's at current end(). + // - move assign some T's before current end(). + // - destroy the old host for those "moved from" but not "moved to". + // + // This should leave a nice gap to construct the new range in without the need for move assigning via rotate afterwards. + // + // I don't know what to do about exception guarantees with that implementation though so I'll leave it to something to think + // about. Perhaps it can be used for T's with a non-throwing move assignment operator and move constructor. + // It will at least be ok for trivial types. + } + */ + + public: + template + LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, const T& value) -> + typename std::enable_if::value, iterator>::type { + // static_assert(std::is_nothrow_move_assignable::value, "only nothrow move assignable types may be used for now"); + if (size() == capacity()) throw std::bad_alloc(); + const auto ncpos = const_cast(pos); + unchecked_push_back(value); + std::rotate(ncpos, std::prev(end()), end()); + return ncpos; + } + + template + LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, T&& value) -> + typename std::enable_if::value, iterator>::type { + // static_assert(std::is_nothrow_move_assignable::value, "only nothrow move assignable types may be used for now"); + if (size() == capacity()) throw std::bad_alloc(); + const auto ncpos = const_cast(pos); + unchecked_push_back(std::move(value)); + std::rotate(ncpos, std::prev(end()), end()); + return ncpos; + } + + template + LYNIPV_CXX20_CONSTEXPR auto insert(const_iterator pos, size_type count, const T& value) -> + typename std::enable_if::value, iterator>::type { + // static_assert(std::is_nothrow_move_assignable::value, "only nothrow move assignable types may be used for now"); + if (size() + count > capacity()) throw std::bad_alloc(); + const auto ncpos = const_cast(pos); + auto oldsize = size(); + auto first_inserted = end(); + try { + while (count--) { + unchecked_push_back(value); + } + } + catch (...) { + shrink_to(oldsize); + throw; + } + std::rotate(ncpos, first_inserted, end()); + return ncpos; + } + template + LYNIPV_CXX20_CONSTEXPR auto insert(const_iterator pos, InputIt first, InputIt last) -> + typename std::enable_if::value_type>::value && + !std::is_const::value, + iterator>::type { + // static_assert(std::is_nothrow_move_assignable::value, "only nothrow move assignable types may be used for now"); + const auto ncpos = const_cast(pos); + auto oldsize = size(); + auto first_inserted = end(); + try { + for (; first != last; std::advance(first, 1)) { + push_back(*first); + } + } + catch (...) { + shrink_to(oldsize); + throw; + } + std::rotate(ncpos, first_inserted, end()); + return ncpos; + } + template + LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, std::initializer_list ilist) -> + typename std::enable_if::value && !std::is_const::value, iterator>::type { + return insert(pos, ilist.begin(), ilist.end()); + } + + template + LYNIPV_CXX14_CONSTEXPR auto emplace(const_iterator pos, Args&&... args) -> + typename std::enable_if::value, iterator>::type { + // static_assert(std::is_nothrow_move_assignable::value, "only nothrow move assignable types may be used for now"); + const auto ncpos = const_cast(pos); + emplace_back(std::forward(args)...); + std::rotate(ncpos, std::prev(end()), end()); + return ncpos; + } + + template + LYNIPV_CXX14_CONSTEXPR auto unchecked_emplace_back(Args&&... args) -> + typename std::enable_if::value, reference>::type { + auto& rv = construct(size(), std::forward(args)...); + this->inc(); + return rv; + } + + template + LYNIPV_CXX14_CONSTEXPR auto unchecked_push_back(T const& value) -> + typename std::enable_if::value, reference>::type { + auto& rv = construct(size(), value); + this->inc(); + return rv; + } + + template + LYNIPV_CXX14_CONSTEXPR auto unchecked_push_back(T&& value) -> + typename std::enable_if::value, reference>::type { + auto& rv = construct(size(), std::move(value)); + this->inc(); + return rv; + } + + template + LYNIPV_CXX14_CONSTEXPR auto emplace_back(Args&&... args) -> + typename std::enable_if::value, reference>::type { + if (size() == N) throw std::bad_alloc(); + return unchecked_emplace_back(std::forward(args)...); + } + + template + LYNIPV_CXX14_CONSTEXPR auto try_emplace_back(Args&&... args) -> + typename std::enable_if::value, pointer>::type { + if (size() == N) return nullptr; + return std::addressof(unchecked_emplace_back(std::forward(args)...)); + } + + template + LYNIPV_CXX14_CONSTEXPR auto push_back(T const& value) -> + typename std::enable_if::value, reference>::type { + if (size() == N) throw std::bad_alloc(); + return unchecked_push_back(value); + } + + template + LYNIPV_CXX14_CONSTEXPR auto push_back(T&& value) -> + typename std::enable_if::value, reference>::type { + if (size() == N) throw std::bad_alloc(); + return unchecked_push_back(std::move(value)); + } + + template + LYNIPV_CXX14_CONSTEXPR auto try_push_back(T const& value) -> + typename std::enable_if::value, pointer>::type { + if (size() == N) return nullptr; + return std::addressof(unchecked_push_back(value)); + } + + template + LYNIPV_CXX14_CONSTEXPR auto try_push_back(T&& value) -> + typename std::enable_if::value, pointer>::type { + if (size() == N) return nullptr; + return std::addressof(unchecked_push_back(std::move(value))); + } + + LYNIPV_CXX14_CONSTEXPR void pop_back() noexcept { destroy(this->dec()); } + LYNIPV_CXX14_CONSTEXPR void clear() noexcept { shrink_to(0); } + + template + LYNIPV_CXX14_CONSTEXPR auto erase(const_iterator first, const_iterator last) -> + typename std::enable_if::value, iterator>::type { + auto ncfirst = const_cast(first); + auto nclast = const_cast(last); + auto removed = static_cast(std::distance(ncfirst, nclast)); + std::move(nclast, end(), ncfirst); + for (size_type idx = size() - removed; idx < size(); ++idx) { + destroy(idx); + } + this->dec(removed); + return ncfirst; + } + + template + LYNIPV_CXX14_CONSTEXPR auto erase(const_iterator pos) -> typename std::enable_if::value, iterator>::type { + return erase(pos, std::next(pos)); + } + + template + LYNIPV_CXX14_CONSTEXPR auto swap(inplace_vector& other) noexcept(N == 0 || + (lyn_inplace_vector_detail::is_nothrow_swappable::value && + std::is_nothrow_move_constructible::value)) -> + typename std::enable_if::value>::type { + auto&& p = (size() < other.size()) ? std::pair(*this, other) + : std::pair(other, *this); + auto& small = p.first; + auto& large = p.second; + size_type idx = 0, small_size = small.size(); + for (; idx < small_size; ++idx) { + using std::swap; + swap(small[idx], large[idx]); + } + for (; idx < large.size(); ++idx) { + small.push_back(std::move(large[idx])); + } + large.shrink_to(small_size); + } + + LYNIPV_CXX14_CONSTEXPR void friend swap(inplace_vector& lhs, inplace_vector& rhs) noexcept( + N == 0 || (lyn_inplace_vector_detail::is_nothrow_swappable::value && std::is_nothrow_move_constructible::value)) { + lhs.swap(rhs); + } + +#if __cplusplus >= 202002L + constexpr friend auto operator<=>(const inplace_vector& lhs, const inplace_vector& rhs) { + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } +#else + friend bool operator<(const inplace_vector& lhs, const inplace_vector& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + friend bool operator>(const inplace_vector& lhs, const inplace_vector& rhs) { return rhs < lhs; } + friend bool operator<=(const inplace_vector& lhs, const inplace_vector& rhs) { return !(rhs < lhs); } + friend bool operator>=(const inplace_vector& lhs, const inplace_vector& rhs) { return rhs <= lhs; } + friend bool operator!=(const inplace_vector& lhs, const inplace_vector& rhs) { return !(lhs == rhs); } +#endif + friend bool operator==(const inplace_vector& lhs, const inplace_vector& rhs) { + if (lhs.size() != rhs.size()) return false; + return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin()); + } + }; + + template + LYNIPV_CXX14_CONSTEXPR typename inplace_vector::size_type erase(inplace_vector& c, const U& value) { + auto it = std::remove(c.begin(), c.end(), value); + auto r = static_cast::size_type>(std::distance(it, c.end())); + c.erase(it, it.end()); + return r; + } + + template + LYNIPV_CXX14_CONSTEXPR typename inplace_vector::size_type erase_if(inplace_vector& c, Predicate pred) { + auto it = std::remove_if(c.begin(), c.end(), pred); + auto r = static_cast::size_type>(std::distance(it, c.end())); + c.erase(it, c.end()); + return r; + } + +} // namespace lyn + +// clean up defines +#undef LYNIPV_CXX14_CONSTEXPR +#undef LYNIPV_CXX20_CONSTEXPR +#undef LYNIPV_CONSTRUCT_AT + +#endif \ No newline at end of file diff --git a/include/Util/RandomPicker.h b/include/Util/RandomPicker.h new file mode 100644 index 0000000..90cbf53 --- /dev/null +++ b/include/Util/RandomPicker.h @@ -0,0 +1,198 @@ +#pragma once + +#include +#include + +template +class RandomPickerT +{ +public: + struct Entry + { + Entry() = default; + Entry(const T& val, WeightType weight) : Val{ val }, Weight{ weight } {} + + T Val{}; + const WeightType Weight{}; + WeightType AccumulatedWeight{}; + WeightType GetWeightSum() { return AccumulatedWeight + Weight; } + }; + +public: + std::unique_ptr Entries{}; + WeightType TotalWeight{}; + uint32_t TotalEntries{}; + const uint32_t DataSize{}; + +public: + RandomPickerT() = default; + + template + RandomPickerT(const Container& container, WeightGetter getter) + : Entries{ std::make_unique(container.size()) } + , TotalEntries{ container.size() } + , DataSize{ container.size() } + { + int counter{}; + for (const auto& entry : container) + { + auto weight = getter(entry); + Entries[counter++] = Entry{entry, weight}; + } + + RecalculateWeights(); + } + + template + RandomPickerT(const EntriesT& entries) + : Entries{ std::make_unique(entries.size()) } + , TotalEntries{ entries.size() } + , DataSize{ entries.size() } + { + int counter{}; + for (const auto& entry : entries) + { + Entries[counter++] = entry; + } + + RecalculateWeights(); + } + +public: + T GetRandom(int& index, WeightType randomVal) const + { + WeightType weight = GetRandomWeight(randomVal); + index = GetIndex(weight); + + return Entries[index].Val; + } + + int GetIndex(WeightType weight) const + { + if (weight < 0 || weight > TotalWeight || TotalEntries == 0) + return -1; + if (TotalEntries == 1) + return 0; + return BinarySearchRecursive(weight, 0, TotalEntries - 1); + } + + void RemoveEntry(int index) + { + if (ValidateIndex(index)) RemoveEntryInternal(index); + } + + T GetAndRemoveRandom(int& index, WeightType randomVal) + { + WeightType weight = GetRandomWeight(randomVal); + int index = GetIndex(weight); + + auto returnVal = Entries[index].Val; + RemoveEntryInternal(index); + + return returnVal; + } + + T GetAndRemoveRandom(WeightType randomVal) + { + int index{}; + return GetAndRemoveRandom(index, randomVal); + } + + T Peek(int index) const + { + _ASSERT(ValidateIndex(index)); + return Entries[index]; + } + + void Reset() + { + TotalEntries = DataSize; + RecalculateWeights(); + } + + template + void SetObjectAtIndex(int index, const T& val, WeightGetter getter) + { + if (!ValidateIndex(index)) return; + + auto entry = Entries[index]; + + auto newWeight = getter(val); + bool recalculateWeights = newWeight != entry.Weight; + + Entries[index] = Entry{ val, newWeight }; + + if (recalculateWeights) RecalculateWeights(); + } + + void SetEntryAtIndex(int index, const Entry& entry) + { + if (!ValidateIndex(index)) return; + + bool recalculateWeights = entry.Weight != Entries[index].Weight; + Entries[index] = entry; + + if (recalculateWeights) RecalculateWeights(); + } + +private: + void RecalculateWeights() + { + WeightType accumulatedWeight = 0; + for (uint32_t i{}; i < TotalEntries; ++i) + { + Entries[i].AccumulatedWeight = accumulatedWeight; + accumulatedWeight += Entries[i].Weight; + } + TotalWeight = accumulatedWeight; + } + + WeightType GetRandomWeight(WeightType randomVal) const + { + if constexpr (std::is_integral_v) + { + return randomVal % TotalWeight; + } + if constexpr (std::is_floating_point_v) + { + return std::fmod(randomVal, TotalWeight); + } + } + + int BinarySearchRecursive(WeightType weight, int min, int max) const + { + int middle = (min + max) >> 1; + + auto entry = Entries[middle]; + if (weight >= entry.AccumulatedWeight && weight < entry.GetWeightSum()) + return middle; + + return (weight < entry.AccumulatedWeight) ? BinarySearchRecursive(weight, min, middle - 1) : BinarySearchRecursive(weight, middle + 1, max); + } + + void RemoveEntryInternal(int index) + { + if (!ValidateIndex(index)) + return; + + std::swap(Entries[index], Entries.back()); + --TotalEntries; + + if (TotalEntries != 0) RecalculateWeights(); + else TotalWeight = 0; + } + + bool ValidateIndex(int index) const + { + return index >= 0 && index < TotalEntries; + } +}; + +template +using RandomPicker32 = RandomPickerT; +template +using RandomPicker64 = RandomPickerT; +template +using RandomPickerF = RandomPickerT; +template +using RandomPickerD = RandomPickerT; \ No newline at end of file diff --git a/include/Util/ResourceAccess.h b/include/Util/ResourceAccess.h new file mode 100644 index 0000000..eefeb7e --- /dev/null +++ b/include/Util/ResourceAccess.h @@ -0,0 +1,41 @@ +#pragma once + +#include "core/os/mutex.h" +#include "core/object/ref_counted.h" + +template +class ResourceAccess final +{ +public: + ResourceAccess(T& resource, LockT& lock) + : Resource{ resource } + , Lock{ lock } + { + Lock.lock(); + } + ~ResourceAccess() + { + if (validLock) Lock.unlock(); + } + + ResourceAccess(const ResourceAccess& other) = delete; + ResourceAccess& operator=(const ResourceAccess& other) = delete; + ResourceAccess& operator=(ResourceAccess&& other) noexcept = delete; + + ResourceAccess(ResourceAccess&& other) noexcept + : Resource{ other.Resource } + , Lock{ other.Lock } + , validLock{ true } + { + other.validLock = false; + } + +public: + T& GetResource() { return Resource; } + T* operator->() { return &Resource; } + +private: + T& Resource; + LockT& Lock; + bool validLock{ true }; +}; \ No newline at end of file diff --git a/include/Util/SharedBuffer.h b/include/Util/SharedBuffer.h new file mode 100644 index 0000000..093ad2b --- /dev/null +++ b/include/Util/SharedBuffer.h @@ -0,0 +1,112 @@ +#pragma once + +#include "stdint.h" +#include "cassert" +#include "Util/Span.h" + +template +inline T const* ByteToData(uint8_t const* data) { return reinterpret_cast(data); } + +template +inline T* ByteToData(uint8_t* data) { return reinterpret_cast(data); } + +template +class SharedBuffer final +{ +private: + struct ControlBlock + { + ControlBlock(uint32_t size, const MetaData& data) : Size{ size }, Data { data } {} + + std::atomic Count{ 1 }; + uint32_t Size{}; + MetaData Data{}; + }; + + static constexpr size_t ControlBlockOffset = 0; + static constexpr size_t DataOffset = sizeof(ControlBlock); + +public: + SharedBuffer() = default; + SharedBuffer(int size, const MetaData& metaData) + { + Data = new uint8_t[sizeof(ControlBlock) + size * sizeof(T)]{}; + + new (GetControlBlock()) ControlBlock(size, metaData); + for (size_t i{}; i < size; ++i) + { + new (&Ptr()[i]) T{}; + } + } + + ~SharedBuffer() + { + Destruct(); + } + + SharedBuffer(const SharedBuffer& other) + : Data{ other.Data } + { + if (Data) ++GetControlBlock()->Count; + } + SharedBuffer(SharedBuffer&& other) noexcept + : Data{ other.Data } + { + other.Data = nullptr; + } + + SharedBuffer& operator=(const SharedBuffer& other) + { + if (this != &other) + { + Destruct(); + Data = other.Data; + if (Data) ++GetControlBlock()->Count; + } + return *this; + } + SharedBuffer& operator=(SharedBuffer&& other) noexcept + { + if (this != &other) + { + Destruct(); + Data = other.Data; + other.Data = nullptr; + } + return *this; + } + + +private: + ControlBlock const* GetControlBlock() const { assert(Data); return ByteToData(Data + ControlBlockOffset); } + ControlBlock* GetControlBlock() { assert(Data); return ByteToData(Data + ControlBlockOffset); } + + void Destruct() + { + if (Data != nullptr && --GetControlBlock()->Count == 0) + { + uint32_t size = GetSize(); + GetControlBlock()->~ControlBlock(); + for (size_t i = 0; i < size; ++i) + Ptr()[i].~T(); + delete[] Data; + } + } + +public: + T const* Ptr() const { assert(Data); return ByteToData(Data + DataOffset); } + T* Ptr() { assert(Data); return ByteToData(Data + DataOffset); } + tcb::span GetData() const { return tcb::span(Ptr(), GetSize()); } + tcb::span GetData() { return tcb::span(Ptr(), GetSize()); } + MetaData const* GetMetaData() const { assert(Data); return &GetControlBlock()->Data; } + MetaData* GetMetaData() { assert(Data); return &GetControlBlock()->Data; } + + T& operator[] (uint32_t index) { assert(index < GetControlBlock()->Size); return GetData()[index]; } + const T& operator[] (uint32_t index) const { assert(index < GetControlBlock()->Size); return GetData()[index]; } + uint32_t GetSize() const { return GetControlBlock()->Size; } + + operator bool() const { return Data; } + +private: + uint8_t* Data{}; +}; \ No newline at end of file diff --git a/include/Util/Span.h b/include/Util/Span.h new file mode 100644 index 0000000..83d9faf --- /dev/null +++ b/include/Util/Span.h @@ -0,0 +1,630 @@ + +/* +This is an implementation of C++20's std::span +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf +*/ + +// Copyright Tristan Brindle 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file ../../LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef TCB_SPAN_HPP_INCLUDED +#define TCB_SPAN_HPP_INCLUDED + +#include +#include +#include +#include + +#ifndef TCB_SPAN_NO_EXCEPTIONS +// Attempt to discover whether we're being compiled with exception support +#if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) +#define TCB_SPAN_NO_EXCEPTIONS +#endif +#endif + +#ifndef TCB_SPAN_NO_EXCEPTIONS +#include +#include +#endif + +// Various feature test macros + +#ifndef TCB_SPAN_NAMESPACE_NAME +#define TCB_SPAN_NAMESPACE_NAME tcb +#endif + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define TCB_SPAN_HAVE_CPP17 +#endif + +#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define TCB_SPAN_HAVE_CPP14 +#endif + +namespace TCB_SPAN_NAMESPACE_NAME { + + // Establish default contract checking behavior +#if !defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) && \ + !defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \ + !defined(TCB_SPAN_NO_CONTRACT_CHECKING) +#if defined(NDEBUG) || !defined(TCB_SPAN_HAVE_CPP14) +#define TCB_SPAN_NO_CONTRACT_CHECKING +#else +#define TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION +#endif +#endif + +#if defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) + struct contract_violation_error : std::logic_error { + explicit contract_violation_error(const char* msg) : std::logic_error(msg) + { + } + }; + + inline void contract_violation(const char* msg) + { + throw contract_violation_error(msg); + } + +#elif defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) + [[noreturn]] inline void contract_violation(const char* /*unused*/) + { + std::terminate(); + } +#endif + +#if !defined(TCB_SPAN_NO_CONTRACT_CHECKING) +#define TCB_SPAN_STRINGIFY(cond) #cond +#define TCB_SPAN_EXPECT(cond) \ + cond ? (void) 0 : contract_violation("Expected " TCB_SPAN_STRINGIFY(cond)) +#else +#define TCB_SPAN_EXPECT(cond) +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables) +#define TCB_SPAN_INLINE_VAR inline +#else +#define TCB_SPAN_INLINE_VAR +#endif + +#if defined(TCB_SPAN_HAVE_CPP14) || \ + (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) +#define TCB_SPAN_HAVE_CPP14_CONSTEXPR +#endif + +#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) +#define TCB_SPAN_CONSTEXPR14 constexpr +#else +#define TCB_SPAN_CONSTEXPR14 +#endif + +#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) && \ + (!defined(_MSC_VER) || _MSC_VER > 1900) +#define TCB_SPAN_CONSTEXPR_ASSIGN constexpr +#else +#define TCB_SPAN_CONSTEXPR_ASSIGN +#endif + +#if defined(TCB_SPAN_NO_CONTRACT_CHECKING) +#define TCB_SPAN_CONSTEXPR11 constexpr +#else +#define TCB_SPAN_CONSTEXPR11 TCB_SPAN_CONSTEXPR14 +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides) +#define TCB_SPAN_HAVE_DEDUCTION_GUIDES +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte) +#define TCB_SPAN_HAVE_STD_BYTE +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr) +#define TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC +#endif + +#if defined(TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC) +#define TCB_SPAN_ARRAY_CONSTEXPR constexpr +#else +#define TCB_SPAN_ARRAY_CONSTEXPR +#endif + +#ifdef TCB_SPAN_HAVE_STD_BYTE + using byte = std::byte; +#else + using byte = unsigned char; +#endif + +#if defined(TCB_SPAN_HAVE_CPP17) +#define TCB_SPAN_NODISCARD [[nodiscard]] +#else +#define TCB_SPAN_NODISCARD +#endif + + TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX; + + template + class span; + + namespace detail { + + template + struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept + : ptr(p_ptr) + { + } + + E* ptr = nullptr; + static constexpr std::size_t size = S; + }; + + template + struct span_storage { + constexpr span_storage() noexcept = default; + + constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept + : ptr(p_ptr), size(p_size) + { + } + + E* ptr = nullptr; + std::size_t size = 0; + }; + + // Reimplementation of C++17 std::size() and std::data() +#if defined(TCB_SPAN_HAVE_CPP17) || \ + defined(__cpp_lib_nonmember_container_access) + using std::data; + using std::size; +#else + template + constexpr auto size(const C& c) -> decltype(c.size()) + { + return c.size(); + } + + template + constexpr std::size_t size(const T(&)[N]) noexcept + { + return N; + } + + template + constexpr auto data(C& c) -> decltype(c.data()) + { + return c.data(); + } + + template + constexpr auto data(const C& c) -> decltype(c.data()) + { + return c.data(); + } + + template + constexpr T* data(T(&array)[N]) noexcept + { + return array; + } + + template + constexpr const E* data(std::initializer_list il) noexcept + { + return il.begin(); + } +#endif // TCB_SPAN_HAVE_CPP17 + +#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t) + using std::void_t; +#else + template + using void_t = void; +#endif + + template + using uncvref_t = + typename std::remove_cv::type>::type; + + template + struct is_span : std::false_type {}; + + template + struct is_span> : std::true_type {}; + + template + struct is_std_array : std::false_type {}; + + template + struct is_std_array> : std::true_type {}; + + template + struct has_size_and_data : std::false_type {}; + + template + struct has_size_and_data())), + decltype(detail::data(std::declval()))>> + : std::true_type {}; + + template > + struct is_container { + static constexpr bool value = + !is_span::value && !is_std_array::value && + !std::is_array::value && has_size_and_data::value; + }; + + template + using remove_pointer_t = typename std::remove_pointer::type; + + template + struct is_container_element_type_compatible : std::false_type {}; + + template + struct is_container_element_type_compatible< + T, E, + typename std::enable_if< + !std::is_same< + typename std::remove_cv()))>::type, + void>::value&& + std::is_convertible< + remove_pointer_t()))>(*)[], + E(*)[]>::value + >::type> + : std::true_type { + }; + + template + struct is_complete : std::false_type {}; + + template + struct is_complete : std::true_type {}; + + } // namespace detail + + template + class span { + static_assert(std::is_object::value, + "A span's ElementType must be an object type (not a " + "reference type or void)"); + static_assert(detail::is_complete::value, + "A span's ElementType must be a complete type (not a forward " + "declaration)"); + static_assert(!std::is_abstract::value, + "A span's ElementType cannot be an abstract class type"); + + using storage_type = detail::span_storage; + + public: + // constants and types + using element_type = ElementType; + using value_type = typename std::remove_cv::type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using const_reference = const element_type&; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator; + + static constexpr size_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + template < + std::size_t E = Extent, + typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0> + constexpr span() noexcept + { + } + + TCB_SPAN_CONSTEXPR11 span(pointer ptr, size_type count) + : storage_(ptr, count) + { + TCB_SPAN_EXPECT(extent == dynamic_extent || count == extent); + } + + TCB_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem) + : storage_(first_elem, last_elem - first_elem) + { + TCB_SPAN_EXPECT(extent == dynamic_extent || + last_elem - first_elem == + static_cast(extent)); + } + + template ::value, + int>::type = 0> + constexpr span(element_type(&arr)[N]) noexcept : storage_(arr, N) + { + } + + template &, ElementType>::value, + int>::type = 0> + TCB_SPAN_ARRAY_CONSTEXPR span(std::array& arr) noexcept + : storage_(arr.data(), N) + { + } + + template &, ElementType>::value, + int>::type = 0> + TCB_SPAN_ARRAY_CONSTEXPR span(const std::array& arr) noexcept + : storage_(arr.data(), N) + { + } + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value&& + detail::is_container_element_type_compatible< + Container&, ElementType>::value, + int>::type = 0> + constexpr span(Container & cont) + : storage_(detail::data(cont), detail::size(cont)) + { + } + + template < + typename Container, std::size_t E = Extent, + typename std::enable_if< + E == dynamic_extent && detail::is_container::value&& + detail::is_container_element_type_compatible< + const Container&, ElementType>::value, + int>::type = 0> + constexpr span(const Container & cont) + : storage_(detail::data(cont), detail::size(cont)) + { + } + + constexpr span(const span& other) noexcept = default; + + template ::value, + int>::type = 0> + constexpr span(const span& other) noexcept + : storage_(other.data(), other.size()) + { + } + + ~span() noexcept = default; + + TCB_SPAN_CONSTEXPR_ASSIGN span& + operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + TCB_SPAN_CONSTEXPR11 span first() const + { + TCB_SPAN_EXPECT(Count <= size()); + return { data(), Count }; + } + + template + TCB_SPAN_CONSTEXPR11 span last() const + { + TCB_SPAN_EXPECT(Count <= size()); + return { data() + (size() - Count), Count }; + } + + template + using subspan_return_t = + span; + + template + TCB_SPAN_CONSTEXPR11 subspan_return_t subspan() const + { + TCB_SPAN_EXPECT(Offset <= size() && + (Count == dynamic_extent || Offset + Count <= size())); + return { data() + Offset, + Count != dynamic_extent ? Count : size() - Offset }; + } + + TCB_SPAN_CONSTEXPR11 span + first(size_type count) const + { + TCB_SPAN_EXPECT(count <= size()); + return { data(), count }; + } + + TCB_SPAN_CONSTEXPR11 span + last(size_type count) const + { + TCB_SPAN_EXPECT(count <= size()); + return { data() + (size() - count), count }; + } + + TCB_SPAN_CONSTEXPR11 span + subspan(size_type offset, size_type count = dynamic_extent) const + { + TCB_SPAN_EXPECT(offset <= size() && + (count == dynamic_extent || offset + count <= size())); + return { data() + offset, + count == dynamic_extent ? size() - offset : count }; + } + + // [span.obs], span observers + constexpr size_type size() const noexcept { return storage_.size; } + + constexpr size_type size_bytes() const noexcept + { + return size() * sizeof(element_type); + } + + TCB_SPAN_NODISCARD constexpr bool empty() const noexcept + { + return size() == 0; + } + + // [span.elem], span element access + TCB_SPAN_CONSTEXPR11 reference operator[](size_type idx) const + { + TCB_SPAN_EXPECT(idx < size()); + return *(data() + idx); + } + + TCB_SPAN_CONSTEXPR11 reference front() const + { + TCB_SPAN_EXPECT(!empty()); + return *data(); + } + + TCB_SPAN_CONSTEXPR11 reference back() const + { + TCB_SPAN_EXPECT(!empty()); + return *(data() + (size() - 1)); + } + + constexpr pointer data() const noexcept { return storage_.ptr; } + + // [span.iterators], span iterator support + constexpr iterator begin() const noexcept { return data(); } + + constexpr iterator end() const noexcept { return data() + size(); } + + TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept + { + return reverse_iterator(end()); + } + + TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept + { + return reverse_iterator(begin()); + } + + private: + storage_type storage_{}; + }; + +#ifdef TCB_SPAN_HAVE_DEDUCTION_GUIDES + + /* Deduction Guides */ + template + span(T(&)[N]) -> span; + + template + span(std::array&) -> span; + + template + span(const std::array&) -> span; + + template + span(Container&) -> span()))>::type>; + + template + span(const Container&) -> span; + +#endif // TCB_HAVE_DEDUCTION_GUIDES + + template + constexpr span + make_span(span s) noexcept + { + return s; + } + + template + constexpr span make_span(T(&arr)[N]) noexcept + { + return { arr }; + } + + template + TCB_SPAN_ARRAY_CONSTEXPR span make_span(std::array& arr) noexcept + { + return { arr }; + } + + template + TCB_SPAN_ARRAY_CONSTEXPR span + make_span(const std::array& arr) noexcept + { + return { arr }; + } + + template + constexpr span()))>::type> + make_span(Container& cont) + { + return { cont }; + } + + template + constexpr span + make_span(const Container& cont) + { + return { cont }; + } + + template + span + as_bytes(span s) noexcept + { + return { reinterpret_cast(s.data()), s.size_bytes() }; + } + + template < + class ElementType, size_t Extent, + typename std::enable_if::value, int>::type = 0> + span + as_writable_bytes(span s) noexcept + { + return { reinterpret_cast(s.data()), s.size_bytes() }; + } + + template + constexpr auto get(span s) -> decltype(s[N]) + { + return s[N]; + } + +} // namespace TCB_SPAN_NAMESPACE_NAME + +namespace std { + + template + class tuple_size> + : public integral_constant { + }; + + template + class tuple_size>; // not defined + + template + class tuple_element> { + public: + static_assert(Extent != TCB_SPAN_NAMESPACE_NAME::dynamic_extent && + I < Extent, + ""); + using type = ElementType; + }; + +} // end namespace std + +#endif // TCB_SPAN_HPP_INCLUDED \ No newline at end of file diff --git a/include/Util/StackAllocator.h b/include/Util/StackAllocator.h new file mode 100644 index 0000000..af4b18e --- /dev/null +++ b/include/Util/StackAllocator.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include "core/error/error_macros.h" + +class StackAllocator { +public: + StackAllocator() noexcept = default; + StackAllocator(size_t size) noexcept + { + m_memory = new char[size]; + m_size = size; + m_offset = 0; + } + + ~StackAllocator() + { + delete[] m_memory; + } + + template + T* allocate(size_t size = 1) + { + return static_cast(allocate(size * sizeof(T))); + } + + void* allocate(size_t size = 1) + { + size = std::max(size, 8); + + if (m_offset + size > m_size) + { + throw std::bad_alloc(); + } + void* ptr = m_memory + m_offset; + m_offset += size; + return ptr; + } + + template + void deallocate(T* p, size_t size) noexcept + {} + + void reset() { + m_offset = 0; + } + +private: + char* m_memory; + size_t m_size; + size_t m_offset; +}; \ No newline at end of file diff --git a/include/Util/fsa.h b/include/Util/fsa.h new file mode 100644 index 0000000..3d4ec4d --- /dev/null +++ b/include/Util/fsa.h @@ -0,0 +1,252 @@ +/* + +A* Algorithm Implementation using STL is +Copyright (C)2001-2005 Justin Heyes-Jones + +Permission is given by the author to freely redistribute and +include this code in any program as long as this credit is +given where due. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE + OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED + CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL + DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE + OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER + THIS DISCLAIMER. + + Use at your own risk! + + + + FixedSizeAllocator class + Copyright 2001 Justin Heyes-Jones + + This class is a constant time O(1) memory manager for objects of + a specified type. The type is specified using a template class. + + Memory is allocated from a fixed size buffer which you can specify in the + class constructor or use the default. + + Using GetFirst and GetNext it is possible to iterate through the elements + one by one, and this would be the most common use for the class. + + I would suggest using this class when you want O(1) add and delete + and you don't do much searching, which would be O(n). Structures such as binary + trees can be used instead to get O(logn) access time. + +*/ + +#ifndef FSA_H +#define FSA_H + +#include +#include + +template class FixedSizeAllocator +{ + +public: + // Constants + enum + { + FSA_DEFAULT_SIZE = 100 + }; + + // This class enables us to transparently manage the extra data + // needed to enable the user class to form part of the double-linked + // list class + struct FSA_ELEMENT + { + USER_TYPE UserType; + + FSA_ELEMENT* pPrev; + FSA_ELEMENT* pNext; + }; + +public: // methods + FixedSizeAllocator(unsigned int MaxElements = FSA_DEFAULT_SIZE) : + m_pFirstUsed(NULL), + m_MaxElements(MaxElements) + { + // Allocate enough memory for the maximum number of elements + + char* pMem = new char[m_MaxElements * sizeof(FSA_ELEMENT)]; + + m_pMemory = (FSA_ELEMENT*)pMem; + + // Set the free list first pointer + m_pFirstFree = m_pMemory; + + // Clear the memory + memset(m_pMemory, 0, sizeof(FSA_ELEMENT) * m_MaxElements); + + // Point at first element + FSA_ELEMENT* pElement = m_pFirstFree; + + // Set the double linked free list + for (unsigned int i = 0; i < m_MaxElements; i++) + { + pElement->pPrev = pElement - 1; + pElement->pNext = pElement + 1; + + pElement++; + } + + // first element should have a null prev + m_pFirstFree->pPrev = NULL; + // last element should have a null next + (pElement - 1)->pNext = NULL; + + } + + + ~FixedSizeAllocator() + { + // Free up the memory + delete[](char*) m_pMemory; + } + + // Allocate a new USER_TYPE and return a pointer to it + USER_TYPE* alloc() + { + + FSA_ELEMENT* pNewNode = NULL; + + if (!m_pFirstFree) + { + return NULL; + } + else + { + pNewNode = m_pFirstFree; + m_pFirstFree = pNewNode->pNext; + + // if the new node points to another free node then + // change that nodes prev free pointer... + if (pNewNode->pNext) + { + pNewNode->pNext->pPrev = NULL; + } + + // node is now on the used list + + pNewNode->pPrev = NULL; // the allocated node is always first in the list + + if (m_pFirstUsed == NULL) + { + pNewNode->pNext = NULL; // no other nodes + } + else + { + m_pFirstUsed->pPrev = pNewNode; // insert this at the head of the used list + pNewNode->pNext = m_pFirstUsed; + } + + m_pFirstUsed = pNewNode; + } + + return reinterpret_cast(pNewNode); + } + + // Free the given user type + // For efficiency I don't check whether the user_data is a valid + // pointer that was allocated. I may add some debug only checking + // (To add the debug check you'd need to make sure the pointer is in + // the m_pMemory area and is pointing at the start of a node) + void free(USER_TYPE* user_data) + { + FSA_ELEMENT* pNode = reinterpret_cast(user_data); + + // manage used list, remove this node from it + if (pNode->pPrev) + { + pNode->pPrev->pNext = pNode->pNext; + } + else + { + // this handles the case that we delete the first node in the used list + m_pFirstUsed = pNode->pNext; + } + + if (pNode->pNext) + { + pNode->pNext->pPrev = pNode->pPrev; + } + + // add to free list + if (m_pFirstFree == NULL) + { + // free list was empty + m_pFirstFree = pNode; + pNode->pPrev = NULL; + pNode->pNext = NULL; + } + else + { + // Add this node at the start of the free list + m_pFirstFree->pPrev = pNode; + pNode->pNext = m_pFirstFree; + m_pFirstFree = pNode; + } + + } + + // For debugging this displays both lists (using the prev/next list pointers) + void Debug() + { + printf("free list "); + + FSA_ELEMENT* p = m_pFirstFree; + while (p) + { + printf("%x!%x ", p->pPrev, p->pNext); + p = p->pNext; + } + printf("\n"); + + printf("used list "); + + p = m_pFirstUsed; + while (p) + { + printf("%x!%x ", p->pPrev, p->pNext); + p = p->pNext; + } + printf("\n"); + } + + // Iterators + + USER_TYPE* GetFirst() + { + return reinterpret_cast(m_pFirstUsed); + } + + USER_TYPE* GetNext(USER_TYPE* node) + { + return reinterpret_cast + ( + (reinterpret_cast(node))->pNext + ); + } + +public: // data + +private: // methods + +private: // data + + FSA_ELEMENT* m_pFirstFree; + FSA_ELEMENT* m_pFirstUsed; + unsigned int m_MaxElements; + FSA_ELEMENT* m_pMemory; + +}; + +#endif // defined FSA_H \ No newline at end of file diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..c29a3ec --- /dev/null +++ b/include/config.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +typedef uint16_t UnderlyingItemT; + +constexpr uint32_t framesPerSecond = 32; + +inline float FrameToSeconds(uint32_t frame) +{ + return static_cast(frame) / framesPerSecond; +} + +inline uint32_t SecondsToFrames(float time) +{ + return static_cast(time * framesPerSecond); +} + +#define DEV_ASSERT(x) assert(x) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index ad6d478..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(core) diff --git a/src/Components/Config/WorldConfig.cpp b/src/Components/Config/WorldConfig.cpp new file mode 100644 index 0000000..8ccbaa9 --- /dev/null +++ b/src/Components/Config/WorldConfig.cpp @@ -0,0 +1,13 @@ +#include "Components/Configs/WorldConfig.hpp" + +void WorldConfig::RegisterItem(const std::string& name) +{ + for (const auto& item : Items) + { + if (item.Name == name) return; + } + + ItemConfig cfg{}; + cfg.Name = name; + Items.push_back(std::move(cfg)); +} \ No newline at end of file diff --git a/src/Core/Chunk.cpp b/src/Core/Chunk.cpp new file mode 100644 index 0000000..9b06156 --- /dev/null +++ b/src/Core/Chunk.cpp @@ -0,0 +1,247 @@ +#include "Core/Chunk.h" +#include "Chunk.h" + +void ChunkData::MarkAsPersistant(entt::entity entity) +{ + auto removedIt = std::remove_if(Entities.begin(), Entities.end(), [entity](EntityTile& tile) + { + return tile.Entity == entity; + }); + + PersistantEntities.insert(PersistantEntities.end(), removedIt, Entities.end()); + Entities.erase(removedIt, Entities.end()); +} + +void ChunkData::RemovePersistance(entt::entity entity) +{ + auto removedIt = std::remove_if(PersistantEntities.begin(), PersistantEntities.end(), [entity](EntityTile& tile) + { + return tile.Entity == entity; + }); + + Entities.insert(Entities.end(), removedIt, PersistantEntities.end()); + PersistantEntities.erase(removedIt, PersistantEntities.end()); +} + +const Chunk& ChunkCollection::GetChunk(int x, int y) +{ + return GetChunk(ChunkKey{x, y}); +} + +const Chunk &ChunkCollection::GetChunk(ChunkKey key) +{ + return GetChunkInternal(key); +} + +Chunk const* ChunkCollection::TryGetChunk(int x, int y) const +{ + return TryGetChunk(ChunkKey{x, y}); +} + +Chunk const *ChunkCollection::TryGetChunk(ChunkKey key) const +{ + int chunkIndex = TryGetChunkIndex(key); + if (chunkIndex == -1) + return nullptr; + + return ChunkDatas[chunkIndex].Chunk.get(); +} + +Tile ChunkCollection::GetTile(int x, int y) +{ + return GetChunk(x, y).GetTile(Chunk::WorldToLocal(x), Chunk::WorldToLocal(y)); +} + +Tile const *ChunkCollection::TryGetTile(int x, int y) const +{ + Chunk const* chunk = TryGetChunk(x, y); + int chunkX = Chunk::WorldToLocal(x); + int chunkY = Chunk::WorldToLocal(y); + + return &chunk->GetTileRef(chunkX, chunkY); +} + +ChunkData& ChunkCollection::GetChunkData(int x, int y) +{ + return GetChunkData(ChunkKey{x, y}); +} + +ChunkData &ChunkCollection::GetChunkData(ChunkKey key) +{ + int index = GetChunkIndex(key); + return ChunkDatas[index]; +} + +Tile &ChunkCollection::GetTileInternal(int x, int y) +{ + return GetChunkInternal(x, y).GetTile(Chunk::WorldToLocal(x), Chunk::WorldToLocal(y)); +} + +void ChunkCollection::SetTile(Tile tile, int x, int y) +{ + GetTileInternal(x, y) = tile; +} + +void ChunkCollection::InvalidateCachedChunk() +{ + CachedChunk = -1; +} + +entt::entity ChunkCollection::GetEntity(int x, int y) const +{ + int chunkIndex = TryGetChunkIndex(x, y); + int chunkX = Chunk::WorldToLocal(x); + int chunkY = Chunk::WorldToLocal(y); + + if (chunkIndex != -1 && + ChunkDatas[chunkIndex].Chunk->GetTile(chunkX, chunkY).HasEntity()) + { + for (auto& entity : ChunkDatas[chunkIndex].Entities) + { + if (entity.ChunkX == chunkX && entity.ChunkY == chunkY) + return entity.Entity; + } + DEV_ASSERT(false); // should be unreachable + } + + return entt::null; +} + +int ChunkCollection::GetChunkIndex(int x, int y) +{ + return GetChunkIndex(ChunkKey{x, y}); +} + +int ChunkCollection::GetChunkIndex(ChunkKey key) +{ + if (key == CachedChunkKey && CachedChunk != -1) + return CachedChunk; + + auto chunkIt = ChunkMap.find(key); + if (chunkIt != ChunkMap.end()) + { + CachedChunkKey = key; + CachedChunk = chunkIt->value; + return chunkIt->value; + } + + ChunkDatas.push_back({}); + ChunkMap.insert(key, ChunkDatas.size() - 1); + + CachedChunkKey = key; + CachedChunk = ChunkDatas.size() - 1; + + return ChunkDatas.size() - 1; +} + +int ChunkCollection::TryGetChunkIndex(int x, int y) const +{ + return TryGetChunkIndex(ChunkKey{x, y}); +} + +int ChunkCollection::TryGetChunkIndex(ChunkKey key) const +{ + if (key == CachedChunkKey && CachedChunk != -1) + return CachedChunk; + + auto chunkIt = ChunkMap.find(key); + if (chunkIt != ChunkMap.end()) + { + const_cast(CachedChunkKey) = key; + const_cast(CachedChunk) = chunkIt->value; + return chunkIt->value; + } + return -1; +} + +Chunk& ChunkCollection::GetChunkInternal(int x, int y) +{ + return GetChunkInternal(ChunkKey{x, y}); +} + +Chunk &ChunkCollection::GetChunkInternal(ChunkKey key) +{ + int index = GetChunkIndex(key); + return *ChunkDatas[index].Chunk.get(); +} + +void ChunkCollection::SetChunkTiles(int x, int y, std::unique_ptr &&chunk) +{ + SetChunkTiles(ChunkKey{x, y}, std::move(chunk)); +} + +void ChunkCollection::SetChunkTiles(ChunkKey key, std::unique_ptr &&chunk) +{ + GetChunkData(key).Chunk = std::move(chunk); +} + +void ChunkCollection::AddEntity(entt::entity entity, const Vector &claimedPositions) +{ + for (auto& pos : claimedPositions) + { + int chunkX = Chunk::WorldToLocal(pos.x); + int chunkY = Chunk::WorldToLocal(pos.y); + + auto& chunkData = GetChunkData(pos.x, pos.y); + chunkData.Entities.push_back(EntityTile{entity, chunkX, chunkY}); + } +} + +void ChunkCollection::AddPersistantEntity(entt::entity entity, const Vector &claimedPositions) +{ + for (auto& pos : claimedPositions) + { + int chunkX = Chunk::WorldToLocal(pos.x); + int chunkY = Chunk::WorldToLocal(pos.y); + + auto& chunkData = GetChunkData(pos.x, pos.y); + chunkData.PersistantEntities.push_back(EntityTile{entity, chunkX, chunkY}); + } +} + +void ChunkCollection::MarkAsPersistant(entt::entity entity) +{ + for (ChunkData& chunk : ChunkDatas) + { + chunk.MarkAsPersistant(entity); + } +} + +void ChunkCollection::RemovePersistance(entt::entity entity) +{ + for (ChunkData& chunk : ChunkDatas) + { + chunk.RemovePersistance(entity); + } +} + +void ChunkCollection::RemoveEntity(entt::entity entity) +{ + for (ChunkData& chunk : ChunkDatas) + { + chunk.Entities.erase(std::remove_if(chunk.Entities.begin(), chunk.Entities.end(), [entity](EntityTile& tile) + { + return tile.Entity == entity; + })); + chunk.PersistantEntities.erase(std::remove_if(chunk.PersistantEntities.begin(), chunk.PersistantEntities.end(), [entity](EntityTile& tile) + { + return tile.Entity == entity; + })); + } +} + +void ChunkCollection::RemoveChunk(int x, int y) +{ + RemoveChunk(ChunkKey{x, y}); +} + +void ChunkCollection::RemoveChunk(ChunkKey key) +{ + auto it = ChunkMap.find(key); + if (it != ChunkMap.end()) + { + ChunkDatas[it->value].Clear(); + + InvalidateCachedChunk(); + } +} diff --git a/src/Core/FactoryCommandQueue.cpp b/src/Core/FactoryCommandQueue.cpp new file mode 100644 index 0000000..c3dfbec --- /dev/null +++ b/src/Core/FactoryCommandQueue.cpp @@ -0,0 +1,16 @@ +#include "Core/FactoryCommandQueue.h" + +void FactoryCommandQueue::ExecuteAll(FactoryWorld& world) +{ + std::scoped_lock lock(Mutex); + for (auto& command : Commands) + command.Command(world, command.Entity, command.Data); + ClearUnsafe(); +} + +void FactoryCommandQueue::Clear() +{ + std::scoped_lock lock(Mutex); + ClearUnsafe(); +} + diff --git a/src/Core/FactoryWorld.cpp b/src/Core/FactoryWorld.cpp new file mode 100644 index 0000000..4398830 --- /dev/null +++ b/src/Core/FactoryWorld.cpp @@ -0,0 +1,920 @@ +#include "Core/FactoryWorld.h" + +#include "modules/noise/fastnoise_lite.h" +#include "core/config/engine.h" +#include "core/templates/hash_set.h" +#include "scene/main/scene_tree.h" +#include "core/math/random_number_generator.h" + +#include "Main/factory_server.h" + +#include "Core/WorldGenerator.h" +#include "Core/Serialization.h" + +#include "Nodes/Entity/FactoryEntity.h" +#include "Nodes/FactoryWorldInterface.h" + +#include "Components/Sync.h" +#include "Components/Minion.h" +#include "Components/Position.h" +#include "Components/Support.h" + +#include "Util/AStar.h" + +#include +#include "FactoryWorld.h" + +using namespace godot; + +void FactoryWorld::Tick(int amount) +{ + // dont tick sync and render systems when double processing a frame + int32_t framesDrawn = Engine::get_singleton()->get_frames_drawn(); + int lastOrder = (framesDrawn == LastDrawnFrame) ? SystemBase::SYSTEM_EXECUTION_ORDER_SYNC : INT32_MAX; + + WorldAccessMutex.lock(); + + CommandQueue->ExecuteAll(Registry); + + auto& systems = Systems->GetSystems(); + for (auto it{systems.begin()}; it != systems.end() && (*it)->GetExecutionOrder() < lastOrder; ++it) + { + (*it)->Tick(Registry); + } + + WorldAccessMutex.unlock(); + + //Object::cast_to(Engine::get_singleton()->get_main_loop()); +} + +static constexpr auto WorldSavePath = "user://world"; + +void FactoryWorld::Initialize(FactoryWorldInterface* worldInterface) +{ + Interface = worldInterface; + + auto& systemGenerators = FactoryServer::get_singleton()->GetSystemGenerators(); + WorldSettings = FactoryServer::get_singleton()->GetSettings(); + + auto loader = WorldLoader{ *this }; + if (!loader.LoadFile(WorldSavePath)) + { + WorldInventory = { WorldSettings->Items }; + auto random = Ref{}; + random.instantiate(); + Seed = random->randi(); + } + + for (auto& generator : systemGenerators) + { + Systems->RegisterSystem(generator()); + } +} + +void FactoryWorld::Save() +{ + auto saver = WorldSerializer{ *this }; + saver.SerializeFile(WorldSavePath); +} + +void FactoryWorld::SetInventory(const Vector>& items) +{ + WorldInventory = { items }; +} + + + +Inventory FactoryWorld::GetWorldInventory(Vector2 position) const +{ + auto view = Registry.view(); + + int minDistance = INFINITY; + Inventory closestInventory = WorldInventory; + + for (auto [ent, pos, area, inv] : view.each()) + { + if (position.x >= pos.Position.x - area.Size && + position.x <= pos.Position.x + area.Size && + position.y >= pos.Position.y - area.Size && + position.y <= pos.Position.y + area.Size) + { + int distanceSQ = pos.Position.distance_squared_to(position); + if ((!area.IsCircle || distanceSQ <= area.Size * area.Size) && + distanceSQ < minDistance) + { + minDistance = distanceSQ; + closestInventory = inv; + } + } + } + + return closestInventory; +} + +Inventory FactoryWorld::GetWorldInventory(entt::entity entity) const +{ + auto position = Registry.get(entity); + return GetWorldInventory(position.Position); +} + +bool FactoryWorld::SupportCheckerHelper(entt::entity entity) const +{ + auto support = Registry.try_get(entity); + return support && support->SupportsAvailable > 0; +} + +uint8_t FactoryWorld::SupportValueHelper(entt::entity entity) const +{ + auto support = Registry.try_get(entity); + return support ? support->SupportsAvailable : 0; +} + +uint8_t FactoryWorld::GetSupportValue(int x, int y) const +{ + auto entity = Chunks.GetEntity(x, y); + if (entity != entt::null) + return SupportValueHelper(entity); + return 0; +} + +bool FactoryWorld::CheckIfSupportHelper(entt::entity entity, Support& support) const +{ + auto pSupport = Registry.try_get(entity); + if (pSupport) + support = *pSupport; + return pSupport; +} + +void FactoryWorld::ConnectSupports(Vector2i pos, Support& support, Vector2i direction) +{ +} + +// FactoryError FactoryWorld::CanPlaceSupport(int x, int y) const +// { +// { +// auto bottomTile = TryGetTile(x, y - 1); +// if (bottomTile && (bottomTile->IsFiller() || (bottomTile->HasEntity() && SupportCheckerHelper(GetEntity(x, y - 1))))) +// return FACTORY_ERROR_NONE; +// } { +// auto leftTile = TryGetTile(x - 1, y); +// if (leftTile && (leftTile->IsFiller() || (leftTile->HasEntity() && SupportCheckerHelper(GetEntity(x - 1, y))))) +// return FACTORY_ERROR_NONE; +// } { +// auto rightTile = TryGetTile(x + 1, y); +// if (rightTile && (rightTile->IsFiller() || (rightTile->HasEntity() && SupportCheckerHelper(GetEntity(x + 1, y))))) +// return FACTORY_ERROR_NONE; +// } +// return FACTORY_ERROR_REQUIRES_SUPPORT; +// } + +// FactoryError FactoryWorld::CanRemoveSupport(int x, int y) const +// { +// auto supportTile = TryGetTile(x, y); +// Support support{}; +// if (!supportTile || !supportTile->HasEntity() || CheckIfSupportHelper(GetEntity(x, y), support)) +// return FACTORY_ERROR_INVALID_POS; + +// if (support.SupportsUp) +// { +// auto topTile = TryGetTile(x, y + 1); +// auto entity = GetEntity(x, y + 1); +// if (topTile && +// topTile->HasEntity() && +// !Registry.all_of(entity) && +// support.SupportsAvailable > SupportValueHelper(entity) && +// GetSupportValue(x - 1, y + 1) == 0 && +// GetSupportValue(x + 1, y + 1) == 0) +// return FACTORY_ERROR_REQUIRES_SUPPORT; +// } +// if (support.SupportsLeft) +// { +// auto leftTile = TryGetTile(x - 1, y); +// auto entity = GetEntity(x - 1, y); +// if (leftTile && +// leftTile->HasEntity() && +// support.SupportsAvailable > SupportValueHelper(entity) && +// GetSupportValue(x - 2, y) == 0 && +// GetSupportValue(x - 1, y - 1) == 0) +// return FACTORY_ERROR_REQUIRES_SUPPORT; +// } +// if (support.SupportsRight) +// { +// auto rightTile = TryGetTile(x + 1, y); +// auto entity = GetEntity(x + 1, y); +// if (rightTile && +// rightTile->HasEntity() && +// support.SupportsAvailable > SupportValueHelper(entity) && +// GetSupportValue(x + 2, y) == 0 && +// GetSupportValue(x + 1, y - 1) == 0) +// return FACTORY_ERROR_REQUIRES_SUPPORT; +// } + +// return FACTORY_ERROR_NONE; +// } + +// void FactoryWorld::RegisterSupport(int x, int y, Support& support) +// { +// DEV_ASSERT(support.MaxSupport != 0); +// DEV_ASSERT(CanPlaceSupport(x, y) == FACTORY_ERROR_NONE); + +// //auto& tile = GetTileInternal(x, y); + +// { +// auto bottomTile = GetTile(x, y - 1); +// if (bottomTile.IsFiller()) +// { +// support.SupportsAvailable = support.MaxSupport; +// return; +// } +// if (bottomTile.HasEntity()) +// { +// auto entity = GetEntity(x, y - 1); +// auto bottomSupport = Registry.try_get(entity); +// if (bottomSupport && bottomSupport->SupportsAvailable > 0) +// { +// support.SupportsAvailable = std::max(support.SupportsAvailable, bottomSupport->SupportsAvailable - 1); +// bottomSupport->SupportsUp = true; +// support.SupportedByBottom = true; +// } +// } +// } { +// auto leftTile = GetTile(x - 1, y); +// if (leftTile.IsFiller()) +// { +// support.SupportsAvailable = support.MaxSupport; +// return; +// } +// if (leftTile.HasEntity()) +// { +// auto entity = GetEntity(x - 1, y); +// auto leftSupport = Registry.try_get(entity); +// if (leftSupport && leftSupport->SupportsAvailable > 0) +// { +// support.SupportsAvailable = std::max(support.SupportsAvailable, leftSupport->SupportsAvailable - 1); +// leftSupport->SupportsRight = true; +// support.SupportedByLeft = true; +// } +// } +// } { +// auto rightTile = GetTile(x + 1, y); +// if (rightTile.IsFiller()) +// { +// support.SupportsAvailable = support.MaxSupport; +// return; +// } +// if (rightTile.HasEntity()) +// { +// auto entity = GetEntity(x + 1, y); +// auto rightSupport = Registry.try_get(entity); +// if (rightSupport && rightSupport->SupportsAvailable > 0) +// { +// support.SupportsAvailable = std::max(support.SupportsAvailable, rightSupport->SupportsAvailable - 1); +// rightSupport->SupportsLeft = true; +// support.SupportedByRight = true; +// } +// } +// } +// support.SupportsAvailable = std::min(support.SupportsAvailable, support.MaxSupport); +// } + +// void FactoryWorld::RemoveSupport(int x, int y) +// { +// DEV_ASSERT(CanRemoveSupport(x, y) == FACTORY_ERROR_NONE); +// DEV_ASSERT(Registry.all_of(GetEntity(x, y))); + +// Support& support = Registry.get(GetEntity(x, y)); + +// } + +FactoryError FactoryWorld::CanPlaceEntity(int x, int y, Ref archetype) +{ + if (archetype.is_valid() && archetype->Scene.is_valid()) + { + auto& chunk = Chunks.GetChunkData(x, y); + + for (auto& condition : archetype->SpawnConditions) + { + if (condition->IsValid(Chunks, Vector2i{x, y})) + return FACTORY_ERROR_NO_SPACE; + } + + Vector claimedCoordinates{}; + for (auto& condition : archetype->SpawnConditions) + { + condition->ClaimTiles(claimedCoordinates, Chunks, Vector2i{x, y}); + } + + for (auto coordinate : claimedCoordinates) + { + for (auto entity : chunk.Entities) + { + if (entity.ChunkX == coordinate.x && entity.ChunkY == coordinate.y) + return FACTORY_ERROR_NO_SPACE; + } + } + + return FACTORY_ERROR_NONE; + } + + return FACTORY_ERROR_NO_SPACE; +} + +FactoryError FactoryWorld::AddEntity(int x, int y, Ref archetype) +{ + entt::entity createdEntity = CreateEntity(); + return AddEntity(x, y, archetype, createdEntity); +} + +void FactoryWorld::RemoveEntity(FactoryEntity *node) +{ + RemoveEntity(node->GetEntity()); +} + +void FactoryWorld::RemoveEntity(int x, int y) +{ + RemoveEntity(Chunks.GetEntity(x, y)); +} + +void FactoryWorld::RemoveEntity(entt::entity entity) +{ + if (entity == entt::null || !Registry.all_of(entity)) + return; + + auto&& [node, arch, pos] = Registry.get(entity); + + node.Node->queue_free(); + Chunks.RemoveEntity(entity); + Registry.destroy(entity); +} + +FactoryError FactoryWorld::AddEntity(int x, int y, Ref archetype, entt::entity entityID) +{ + auto canPlace = CanPlaceEntity(x, y, archetype); + if (canPlace != FACTORY_ERROR_NONE) + return canPlace; + + // instantiate node + Node2D* node = Object::cast_to(archetype->Scene->instantiate()); + auto factoryNode = Object::cast_to(node); + DEV_ASSERT(node); + + // set position in ecs + auto tilePos = TilePosition(); + tilePos.Position = Vector2i(x, y); + Registry.emplace(entityID, tilePos); + + // link archetype to the entity + ArchetypePtr archPtr{}; + archPtr.Archetype = archetype; + Registry.emplace(entityID, archPtr); + + // Add Level component if archetype has levels + if (!archetype->Upgrades.is_empty()) + { + Registry.emplace(entityID); + } + + // register the entities in the chunks + Vector locations{}; + for (auto& condition : archetype->SpawnConditions) + { + condition->ClaimTiles(locations, Chunks, Vector2i{x, y}); + } + + Chunks.AddEntity(entityID, locations); + + // TODO add to chunk node instead of world + Interface->add_child(node); + node->set_position(Vector2(x + 0.5f, -y + 1)); + + if (factoryNode) + { + // set node ptr in ecs + NodePtr nodePtr{}; + nodePtr.Node = factoryNode; + Registry.emplace(entityID, nodePtr); + + factoryNode->SetCommandQueue(CommandQueue); + factoryNode->SetEntity(entityID); + + // initialize data in ecs + factoryNode->Initialize(Registry, *this, entityID); + } + + return FACTORY_ERROR_NONE; +} + +entt::entity FactoryWorld::CreateEntity() +{ + return Registry.create(); +} + +bool FactoryWorld::IsValidCameraPos(Rect2i viewport) const +{ + auto pos0 = viewport.position; + auto pos3 = viewport.get_end(); + auto pos1 = Vector2i{ pos0.x, pos3.y }; + auto pos2 = Vector2i{ pos3.x, pos0.y }; + + bool valid0{}; + bool valid1{}; + bool valid2{}; + bool valid3{}; + + for (auto chunk : UnlockedChunks) + { + auto bounds = chunk.GetBounds(); + valid0 = valid0 || bounds.has_point(pos0); + valid1 = valid1 || bounds.has_point(pos1); + valid2 = valid2 || bounds.has_point(pos2); + valid3 = valid3 || bounds.has_point(pos3); + } + + return valid0 && valid1 && valid2 && valid3; +} + +FactoryError FactoryWorld::TryUnlockChunk(ChunkKey chunk) +{ + for (auto& unlockableChunk : UnlockableChunks) + { + if (unlockableChunk.ChunkID == chunk) + { + for (auto& requirement : unlockableChunk.Items) + if (WorldInventory.GetItemsAmount(requirement.Item.Item) < requirement.Amount) + return FACTORY_ERROR_NOT_ENOUGH_ITEMS; + + for (auto& requirement : unlockableChunk.Items) + WorldInventory.RemoveItems(requirement.Item); + + UnlockedChunks.push_back(chunk); + RefreshUnlockedChunks(); + return FACTORY_ERROR_NONE; + } + } + return FACTORY_ERROR_INVALID_POS; +} + +void FactoryWorld::RefreshUnlockedChunks() +{ + UnlockedChunks.clear(); + if (WorldSettings->LayerConfigs.is_empty()) return; + + HashSet unlockableChunks{}; + int maxHeight = WorldSettings->LayerConfigs[0]->StartChunk; + + for (auto& unlockedChunk : UnlockedChunks) + { + ChunkKey chunk0 = unlockedChunk; + ChunkKey chunk1 = unlockedChunk; + ChunkKey chunk2 = unlockedChunk; + ChunkKey chunk3 = unlockedChunk; + + chunk0.X -= 1; chunk1.X += 1; + chunk2.Y -= 1; chunk3.Y += 1; + + unlockableChunks.insert(chunk0); + unlockableChunks.insert(chunk1); + unlockableChunks.insert(chunk2); + unlockableChunks.insert(chunk3); + } + + for (auto& unlockedChunk : UnlockedChunks) + { + unlockableChunks.erase(unlockedChunk); + } + + for (auto& unlockable : unlockableChunks) + { + UnlockedChunks.push_back(unlockable); + } +} + +// void FactoryWorld::PlaceLight(LightValue light, Vector2i position) +// { +// PropogateLight(light, position); +// Interface->call_deferred("update_lightmap"); +// } + +// void FactoryWorld::RefreshLightMap() +// { +// auto lights = Registry.view(); + +// // Find World Bounds +// Bounds = Bounds.merge(Rect2i(-256, -256, 512, 300)); +// lights.each([this](LightValue light, TilePosition position) +// { +// Rect2i lightRadius = Rect2i(position.Position - Vector2i(light.Val, light.Val), 2.f * light.Val * Vector2i(1, 1)); +// Bounds = Bounds.merge(lightRadius); +// }); + +// // reset light chunks +// for (int i{}; i < Chunks.size(); ++i) +// for (int j{}; j < Chunks[i].LightChunk.Tiles.size(); ++j) +// Chunks[i].LightChunk.Tiles[j].Val = 0; + +// // skylight +// ApplySkyLight(Bounds); + +// // propogate all light sources +// lights.each([this](LightValue light, TilePosition position) +// { +// PropogateLight(light, position.Position); +// }); + +// // sync bounds with interface +// Interface->call_deferred("set_bounds", Bounds); +// } + +// void FactoryWorld::ApplySkyLight(Rect2i bounds) +// { +// int lowestLightval = INT32_MAX; +// int highestLightVal = INT32_MIN; + +// // go down until block is found +// for (int x{ bounds.position.x }; x <= bounds.get_end().x; ++x) +// for (int y{ bounds.get_end().y }; y >= bounds.position.y; --y) +// { +// auto tileData = GetTileData(x, y); + +// if (tileData.Tile.IsTile()) +// { +// lowestLightval = std::min(lowestLightval, y + 1); +// highestLightVal = std::max(highestLightVal, y + 1); +// break; +// } + +// tileData.Light.Val = LightValue::MaxLightVal; + +// } + +// // spread each light block +// for (int x{ bounds.position.x }; x <= bounds.get_end().x; ++x) +// for (int y{ lowestLightval }; y <= highestLightVal; ++y) +// { +// LightValue& value = GetLightValueInternal(x, y); +// if (value.Val == LightValue::MaxLightVal) +// { +// auto spreadValue = value; +// value.Val = 0; +// PropogateLight(spreadValue, Vector2i(x, y)); +// } +// } + +// Interface->call_deferred("update_lightmap"); +// } + +// void FactoryWorld::PropogateLight(LightValue light, Vector2i position) +// { +// // light value of tile +// LightValue& lightVal = GetLightValueInternal(position.x, position.y); + +// int previousVal = lightVal.Val; +// lightVal.Val = std::max(light.Val, lightVal.Val); + +// // new light value decided by taking substracting the penetration +// int newLightVal = light.Val - std::max(1, lightVal.Penetration / light.Penetration); + +// if (light.Val > previousVal && newLightVal >= 0) +// { +// light.Val = newLightVal; + +// // check neighboring tiles +// PropogateLight(light, position + Vector2i(0, +1)); +// PropogateLight(light, position + Vector2i(0, -1)); +// PropogateLight(light, position + Vector2i(+1, 0)); +// PropogateLight(light, position + Vector2i(-1, 0)); +// } +// } + +FactoryError FactoryWorld::TryUpgradeEntity(FactoryEntity* entity) +{ + return TryUpgradeEntity(entity->GetEntity()); +} + +FactoryError FactoryWorld::TryUpgradeEntity(entt::entity entity) +{ + auto upgradeError = CanUpgradeEntity(entity); + if (upgradeError) + return upgradeError; + + Level const* level = Registry.try_get(entity); + auto archetype = Registry.get(entity); + + // remove ingredients + auto newLevelIngredients = archetype.Archetype->Upgrades[level->Val]; + { + auto inventory = FactoryServer::get_singleton()->AccessInventory(); + + for (auto ingredient : newLevelIngredients->UpgradeCost) + { + inventory->RemoveItems(ingredient->Item->Item.ItemID, ingredient->Amount); + } + } + + UpgradeEntity(entity, archetype.Archetype); + + return FACTORY_ERROR_NONE; +} + +FactoryError FactoryWorld::TryUpgradeEntity(const Vector2i& position) +{ + auto entity = Chunks.GetEntity(position.x, position.y); + if (entity != entt::null) + return TryUpgradeEntity(entity); + + return FACTORY_ERROR_NOT_ENTITY; +} + +FactoryError FactoryWorld::CanUpgradeEntity(entt::entity entity) const +{ + Level const* level = Registry.try_get(entity); + auto archetype = Registry.get(entity); + if (!level || archetype.Archetype.is_null()) + return FACTORY_ERROR_CANT_UPGRADE; + + if (level->Val >= archetype.Archetype->Upgrades.size()) + return FACTORY_ERROR_FULLY_UPGRADED; + + auto newLevelIngredients = archetype.Archetype->Upgrades[level->Val]; + { + auto inventory = FactoryServer::get_singleton()->AccessInventory(); + for (auto ingredient : newLevelIngredients->UpgradeCost) + { + if (inventory->GetItemsAmount(ingredient->Item->Item.ItemID) < ingredient->Amount) + return FACTORY_ERROR_NOT_ENOUGH_ITEMS; + } + } + + auto upgradeError = Registry.get(entity).Node->CanUpgrade(); + if (upgradeError != FACTORY_ERROR_NONE) + return upgradeError; + + return FACTORY_ERROR_NONE; +} + +FactoryError FactoryWorld::CanUpgradeEntity(FactoryEntity* entity) const +{ + return CanUpgradeEntity(entity->GetEntity()); +} + +FactoryError FactoryWorld::CanUpgradeEntity(const Vector2i& position) +{ + auto entity = Chunks.GetEntity(position.x, position.y); + if (entity != entt::null) + return CanUpgradeEntity(entity); + + return FACTORY_ERROR_NOT_ENTITY; +} + +void FactoryWorld::UpgradeEntity(entt::entity entity, Ref archetype) +{ + FactoryEntity* node = Registry.get(entity).Node; + + auto& level = Registry.get(entity); + Registry.emplace(entity); + + auto newLevelIngredients = archetype->Upgrades[level.Val]; + int newLevel = ++level.Val; + + node->SetLevel(newLevel, newLevelIngredients); +} + +void FactoryWorld::SetEntityLevel(entt::entity entity, uint8_t level) +{ + SetEntityLevel(entity, Registry.get(entity).Archetype, level); +} + +void FactoryWorld::SetEntityLevel(entt::entity entity, Ref archetype, uint8_t level) +{ + FactoryEntity* node = Registry.get(entity).Node; + + auto& lvl = Registry.get(entity); + + if (lvl.Val != level) + { + lvl = Level(level); + + node->SetLevel(level, archetype->Upgrades[level]); + } +} + +void FactoryWorld::HighlightUpgradableEntities(TileMapLayer* tilemap) const +{ + tilemap->clear(); + + auto view = Registry.view(); + + view.each([tilemap](Level lvl, ArchetypePtr ptr, TilePosition pos) { + if (!ptr.Archetype->OccupiedTiles.is_empty() && + lvl.Val < ptr.Archetype->Upgrades.size()) + { + for (const auto& tile : ptr.Archetype->OccupiedTiles) + { + Vector2i position = pos.Position + tile->Offset; + position.y = -position.y; + tilemap->set_cell(position, 0, Vector2i(0, 0)); + } + } + }); +} + +void FactoryWorld::UpgradeEntity(entt::entity entity) +{ + UpgradeEntity(entity, Registry.get(entity).Archetype); +} + +struct WorldAStarNode +{ +public: + WorldAStarNode(const FactoryWorld& world, Vector2i pos) : World{ &world }, Position{ pos } {}; + WorldAStarNode() = default; + WorldAStarNode(const WorldAStarNode&) = default; + WorldAStarNode(WorldAStarNode&&) = default; + + WorldAStarNode& operator=(const WorldAStarNode&) = default; + WorldAStarNode& operator=(WorldAStarNode&&) = default; + +public: + + // Heuristic function which computes the estimated cost to the goal node + float GoalDistanceEstimate(const WorldAStarNode& nodeGoal) const + { + return static_cast(Position.distance_squared_to(nodeGoal.Position)); + } + + // Returns true if this node is the goal node + bool IsGoal(const WorldAStarNode& nodeGoal) const + { + return Position == nodeGoal.Position; + } + + // Retrieves all successors to this node and adds them via astarsearch.addSuccessor() + bool GetSuccessors(AStarSearch* astarsearch, WorldAStarNode* parent_node) const + { + Vector2i parentPos{}; + if (parent_node) + { + parentPos = parent_node->Position; + } + + Vector2i bottomPos = Vector2i(Position.x, Position.y - 1); + Vector2i leftPos = Vector2i(Position.x - 1, Position.y); + Vector2i rightPos = Vector2i(Position.x + 1, Position.y); + + auto bottomTile = World->TryGetTile(bottomPos.x, bottomPos.y); + auto leftTile = World->TryGetTile(leftPos.x, leftPos.y); + auto rightTile = World->TryGetTile(rightPos.x, rightPos.y); + + if (bottomPos != parentPos && bottomTile && !bottomTile->IsFiller()) + astarsearch->AddSuccessor(WorldAStarNode(*World, bottomPos)); + + if (leftPos != parentPos && leftTile && !leftTile->IsFiller()) + astarsearch->AddSuccessor(WorldAStarNode(*World, leftPos)); + + if (rightPos != parentPos && rightTile && !rightTile->IsFiller()) + astarsearch->AddSuccessor(WorldAStarNode(*World, rightPos)); + + return true; + } + + // Computes the cost of travelling from this node to the successor node + float GetCost(const WorldAStarNode& successor) const + { + return 1.f; + } + + // Returns true if this node is the same as the rhs node + bool IsSameState(const WorldAStarNode& rhs) const + { + return rhs.Position == Position; + } + + // Returns a hash for the state + size_t Hash() const + { + static_assert(sizeof(size_t) == sizeof(Vector2i)); + return reinterpret_cast(Position); + } +public: + FactoryWorld const* World; + Vector2i Position{}; +}; + +FactoryError FactoryWorld::FindChutePath(Vector& path, Vector2i startPos, Vector2i endPos) const +{ + constexpr int MaxLength = 128; + if (startPos.distance_squared_to(endPos) > MaxLength * MaxLength) + return FACTORY_ERROR_PATH_TOO_LONG; + + auto tile = TryGetTile(startPos.x, startPos.y); + if (!tile || !tile->IsAir()) + return FACTORY_ERROR_INVALID_POS; + + tile = TryGetTile(endPos.x, endPos.y); + if (!tile || !tile->IsAir()) + return FACTORY_ERROR_INVALID_POS; + + if (endPos.y > startPos.y) + std::swap(endPos, startPos); + + AStarSearch aStarSearch{MaxLength * MaxLength * 2}; + aStarSearch.SetStartAndGoalStates(WorldAStarNode{ *this, startPos }, WorldAStarNode{ *this, endPos }); + + unsigned int searchState = -1; + do + { + searchState = aStarSearch.SearchStep(); + + } while (searchState == AStarSearch::SEARCH_STATE_SEARCHING); + + switch (searchState) + { + case AStarSearch::SEARCH_STATE_FAILED: + case AStarSearch::SEARCH_STATE_INVALID: + case AStarSearch::SEARCH_STATE_NOT_INITIALISED: + case AStarSearch::SEARCH_STATE_OUT_OF_MEMORY: + default: + return FACTORY_ERROR_INVALID_PATH; + + case AStarSearch::SEARCH_STATE_SUCCEEDED: + path.clear(); + path.push_back(aStarSearch.GetSolutionStart()->Position); + + while (true) + { + auto newCell = aStarSearch.GetSolutionNext(); + if (unlikely(!newCell)) + { + path.push_back(endPos); + break; + } + + if (Raycast(path[path.size() - 1], newCell->Position)) + path.push_back(newCell->Position); + } + + aStarSearch.FreeSolutionNodes(); + break; + } + + aStarSearch.EnsureMemoryFreed(); + return FACTORY_ERROR_NONE; +} + +Tile const* FactoryWorld::Raycast(Vector2i startPos, Vector2i endPos) const +{ + Vector2 direction = Vector2(endPos - startPos).normalized(); + Vector2 deltaDist{ std::abs(1 / direction.x), std::abs(1 / direction.y) }; + Vector2 step{ 1, 1 }; + Vector2 sideDist{ 0.5f, 0.5f }; + + int mapX = startPos.x; + int mapY = startPos.y; + + if (direction.x < 0) + { + step.x = -1; + } + + if (direction.y < 0) + { + step.y = -1; + } + + while (true) + { + if (sideDist.x < sideDist.y) + { + sideDist.x += deltaDist.x; + mapX += step.x; + } + else + { + sideDist.y += deltaDist.y; + mapY += step.y; + } + + Tile const* cell = TryGetTile(mapX, mapY); + + if (cell && !cell->IsAir()) + { + return cell; + } + + if (mapX < 0 || mapX >= endPos.x || mapY < 0 || mapY >= endPos.y) + { + return nullptr; + } + } +} + +bool FactoryWorld::IsSupport(int x, int y) const +{ + auto entity = GetEntity(x, y); + return entity != entt::null && Registry.try_get(entity); +} + +bool FactoryWorld::IsSupport(entt::entity entity) const +{ + return Registry.try_get(entity); +} diff --git a/src/Core/WorldGenerator.cpp b/src/Core/WorldGenerator.cpp new file mode 100644 index 0000000..89fb738 --- /dev/null +++ b/src/Core/WorldGenerator.cpp @@ -0,0 +1,618 @@ +#include "Core/WorldGenerator.h" +#include "Core/FactoryWorld.h" +#include "WorldGenerator.h" +#include + +using namespace godot; + +template +void SetNoiseSeed(Vector>& layers, int32_t seed) +{ + for (auto& layer : layers) + { + if (layer->NoiseGenerator.is_null()) + layer->NoiseGenerator.instantiate(); + layer->NoiseGenerator->set_seed(seed); + } +} + +// WorldGenerator::WorldGenerator(Ref settings, int32_t seed) +// : Settings{ settings } +// , Seed{ seed } +// { +// if (Settings.is_valid()) +// { +// Graph = Settings->WorldGenerator; +// } +// } + +// static Vector GetGraphFunctions(Ref layer, const WorldGraph& graph); +// static std::array, TILE_TYPE::TILE_MAX> DivideTileTypes(const Vector& tiles); + +struct TileGeneratorFunction final +{ + WorldNodeBase* Function{}; + Tile ReturnedTile{}; +}; + +Vector GetGraphFunctions(Ref layer, const WorldGraph& graph) +{ + if (layer.is_null()) return {}; + + Vector Generators{}; + for (auto tileGen : layer->Tiles) + { + TileGeneratorFunction generator{}; + generator.Function = graph.GetNode(tileGen->TileGenerator); + generator.ReturnedTile = tileGen->Tile->TileData; + Generators.push_back(generator); + } + return Generators; +} + +std::array, TILE_TYPE::TILE_MAX> DivideTileTypes(const Vector& tiles) +{ + std::array, TILE_TYPE::TILE_MAX> values{}; + for (int i{}, j{}; i < TILE_TYPE::TILE_MAX; ++i) + { + values[i] = tcb::span{tiles.ptr() + j, std::size_t{0}}; + for (; j < tiles.size() && tiles[j].ReturnedTile.GetType() == static_cast(i); ++j) + { + values[i] = tcb::span(values[i].data(), tiles.ptr() + j + 1); + } + } + return values; +} + +void ApplyGeneratorFunctions(WorldNodeParameters& parameters, tcb::span generators, tcb::span nextGenerators = {}) +{ + if (generators.empty() && nextGenerators.empty()) return; + + auto bounds = parameters.GetGenerationBounds(); + auto chunkBounds = parameters.ChunkInfo.GetBounds(); + + auto buffer = WorldNodeParameters::TileArray{ *parameters.GeneratedTiles }; + + for (int y = bounds.position.y; y < bounds.get_end().y; ++y) + { + float topLayerSubtract = !nextGenerators.empty() ? std::clamp(static_cast(y - chunkBounds.position.y) / Chunk::ChunkSize, 0.f, 1.f) : 0; + float bottomLayerSubstract = 1.f - topLayerSubtract; + + for (int x = bounds.position.x; x < bounds.get_end().x; ++x) + { + parameters.X = x; + parameters.Y = y; + + float maxVal{}; + + for (auto& gen : generators) + { + auto val = gen.Function->Evaluate(parameters).get_unsafe_float() - topLayerSubtract; + if (val > maxVal) + { + maxVal = val; + buffer[parameters.GetArrayIndex(x, y)] = gen.ReturnedTile; + } + } + for (auto& gen : nextGenerators) + { + auto val = gen.Function->Evaluate(parameters).get_unsafe_float() - bottomLayerSubstract; + if (val > maxVal) + { + maxVal = val; + buffer[parameters.GetArrayIndex(x, y)] = gen.ReturnedTile; + } + } + } + } + + *parameters.GeneratedTiles = buffer; +} + +bool IsPocket(const WorldNodeParameters::TileArray& tiles, Vector2i pos, Tile tile) +{ + for (int x{pos.x}; x >= 0 && tiles[WorldNodeParameters::GetArrayIndex(x - 1, pos.y)].IsAir(); --x) + { + auto underneathTile = tiles[WorldNodeParameters::GetArrayIndex(x, pos.y - 1)]; + if ((!underneathTile.IsFiller() && underneathTile != tile) || x - 1 < 0) return false; + } + for (int x{pos.x + 1}; x < Chunk::ChunkSize && tiles[WorldNodeParameters::GetArrayIndex(x + 1, pos.y)].IsAir(); ++x) + { + auto underneathTile = tiles[WorldNodeParameters::GetArrayIndex(x, pos.y - 1)]; + if ((!underneathTile.IsFiller() && underneathTile != tile) || x + 1 >= Chunk::ChunkSize) return false; + } + return true; +} + +void FillPocket(WorldNodeParameters::TileArray& tiles, Vector2i pos, Tile tile) +{ + for (int x{pos.x}; x >= 0 && tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)].IsAir(); --x) + { + tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)] = tile; + } + for (int x{pos.x + 1}; x < Chunk::ChunkSize && tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)].IsAir(); ++x) + { + tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)] = tile; + } +} + +void FlowLiquid(WorldNodeParameters& parameters, Vector2i pos, Tile liquidTile) +{ + auto& tiles = *parameters.GeneratedTiles; + + if (tiles[parameters.GetArrayIndex(pos.x, pos.y)].IsFiller()) return; + + for (int y{pos.y}; y >= 0; --y) + { + if (tiles[parameters.GetArrayIndex(pos.x, y - 1)].IsFiller()) + { + for (int upperBounds{y + 2}; y <= pos.y && y < upperBounds && IsPocket(tiles, Vector2i{pos.x, y}, liquidTile); ++y) + { + FillPocket(tiles, Vector2i{pos.x, y}, liquidTile); + } + return; + } + } +} + +void ApplyLiquids(WorldNodeParameters& parameters, tcb::span generators, tcb::span nextGenerators = {}) +{ + auto chunkCenter = parameters.ChunkInfo.GetBounds().get_center(); + parameters.X = chunkCenter.x; + parameters.Y = chunkCenter.y; + + for (auto& generator : generators) + { + int frequency = std::clamp(static_cast(Chunk::ChunkSize * static_cast(generator.Function->Evaluate(parameters))), 1, Chunk::ChunkSize); + + for (int y{ Chunk::ChunkSize - 1 }; y >= (nextGenerators.empty() ? 0 : (Chunk::ChunkSize / 2)); y -= frequency) + { + for (int x{frequency / 2}; x < Chunk::ChunkSize; x += frequency) + { + FlowLiquid(parameters, Vector2i(x, y), generator.ReturnedTile); + } + } + } + for (auto& generator : nextGenerators) + { + int frequency = std::clamp(static_cast(Chunk::ChunkSize * static_cast(generator.Function->Evaluate(parameters))), 1, Chunk::ChunkSize); + + for (int y{ Chunk::ChunkSize / 2 }; y >= 0; y -= frequency) + { + for (int x{frequency / 2}; x < Chunk::ChunkSize; x += frequency) + { + FlowLiquid(parameters, Vector2i(x, y), generator.ReturnedTile); + } + } + } +} + +// bool WorldGenerator::GenerateChunk(ChunkKey chunkKey, Chunk& chunk) const +// { +// auto [currentLayer, nextLayer] = GetLayers(chunkKey); + +// if (currentLayer.is_null()) return false; + +// return GenerateChunk(chunkKey, chunk, currentLayer, nextLayer); +// } + +// bool WorldGenerator::GenerateChunk(ChunkKey chunkKey, Chunk &chunk, Ref layer, Ref nextLayer) const +// { +// Vector Generators{GetGraphFunctions(layer, Graph)}; +// Vector NextGenerators{GetGraphFunctions(nextLayer, Graph)}; + +// auto generatorLayers = DivideTileTypes(Generators); +// auto nextGeneratorLayers = DivideTileTypes(NextGenerators); + +// WorldNodeParameters Parameters{}; +// Parameters.ChunkInfo = chunkKey; +// Parameters.FinalValueSubstract = 0; +// Parameters.Seed = Seed; +// auto bounds = Parameters.GetGenerationBounds(); + +// WorldNodeParameters::TileArray buffer{}; + +// int chunkStart = chunkKey.GetBounds().position.y; + +// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_AIR], nextGeneratorLayers[TILE_AIR]); +// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_FILLER], nextGeneratorLayers[TILE_FILLER]); +// ApplyLiquids(Parameters, generatorLayers[TILE_LIQUID], nextGeneratorLayers[TILE_LIQUID]); +// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_ORE], nextGeneratorLayers[TILE_ORE]); +// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_NPC], nextGeneratorLayers[TILE_NPC]); +// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_PLANT], nextGeneratorLayers[TILE_PLANT]); + +// for (int y{}; y < Chunk::ChunkSize; ++y) +// { +// for (int x{}; x < Chunk::ChunkSize; ++x) +// { +// chunk.Tiles[y * Chunk::ChunkSize + x] = Parameters.GeneratedTiles[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)]; +// } +// } + +// return true; +// } + +// Vector WorldGenerator::SpawnEntities(ChunkKey chunkKey, Chunk &chunk, const std::vector& persistantEntities) const +// { +// auto [currentLayer, nextLayer] = GetLayers(chunkKey); + +// if (currentLayer.is_null()) return {}; + +// return SpawnEntities(chunkKey, chunk, currentLayer, nextLayer, persistantEntities); +// } + +// Vector WorldGenerator::SpawnEntities(ChunkKey chunkKey, Chunk &chunk, Ref layer, Ref nextLayer, const std::vector& persistantEntities) const +// { + +// } + +// struct ThreadParameters +// { +// WorldGenerator Generator{}; +// ChunkKey ChunkKey{}; +// std::function Callback{}; +// std::vector PersistantEntities{}; +// }; + +// void ThreadedGenerateChunk_Internal(void* pData) +// { +// auto parameters = static_cast(pData); + +// ChunkData data{}; +// data.Chunk = std::make_unique(); + +// parameters->Generator.GenerateChunk(parameters->ChunkKey, *data.Chunk); +// auto entities = parameters->Generator.SpawnEntities(parameters->ChunkKey, *data.Chunk, parameters->PersistantEntities); + +// for (int i{}; i < entities.size(); ++i) +// { +// auto entity = entities[i]; +// for (auto pos : entity.ClaimedPositions) +// { +// data.Entities.push_back(EntityTile { +// entt::entity{i}, ChunkKey::WorldToChunk(pos.x), ChunkKey::WorldToChunk(pos.y) +// }); +// } +// } +// data.PersistantEntities = std::move(std::move(parameters->PersistantEntities)); + +// parameters->Callback(std::move(data)); + +// memdelete(parameters); +// } + +// void WorldGenerator::ThreadedGenerateChunk(ChunkKey chunkKey, std::function callback, const std::vector& persistantEntities) +// { +// DEV_ASSERT(callback); + +// ChunkData Chunk{}; + +// auto pParameters = memnew(ThreadParameters); +// pParameters->Callback = callback; +// pParameters->ChunkKey = chunkKey; +// pParameters->Generator = *this; +// pParameters->PersistantEntities = persistantEntities; + +// WorkerThreadPool::TaskID tid = WorkerThreadPool::get_singleton()->add_native_task(&ThreadedGenerateChunk_Internal, pParameters, false); +// } + +Pair, Ref> ChunkGenerator::GetLayers() const +{ + Ref currentLayer{}; + Ref nextLayer{}; + for (auto& layer : Settings->LayerConfigs) + { + if (layer->StartChunk > ChunkInfo.Y) + { + currentLayer = layer; + } + else if (layer->StartChunk == ChunkInfo.Y) + { + nextLayer = layer; + } + else if (layer->StartChunk < ChunkInfo.Y) + { + break; + } + } + return {currentLayer, nextLayer}; +} + +void ChunkGenerator::FillChunkCollection(int relativeX, int relativeY, ChunkCollection &collection) const +{ + ChunkKey relativeKey{}; + relativeKey.X = relativeX; + relativeKey.Y = relativeY; + + auto bounds = relativeKey.GetBounds(); + + auto chunk = std::make_unique(); + + for (int y{ bounds.position.y }; y < bounds.get_end().y; ++y) + { + for (int x{ bounds.position.x }; x < bounds.get_end().x; ++x) + { + if (InBounds(x, y)) + { + (*chunk).GetTile(Chunk::WorldToLocal(x), Chunk::WorldToLocal(y)) = GetTile(x, y); + } + } + } + + ChunkKey key{ ChunkInfo }; + key.X += relativeX; + key.Y += relativeY; + + collection.SetChunkTiles(key, std::move(chunk)); +} + +Tile ChunkGenerator::GetTile(int x, int y) const +{ + DEV_ASSERT(InBounds(x, y)); + return (*TileArray)[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)]; +} + +bool ChunkGenerator::InBounds(int x, int y) const +{ + return x > -WorldNodeParameters::MaxQueryOffset && x < WorldNodeParameters::MaxQueryOffset + Chunk::ChunkSize && + y > -WorldNodeParameters::MaxQueryOffset && y < WorldNodeParameters::MaxQueryOffset + Chunk::ChunkSize; +} + +struct ThreadParameters +{ + ChunkGenerator Generator; + ChunkGenerator::CreatedChunkCallback ChunkCallback; + ChunkGenerator::SpawnedEntitiesCallback EntitiesCallback; + ChunkGenerator::VisualizedChunkCallback VisualsCallback; + ChunkGenerator::ShadowsCallback ShadowsCallback; +}; + +void ChunkGenerator::GenerateChunk(Ref settings, ChunkKey chunkInfo, int seed, CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback, VisualizedChunkCallback visualsCallback, ShadowsCallback shadowsCallback) +{ + auto parameters = memnew(ThreadParameters); + parameters->Generator = ChunkGenerator(settings, chunkInfo, seed); + parameters->ChunkCallback = chunkCallback; + parameters->EntitiesCallback = entitiesCallback; + parameters->VisualsCallback = visualsCallback; + parameters->ShadowsCallback = shadowsCallback; + + WorkerThreadPool::get_singleton()->add_native_task(&ChunkGenerator::GenerateChunk, parameters, false, "Generating Chunk"); +} + +std::unique_ptr ChunkGenerator::GenerateChunkTilesNonThreaded() +{ + std::unique_ptr returnVal; + + GenerateChunkInternal([&returnVal](std::unique_ptr&& chunk) + { + returnVal = std::move(chunk); + }, {}, {}, {}); + + return std::move(returnVal); +} + +void ChunkGenerator::GenerateChunk(void *pData) +{ + ThreadParameters* parameters = static_cast(pData); + + parameters->Generator.GenerateChunkInternal(parameters->ChunkCallback, parameters->EntitiesCallback, parameters->VisualsCallback, parameters->ShadowsCallback); + + memdelete(parameters); +} + +void ChunkGenerator::GenerateChunkInternal(CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback, VisualizedChunkCallback visualsCallback, ShadowsCallback shadowsCallback) +{ + TileArray = std::make_unique(); + + GenerateChunkTiles(); + + if (chunkCallback) + { + auto chunk = std::make_unique(); + + for (int y{}; y < Chunk::ChunkSize; ++y) + { + for (int x{}; x < Chunk::ChunkSize; ++x) + { + chunk->Tiles[y * Chunk::ChunkSize + x] = (*TileArray)[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)]; + } + } + + chunkCallback(std::move(chunk)); + } + + if (entitiesCallback) + { + auto entities = SpawnEntities(); + + entitiesCallback(entities); + } + + if (visualsCallback) + { + auto visuals = CreateVisuals(); + + visualsCallback(visuals.get()); + } + + if (shadowsCallback) + { + auto shadowMap = CascadeShadows(); + + shadowsCallback(shadowMap.get()); + } +} + +void ChunkGenerator::GenerateChunkTiles() const +{ + auto [currentLayer, nextLayer] = GetLayers(); + + Vector Generators{GetGraphFunctions(currentLayer, Settings->WorldGenerator)}; + Vector NextGenerators{GetGraphFunctions(nextLayer, Settings->WorldGenerator)}; + + auto generatorLayers = DivideTileTypes(Generators); + auto nextGeneratorLayers = DivideTileTypes(NextGenerators); + + WorldNodeParameters Parameters{}; + Parameters.ChunkInfo = ChunkInfo; + Parameters.FinalValueSubstract = 0; + Parameters.Seed = Seed; + Parameters.GeneratedTiles = TileArray.get(); + auto bounds = Parameters.GetGenerationBounds(); + + WorldNodeParameters::TileArray buffer{}; + + int chunkStart = ChunkInfo.GetBounds().position.y; + + ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_AIR], nextGeneratorLayers[TILE_AIR]); + ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_FILLER], nextGeneratorLayers[TILE_FILLER]); + ApplyLiquids(Parameters, generatorLayers[TILE_LIQUID], nextGeneratorLayers[TILE_LIQUID]); + ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_ORE], nextGeneratorLayers[TILE_ORE]); + ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_NPC], nextGeneratorLayers[TILE_NPC]); + ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_PLANT], nextGeneratorLayers[TILE_PLANT]); +} + +Vector ChunkGenerator::SpawnEntities(const std::vector &persistantEntities) const +{ + Vector spawnedEntities{}; + Vector claimedTilesBuffer{}; + std::bitset claimedTiles{}; + auto chunkBounds = ChunkInfo.GetBounds(); + + ChunkCollection collection{}; + + FillChunkCollection(-1, +1, collection); FillChunkCollection(+0, +1, collection); FillChunkCollection(+1, +1, collection); + FillChunkCollection(-1, +0, collection); FillChunkCollection(+0, +0, collection); FillChunkCollection(+1, +0, collection); + FillChunkCollection(-1, -1, collection); FillChunkCollection(+0, -1, collection); FillChunkCollection(+1, -1, collection); + + for (auto& entity : persistantEntities) + { + claimedTiles.set(entity.ChunkY * Chunk::ChunkSize + entity.ChunkX); + } + + auto [layer, nextLayer] = GetLayers(); + + auto currentLayer = layer; + for (int y{chunkBounds.get_end().y}; y >= chunkBounds.get_position().y; --y) + { + if (y < chunkBounds.get_center().y && nextLayer.is_valid()) currentLayer = nextLayer; + + for (int x{chunkBounds.get_position().x}; x < chunkBounds.get_end().x; ++x) + { + for (auto& arch : currentLayer->SpawningArchetypes) + { + for (auto& condition : arch->SpawnConditions) + { + if (!condition->IsValid(collection, Vector2i{x, y})) + { + goto NextArch; + } + } + for (auto& condition : arch->SpawnConditions) + { + claimedTilesBuffer.clear(); + condition->ClaimTiles(claimedTilesBuffer, collection, Vector2i{x, y}); + + for (auto tile : claimedTilesBuffer) + { + if (!chunkBounds.has_point(Vector2i{x, y}) || + claimedTiles[(chunkBounds.position.y - tile.y) * Chunk::ChunkSize + (chunkBounds.position.x - tile.x)]) + { + goto NextArch; + } + } + for (auto tile : claimedTilesBuffer) + { + claimedTiles.set((chunkBounds.position.y - tile.y) * Chunk::ChunkSize + (chunkBounds.position.x - tile.x)); + } + + ChunkGenerator::SpawnedEntities spawnInfo{}; + spawnInfo.Archetype = arch; + spawnInfo.SpawnPosition = Vector2i{x, y}; + spawnInfo.ClaimedPositions = claimedTilesBuffer; + + spawnedEntities.push_back(spawnInfo); + } + NextArch:; + } + } + } +} + +std::unique_ptr ChunkGenerator::CreateVisuals() +{ + auto visuals = std::make_unique(); + + for (int y{}; y < Chunk::ChunkSize; ++y) + { + for (int x{}; x < Chunk::ChunkSize; ++x) + { + auto tile = GetTile(x, y); + + auto& tileConfig = Settings->TileConfigs[tile.GetID()]; + auto& possibleTextures = tileConfig->PossibleTextures; + auto& possibleNeighbors = tileConfig->NeighborTransitions; + + if (possibleTextures.size() == 1) + { + (*visuals)[y * Chunk::ChunkSize + x] = CreatedVisualsTile{ possibleTextures[0]->AtlasX, possibleTextures[0]->AtlasY, possibleTextures[0]->AtlasIndex }; + } + else if (possibleTextures.size() > 1) + { + TextureWeight::GetTexture(possibleTextures, fastnoiselitestatic::SingleValue(Seed, x, y)); + } + } + } + + return visuals; +} + +void ChunkGenerator::CascadeShadows_Recursive(std::array& values, int posX, int posY, int value) +{ + if (InBounds(posX, posY)) + { + int index = posY * WorldNodeParameters::PaddedChunkSide + posX; + value += Settings->TileConfigs[(*TileArray)[index].GetID()]->LightResistance; + + if (value < values[index]) + { + values[index] = value; + + CascadeShadows_Recursive(values, posX + 1, posY, value); + CascadeShadows_Recursive(values, posX - 1, posY, value); + CascadeShadows_Recursive(values, posX, posY + 1, value); + CascadeShadows_Recursive(values, posX, posY - 1, value); + } + } +} + +std::unique_ptr ChunkGenerator::CascadeShadows() +{ + auto lightValues = std::make_unique>(); + auto lightValuesChunk = std::make_unique(); + + for (int i{}; i < WorldNodeParameters::PaddedChunkSize; ++i) + { + auto tile = (*TileArray)[i]; + auto tileConfig = Settings->TileConfigs[tile.GetID()]; + (*lightValues)[i] = tileConfig->LightResistance <= 0 ? tileConfig->LightResistance : std::numeric_limits::max(); + } + + for (int y{}; y < Chunk::ChunkSize; ++y) + { + for (int x{}; x < Chunk::ChunkSize; ++x) + { + CascadeShadows_Recursive(*lightValues, x, y, ); + } + } + + for (int y{}; y < Chunk::ChunkSize; ++y) + { + for (int x{}; x < Chunk::ChunkSize; ++x) + { + (*lightValuesChunk)[y * Chunk::ChunkSize + x] = lightValues[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)]; + } + } + + return std::move(lightValuesChunk); +} diff --git a/src/Core/WorldInstance.cpp b/src/Core/WorldInstance.cpp new file mode 100644 index 0000000..4425881 --- /dev/null +++ b/src/Core/WorldInstance.cpp @@ -0,0 +1,22 @@ +#include "Core/WorldInstance.h" + +#include "Types/Item.hpp" +#include "Components/Misc.hpp" +#include "Components/Resource.hpp" +#include "Components/Tick.hpp" + +WorldInstance::WorldInstance(const WorldConfig& worldConfig) +{ + RegisterTypes(EcsWorld); + + EcsWorld.set(worldConfig); +} + +void WorldInstance::RegisterTypes(flecs::world &world) +{ + Flecs_Misc(world); + Flecs_Item(world); + Flecs_Configs(world); + Flecs_Tick(world); + Flecs_Resource(world); +} diff --git a/src/Data/Archetype.cpp b/src/Data/Archetype.cpp new file mode 100644 index 0000000..05731b2 --- /dev/null +++ b/src/Data/Archetype.cpp @@ -0,0 +1,177 @@ +#include "Data/Archetype.h" +#include "Core/Chunk.h" + +// using namespace godot; + +// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + + +// void Archetype::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_scene"), &Archetype::GetScene); +// ClassDB::bind_method(D_METHOD("get_preview_scene"), &Archetype::GetPreviewScene); +// ClassDB::bind_method(D_METHOD("get_preview_texture"), &Archetype::GetPreviewTexture); +// ClassDB::bind_method(D_METHOD("get_occupied_tiles"), &Archetype::GetOccupiedTiles); +// ClassDB::bind_method(D_METHOD("get_upgrades"), &Archetype::GetUpgrades); + +// ClassDB::bind_method(D_METHOD("set_scene", "scene"), &Archetype::SetScene); +// ClassDB::bind_method(D_METHOD("set_preview_scene", "scene"), &Archetype::SetPreviewScene); +// ClassDB::bind_method(D_METHOD("set_preview_texture", "texture"), &Archetype::SetPreviewTetxture); +// ClassDB::bind_method(D_METHOD("set_occupied_tiles", "tiles"), &Archetype::SetOccupiedTiles); +// ClassDB::bind_method(D_METHOD("set_upgrades", "tiles"), &Archetype::SetUpgrades); + +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), "set_scene", "get_scene"); +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "preview_scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), "set_preview_scene", "get_preview_scene"); +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "preview_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_preview_texture", "get_preview_texture"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "occupied_tiles", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("OccupiedTile")), "set_occupied_tiles", "get_occupied_tiles"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "upgrades", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("UpgradeLevelConfig")), "set_upgrades", "get_upgrades"); +// } + +// void SpawnDescription::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_filter"), &SpawnDescription::GetFilter); +// ClassDB::bind_method(D_METHOD("get_tile"), &SpawnDescription::GetTile); + +// ClassDB::bind_method(D_METHOD("set_filter", "offset"), &SpawnDescription::SetFilter); +// ClassDB::bind_method(D_METHOD("set_tile", "tile"), &SpawnDescription::SetTile); + +// ADD_PROPERTY(PropertyInfo(Variant::INT, "filter", PROPERTY_HINT_ENUM, TileTypeEnumString), "set_offset", "get_offset"); +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "required_tile", PROPERTY_HINT_RESOURCE_TYPE, "TileConfig"), "set_tile", "get_tile"); +// } + +bool SpawnDescription::TryFilterTile(const ChunkCollection &chunks, int x, int y) +{ + auto tile = chunks.TryGetTile(x, y); + if (tile) return FilterTile(*tile); + return false; +} + +bool SpawnDescription::TryFilterTile(const ChunkCollection &chunks, Vector2i pos) +{ + return TryFilterTile(chunks, pos.x, pos.y); +} + +// void SpawnNearby::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_range"), &SpawnNearby::GetRange); + +// ClassDB::bind_method(D_METHOD("set_range", "range"), &SpawnNearby::SetRange); + +// ADD_PROPERTY(PropertyInfo(Variant::INT, "range"), "set_range", "get_range"); +// } + +bool SpawnNearby::IsValid(const ChunkCollection &chunk, Vector2i pos) +{ + for (int y{pos.y - Range}; y < pos.y + Range; ++y) + { + for (int x{pos.x - Range}; x < pos.x + Range; ++x) + { + if (TryFilterTile(chunk, x, y)) + return true; + } + } + return false; +} + +// void OccupiedTiles::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_offsets"), &OccupiedTiles::GetOffset); + +// ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &OccupiedTiles::SetOffset); + +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "offsets", PROPERTY_HINT_ARRAY_TYPE, "Vector2i"), "set_offsets", "get_offsets"); +// } + +bool InBounds(Vector2i pos) +{ + return pos.x >= 0 && pos.x < Chunk::ChunkSize && pos.y >= 0 && pos.y < Chunk::ChunkSize; +} + +bool OccupiedTiles::IsValid(const ChunkCollection &chunk, Vector2i pos) +{ + for (auto offset : Offsets) + { + if (!InBounds(pos + offset) || !TryFilterTile(chunk, pos + offset)) + return false; + } + return true; +} + +void OccupiedTiles::ClaimTiles(Vector &tiles, const ChunkCollection &chunk, Vector2i pos) +{ + for (auto offset : Offsets) + { + if (!tiles.has(pos + offset)) + tiles.push_back(pos + offset); + } +} + +void RequiredTiles::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_offsets"), &RequiredTiles::GetOffset); + + ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &RequiredTiles::SetOffset); + + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "offsets", PROPERTY_HINT_ARRAY_TYPE, "Vector2i"), "set_offsets", "get_offsets"); +} + +bool RequiredTiles::IsValid(const ChunkCollection &chunk, Vector2i pos) +{ + for (auto offset : Offsets) + { + if (InBounds(pos + offset) && TryFilterTile(chunk, pos + offset)) + return true; + } + return false; +} + +void LinkedTiles::_bind_methods() +{ + +} + +bool LinkedTiles::IsValid(const ChunkCollection &chunk, Vector2i pos) +{ + return true; +} + +void LinkedTiles::ClaimTiles_Recursive(Vector& tiles, const ChunkCollection& chunk, Vector2i pos) +{ + if (InBounds(pos) && TryFilterTile(chunk, pos) && !tiles.has(pos)) + { + tiles.push_back(pos); + + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, -1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +0)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, -1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +0)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, +1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, -1)); + } +} + +void LinkedTiles::ClaimTiles(Vector &tiles, const ChunkCollection &chunk, Vector2i pos) +{ + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, -1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +0)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, -1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +0)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, +1)); + ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, -1)); +} + +void PlaceableArchetype::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_archetype"), &PlaceableArchetype::GetArchetype); + ClassDB::bind_method(D_METHOD("get_place_costs"), &PlaceableArchetype::GetPlaceCosts); + + ClassDB::bind_method(D_METHOD("set_archetypes", "archetypes"), &PlaceableArchetype::SetArchetype); + ClassDB::bind_method(D_METHOD("set_place_costs", "costs"), &PlaceableArchetype::SetPlaceCosts); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "archetype", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), "set_archetype", "get_archetype"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "costs", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("ItemAmountConfig")), "set_place_costs", "get_place_costs"); +} diff --git a/src/Data/Item.cpp b/src/Data/Item.cpp new file mode 100644 index 0000000..964909c --- /dev/null +++ b/src/Data/Item.cpp @@ -0,0 +1,49 @@ +// #include "Data/Item.h" +// #include "Main/factory_server.h" + +// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + +// using namespace godot; + +// void ItemConfig::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_id"), &ItemConfig::GetID); + +// ClassDB::bind_method(D_METHOD("get_visual_name"), &ItemConfig::GetName); +// // ClassDB::bind_method(D_METHOD("is_fuel"), &ItemConfig::IsFuel); +// // ClassDB::bind_method(D_METHOD("is_fluid"), &ItemConfig::IsFluid); +// // ClassDB::bind_method(D_METHOD("is_ore"), &ItemConfig::IsOre); +// // ClassDB::bind_method(D_METHOD("is_plant"), &ItemConfig::IsPlant); +// // ClassDB::bind_method(D_METHOD("is_mob_drop"), &ItemConfig::IsMobDrop); +// // ClassDB::bind_method(D_METHOD("is_processed"), &ItemConfig::IsProcessed); + +// ClassDB::bind_method(D_METHOD("set_visual_name", "name"), &ItemConfig::SetName); +// // ClassDB::bind_method(D_METHOD("set_fuel", "val"), &ItemConfig::SetFuel); +// // ClassDB::bind_method(D_METHOD("set_fluid", "val"), &ItemConfig::SetFluid); +// // ClassDB::bind_method(D_METHOD("set_ore", "val"), &ItemConfig::SetOre); +// // ClassDB::bind_method(D_METHOD("set_plant", "val"), &ItemConfig::SetPlant); +// // ClassDB::bind_method(D_METHOD("set_mob_drop", "val"), &ItemConfig::SetMobDrop); +// // ClassDB::bind_method(D_METHOD("set_processed", "val"), &ItemConfig::SetProcessed); + +// ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "visual_name"), "set_visual_name", "get_visual_name"); +// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_fuel"), "set_fuel", "is_fuel"); +// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_fluid"), "set_fluid", "is_fluid"); +// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_ore"), "set_ore", "is_ore"); +// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_plant"), "set_plant", "is_plant"); +// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_mob_drop"), "set_mob_drop", "is_mob_drop"); +// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_processed"), "set_processed", "is_processed"); + +// } + +// void ItemAmountConfig::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_item"), &ItemAmountConfig::GetItem); +// ClassDB::bind_method(D_METHOD("get_amount"), &ItemAmountConfig::GetAmount); + +// ClassDB::bind_method(D_METHOD("set_item", "item"), &ItemAmountConfig::SetItem); +// ClassDB::bind_method(D_METHOD("set_amount", "amount"), &ItemAmountConfig::SetAmount); + +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemConfig"), "set_item", "get_item"); +// ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount"), "set_amount", "get_amount"); + +// } diff --git a/src/Data/Recipe.cpp b/src/Data/Recipe.cpp new file mode 100644 index 0000000..f287d91 --- /dev/null +++ b/src/Data/Recipe.cpp @@ -0,0 +1,51 @@ +#include "Data/Recipe.h" +#include "Main/factory_server.h" + +// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + +// using namespace godot; + +// void RecipeEntryConfig::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_item"), &RecipeEntryConfig::GetItem); +// ClassDB::bind_method(D_METHOD("get_amount"), &RecipeEntryConfig::GetAmount); + +// ClassDB::bind_method(D_METHOD("set_item", "item"), &RecipeEntryConfig::SetItem); +// ClassDB::bind_method(D_METHOD("set_amount", "amount"), &RecipeEntryConfig::SetAmount); + +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemConfig"), "set_item", "get_item"); +// ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount"); +// } + +// void RecipeConfig::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_ingredients"), &RecipeConfig::GetIngredients); +// ClassDB::bind_method(D_METHOD("get_results"), &RecipeConfig::GetResults); +// ClassDB::bind_method(D_METHOD("get_processing_time"), &RecipeConfig::GetProcessingTime); + +// ClassDB::bind_method(D_METHOD("set_ingredients", "item"), &RecipeConfig::SetIngredients); +// ClassDB::bind_method(D_METHOD("set_results", "amount"), &RecipeConfig::SetResults); +// ClassDB::bind_method(D_METHOD("set_processing_time", "time"), &RecipeConfig::SetProcessingTime); + +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ingredients", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("RecipeEntryConfig")), "set_ingredients", "get_ingredients"); +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "results", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("RecipeEntryConfig")), "set_results", "get_results"); +// ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "processing_time"), "set_processing_time", "get_processing_time"); +// } + +// Recipe::Recipe(const Ref& recipe) +// { +// if (recipe.is_null()) return; + +// RecipeMeta meta{}; +// meta.IngredientsAmount = recipe->Ingredients.size(); +// meta.ResultsAmount = recipe->Results.size(); +// meta.OriginalRecipe = recipe; +// meta.ProcessingTime = FactoryServer::SecondToTicks(recipe->ProcessingTime); + +// Data = SharedBuffer(meta.IngredientsAmount + meta.ResultsAmount, meta); + +// for (uint8_t i{}; i < meta.IngredientsAmount; ++i) +// Data[i] = recipe->Ingredients[i]->GetItemAmount(); +// for (uint8_t i{}; i < meta.ResultsAmount; ++i) +// Data[i + meta.IngredientsAmount] = recipe->Results[i]->GetItemAmount(); +// } diff --git a/src/Data/Tile.cpp b/src/Data/Tile.cpp new file mode 100644 index 0000000..a2741e5 --- /dev/null +++ b/src/Data/Tile.cpp @@ -0,0 +1,50 @@ +// #include "Data/Tile.h" + +// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) +// using namespace godot; + +// void TileConfig::_bind_methods() +// { +// BIND_ENUM_CONSTANT(TILE_AIR); +// BIND_ENUM_CONSTANT(TILE_FILLER); +// BIND_ENUM_CONSTANT(TILE_LIQUID); +// BIND_ENUM_CONSTANT(TILE_ORE); +// BIND_ENUM_CONSTANT(TILE_PLANT); +// BIND_ENUM_CONSTANT(TILE_NPC); + +// ClassDB::bind_method(D_METHOD("get_textures"), &TileConfig::GetTextures); +// ClassDB::bind_method(D_METHOD("get_transitions"), &TileConfig::GetTransitions); +// ClassDB::bind_method(D_METHOD("get_type"), &TileConfig::GetType); + +// ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TileConfig::SetTextures); +// ClassDB::bind_method(D_METHOD("set_transitions", "transitions"), &TileConfig::SetTransitions); +// ClassDB::bind_method(D_METHOD("set_type", "type"), &TileConfig::SetType); + +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("TextureWeight")), "set_textures", "get_textures"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("TileTransitionConfig")), "set_transitions", "get_transitions"); +// ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Air,Filler,Liquid,Ore,Npc,Plant"), "set_type", "get_type"); +// } + +// void TileTransitionConfig::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_neighbor"), &TileTransitionConfig::GetNeighbor); +// ClassDB::bind_method(D_METHOD("get_possible_textures"), &TileTransitionConfig::GetPossibleTextures); + +// ClassDB::bind_method(D_METHOD("set_neighbor", "neighbor"), &TileTransitionConfig::SetNeighbor); +// ClassDB::bind_method(D_METHOD("set_possible_textures", "textures"), &TileTransitionConfig::SetPossibleTextures); + +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "neighbor", PROPERTY_HINT_RESOURCE_TYPE, "TileConfig"), "set_neighbor", "get_neighbor"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "possible_textures", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("TextureWeight")), "set_possible_textures", "get_possible_textures"); +// } + +// void TextureWeight::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_texture"), &TextureWeight::GetTexture); +// ClassDB::bind_method(D_METHOD("get_weight"), &TextureWeight::GetWeight); + +// ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TextureWeight::SetTexture); +// ClassDB::bind_method(D_METHOD("set_weight", "weight"), &TextureWeight::SetWeight); + +// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); +// ADD_PROPERTY(PropertyInfo(Variant::INT, "weight"), "set_weight", "get_weight"); +// } \ No newline at end of file diff --git a/src/Data/UpgradeLevel.cpp b/src/Data/UpgradeLevel.cpp new file mode 100644 index 0000000..9747333 --- /dev/null +++ b/src/Data/UpgradeLevel.cpp @@ -0,0 +1,17 @@ +// #include "Data/UpgradeLevel.h" + +// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + +// using namespace godot; + +// void UpgradeLevelConfig::_bind_methods() +// { +// ClassDB::bind_method(D_METHOD("get_costs"), &UpgradeLevelConfig::GetUpgradeCosts); +// ClassDB::bind_method(D_METHOD("get_results"), &UpgradeLevelConfig::GetUpgradeResults); + +// ClassDB::bind_method(D_METHOD("set_costs", "costs"), &UpgradeLevelConfig::SetUpgradeCosts); +// ClassDB::bind_method(D_METHOD("set_results", "results"), &UpgradeLevelConfig::SetUpgradeResults); + +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "recipe_cost", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("ItemAmountConfig")), "set_costs", "get_costs"); +// ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "results"), "set_results", "get_results"); +// } diff --git a/src/Data/WorldGraph/WorldGraph.cpp b/src/Data/WorldGraph/WorldGraph.cpp new file mode 100644 index 0000000..8687fd0 --- /dev/null +++ b/src/Data/WorldGraph/WorldGraph.cpp @@ -0,0 +1,139 @@ +#include "Data/WorldGraph/WorldGraph.h" + +WorldGraph::WorldGraph(const Vector>& nodes) +{ + Compile(nodes); +} + +WorldGraph::WorldGraph(const WorldGraph &other) +{ + MemorySize = other.MemorySize; + CompiledData = other.CopyMemory(NodeMap); +} + +WorldGraph &WorldGraph::operator=(const WorldGraph &other) +{ + MemorySize = other.MemorySize; + CompiledData = other.CopyMemory(NodeMap); + return *this; +} + +Variant WorldGraph::Execute(Ref node, const WorldNodeParameters ¶ms) const +{ + auto nodePtr = GetNode(node); + if (nodePtr) + { + return nodePtr->Evaluate(params); + } + return {}; +} + +WorldNodeBase *WorldGraph::GetNode(Ref node) const +{ + auto it = NodeMap.find(node); + if (it != NodeMap.end()) + { + return it->value; + } + return nullptr; +} + +void AddAllNodes(HashSet>& allNodes, Ref node) +{ + allNodes.insert(node); + for (auto& input : node->InputNodes) + { + if (input.is_valid()) + { + AddAllNodes(allNodes, input); + } + } +} + +void WorldGraph::Compile(const Vector>& nodes) +{ + NodeMap.clear(); + + HashSet> allNodes{}; + HashMap, int> nodeMap{}; + HashMap linker{}; + + // collect all nodes + for (auto& node : nodes) + AddAllNodes(allNodes, node); + + WorldGraphSizeMeasurer sizeMeasurer{}; + for (auto& node : allNodes) + { + if (node.is_null() || !node->GetInternalNode()) + throw std::runtime_error("graph is invalid"); + + // refresh node values + node->RefreshInputs(); + node->RefreshValues(); + + // check if nodes are valid + if (!node->IsValid()) + throw std::runtime_error("graph is invalid"); + + // find the size of the total compiled program + node->GetInternalNode()->Allocate(&sizeMeasurer); + } + DEV_ASSERT(sizeMeasurer.TotalSize % 8 == 0); + + // allocate the nodes + WorldGraphAllocator allocator{ sizeMeasurer.TotalSize }; + for (auto& node : allNodes) + { + void* allocatedNodeAddress = allocator.GetCurrentAddress(); + + node->GetInternalNode()->Allocate(&allocator); + + NodeMap.insert(node, static_cast(allocatedNodeAddress)); + linker.insert(node->GetInternalNode(), static_cast(allocatedNodeAddress)); + } + + // get the compiled memory + std::unique_ptr compiledMemory = std::move(reinterpret_cast&>(allocator.Data)); + + // link the nodes + MemorySize = sizeMeasurer.TotalSize / 8; + for (int i{}; i < MemorySize; ++i) + { + auto it = linker.find(compiledMemory[i]); + if (it != linker.end()) + { + compiledMemory[i] = it->value; + } + } + + CompiledData = std::move(compiledMemory); +} + +std::unique_ptr WorldGraph::CopyMemory(HashMap, WorldNodeBase *> &nodeMap) const +{ + auto memory = std::make_unique(MemorySize); + + int64_t memoryDifference = memory.get() - CompiledData.get(); + + nodeMap = NodeMap; + for (auto& node : nodeMap) + { + node.value += memoryDifference; + } + + WorldNodeBase* memoryStart = reinterpret_cast(CompiledData.get()); + WorldNodeBase* memoryEnd = reinterpret_cast(CompiledData.get() + MemorySize); + + for (int i{}; i < MemorySize; ++i) + { + auto& address = memory[i]; + // if the address points somewhere that is in the old memory address, move it to the new memory address + if (address >= memoryStart && address < memoryEnd) + { + address += memoryDifference; + } + } + + return std::move(memory); +} diff --git a/src/Data/WorldGraph/WorldGraphVisualNode.cpp b/src/Data/WorldGraph/WorldGraphVisualNode.cpp new file mode 100644 index 0000000..5ee7801 --- /dev/null +++ b/src/Data/WorldGraph/WorldGraphVisualNode.cpp @@ -0,0 +1,338 @@ +#include "Data/WorldGraph/WorldGraphVisualNode.h" + +void WorldGraphVisualNodeBase::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_position"), &WorldGraphVisualNodeBase::GetPosition); + ClassDB::bind_method(D_METHOD("get_inputs"), &WorldGraphVisualNodeBase::GetInputs); + + ClassDB::bind_method(D_METHOD("set_position", "position"), &WorldGraphVisualNodeBase::SetPosition); + ClassDB::bind_method(D_METHOD("set_inputs", "input"), &WorldGraphVisualNodeBase::SetInputNodes); + + ClassDB::bind_method(D_METHOD("is_valid"), &WorldGraphVisualNodeBase::NodeIsValid); + ClassDB::bind_method(D_METHOD("get_input_types"), &WorldGraphVisualNodeBase::NodeGetInputTypes); + ClassDB::bind_method(D_METHOD("get_output_type"), &WorldGraphVisualNodeBase::NodeGetOutputType); + //ClassDB::bind_method(D_METHOD("set_default"), &WorldGraphVisualNodeBase::NodeSetDefault); + ClassDB::bind_method(D_METHOD("has_internal_node"), &WorldGraphVisualNodeBase::HasInternalNode); + ClassDB::bind_method(D_METHOD("set_input", "index", "node"), &WorldGraphVisualNodeBase::NodeSetInput); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position"), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "inputs", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("WorldGraphVisualNodeBase")), "set_inputs", "get_inputs"); +} + +bool CanExecuteNode_Recursive(Ref node) +{ + for (auto& node : node->InputNodes) + { + for (auto input : node->InputNodes) + { + if (input.is_null()) return false; + CanExecuteNode_Recursive(input); + } + } + return true; +} + +bool WorldGraphVisualNodeBase::CanExecuteNode() +{ + return CanExecuteNode_Recursive(Ref(this)); +} + +void WorldGraphVisualNodeBase::SetInternalNode(std::unique_ptr &&node) +{ + InternalNode = std::move(node); + + int inputAmounts = InternalNode->GetInputTypes().size(); + if (inputAmounts > InputNodes.size()) + { + InputNodes.resize(inputAmounts); + } + + RefreshInputs(); + RefreshValues(); +} + +void WorldGraphVisualNodeBase::RefreshInputs() +{ + if (InternalNode) + { + auto internalNodeInputs = InternalNode->GetInputTypes().size(); + for (int i{}; i < InputNodes.size() && i < internalNodeInputs; ++i) + SetInput(i, InputNodes[i]); + } +} + +struct NodeGenerator +{ + NodeGenerator() = default; + NodeGenerator(const char* name, std::function()>&& generator) : Name{ name }, Generator{ std::move(generator) } {}; + + const char* Name{}; + std::function()> Generator{}; +}; + +static const NodeGenerator MathGenerators[] = +{ + { "Abs", [](){ return std::make_unique(); }}, + { "Add", [](){ return std::make_unique(); }}, + { "And", [](){ return std::make_unique(); }}, + { "Ceil", [](){ return std::make_unique(); }}, + { "Clamp", [](){ return std::make_unique(); }}, + { "Cos", [](){ return std::make_unique(); }}, + { "Divide", [](){ return std::make_unique(); }}, + { "Equal", [](){ return std::make_unique(); }}, + { "Exp", [](){ return std::make_unique(); }}, + { "Floor", [](){ return std::make_unique(); }}, + { "Greater", [](){ return std::make_unique(); }}, + { "Greater or Equal", [](){ return std::make_unique(); }}, + { "Lerp", [](){ return std::make_unique(); }}, + { "Log", [](){ return std::make_unique(); }}, + { "Max", [](){ return std::make_unique(); }}, + { "Min", [](){ return std::make_unique(); }}, + { "Modulo", [](){ return std::make_unique(); }}, + { "Multiply", [](){ return std::make_unique(); }}, + { "Negate", [](){ return std::make_unique(); }}, + { "One Minus", [](){ return std::make_unique(); }}, + { "Or", [](){ return std::make_unique(); }}, + { "Pow", [](){ return std::make_unique(); }}, + { "Round", [](){ return std::make_unique(); }}, + { "Sin", [](){ return std::make_unique(); }}, + { "Smaller", [](){ return std::make_unique(); }}, + { "Smaller or Equal", [](){ return std::make_unique(); }}, + { "Square", [](){ return std::make_unique(); }}, + { "Substract", [](){ return std::make_unique(); }}, + { "Tan", [](){ return std::make_unique(); }}, +}; +static constexpr int MathArraySize = sizeof(MathGenerators) / sizeof(NodeGenerator); + +void WorldGraphVisualNode_Math::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_node"), &WorldGraphVisualNode_Math::GetNode); + ClassDB::bind_method(D_METHOD("get_node_names"), &WorldGraphVisualNode_Math::GetNodeNames); + + ClassDB::bind_method(D_METHOD("set_node", "name"), &WorldGraphVisualNode_Math::SetNode); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "node_id"), "set_node", "get_node"); +} + +WorldGraphVisualNode_Math::WorldGraphVisualNode_Math() +{ + NodeID = "Add"; + SetInternalNode(std::make_unique()); +} + +TypedArray WorldGraphVisualNode_Math::GetNodeNames() const +{ + TypedArray Values{}; + Values.resize(MathArraySize); + + for (int i{}; i < MathArraySize; ++i) + { + Values[i] = String(MathGenerators[i].Name); + } + + return Values; +} + +void WorldGraphVisualNode_Math::SetNode(String nodeName) +{ + if (nodeName == NodeID) return; + + for (int i{}; i < MathArraySize; ++i) + { + if (MathGenerators[i].Name == nodeName) + { + SetInternalNode(MathGenerators[i].Generator()); + NodeID = nodeName; + } + } +} + +void WorldGraphVisualNode_Constant::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_value"), &WorldGraphVisualNode_Constant::GetValue); + + ClassDB::bind_method(D_METHOD("set_value", "val"), &WorldGraphVisualNode_Constant::SetValue); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "value"), "set_value", "get_value"); +} + +WorldGraphVisualNode_Constant::WorldGraphVisualNode_Constant() +{ + SetInternalNode(std::make_unique()); +} + +void WorldGraphVisualNode_Constant::SetValue(float val) +{ + static_cast(GetInternalNode())->Value = val; +} + +float WorldGraphVisualNode_Constant::GetValue() const +{ + return static_cast(GetInternalNode())->Value; +} + +static const NodeGenerator NoiseGenerators[] = +{ + { "Simplex", [](){ return std::make_unique(); }}, + { "Open Simplex", [](){ return std::make_unique(); }}, + { "Perlin", [](){ return std::make_unique(); }}, + { "Value", [](){ return std::make_unique(); }}, + { "Value Cubic", [](){ return std::make_unique(); }}, +}; +static constexpr int NoiseArraySize = sizeof(NoiseGenerators) / sizeof(NodeGenerator); + +WorldGraphVisualNode_Noise::WorldGraphVisualNode_Noise() +{ + NoiseType = "Simplex"; + SetInternalNode(std::make_unique()); +} + +TypedArray WorldGraphVisualNode_Noise::GetNoiseTypes() const +{ + TypedArray Values{}; + Values.resize(NoiseArraySize); + + for (int i{}; i < NoiseArraySize; ++i) + { + Values[i] = String(NoiseGenerators[i].Name); + } + + return Values; +} + +void WorldGraphVisualNode_Noise::SetFrequency(float val) +{ + Frequency = val; + if (val != 0) + { + static_cast(GetInternalNode())->Frequency = val; + } +} + +void WorldGraphVisualNode_Noise::RefreshValues() +{ + static_cast(GetInternalNode())->Frequency = Frequency; +} + +void WorldGraphVisualNode_Noise::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_noise_type"), &WorldGraphVisualNode_Noise::GetNoiseType); + ClassDB::bind_method(D_METHOD("get_all_noise_types"), &WorldGraphVisualNode_Noise::GetNoiseTypes); + ClassDB::bind_method(D_METHOD("get_frequency"), &WorldGraphVisualNode_Noise::GetFrequency); + + ClassDB::bind_method(D_METHOD("set_noise_type", "type"), &WorldGraphVisualNode_Noise::SetNoiseType); + ClassDB::bind_method(D_METHOD("set_frequency", "frequency"), &WorldGraphVisualNode_Noise::SetFrequency); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "noise_type"), "set_noise_type", "get_noise_type"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency"), "set_frequency", "get_frequency"); +} + +void WorldGraphVisualNode_Noise::SetNoiseType(String noiseType) { + if (noiseType == NoiseType) return; + + for (int i{}; i < NoiseArraySize; ++i) + { + if (NoiseGenerators[i].Name == noiseType) + { + SetInternalNode(NoiseGenerators[i].Generator()); + NoiseType = noiseType; + } + } +} + +// float WorldGraphVisualNode_Noise::GetFrequency() const +// { +// return static_cast(GetInternalNode())->Frequency; +// } + +WorldGraphVisualNode_If::WorldGraphVisualNode_If() +{ + SetInternalNode(std::make_unique()); +} + +void WorldGraphVisualNode_Tile::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_type"), &WorldGraphVisualNode_Tile::GetType); + ClassDB::bind_method(D_METHOD("get_relative_x"), &WorldGraphVisualNode_Tile::GetRelativeX); + ClassDB::bind_method(D_METHOD("get_relative_y"), &WorldGraphVisualNode_Tile::GetRelativeY); + + ClassDB::bind_method(D_METHOD("set_type", "type"), &WorldGraphVisualNode_Tile::SetType); + ClassDB::bind_method(D_METHOD("set_relative_x", "offset"), &WorldGraphVisualNode_Tile::SetRelativeX); + ClassDB::bind_method(D_METHOD("set_relative_y", "offset"), &WorldGraphVisualNode_Tile::SetRelativeY); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Air,Filler,Liquid,Ore,Npc,Plant"), "set_type", "get_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_x"), "set_relative_x", "get_relative_x"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_y"), "set_relative_y", "get_relative_y"); +} + +WorldGraphVisualNode_Tile::WorldGraphVisualNode_Tile() +{ + SetInternalNode(std::make_unique()); +} + +void WorldGraphVisualNode_Tile::SetType(int type) +{ + static_cast(GetInternalNode())->TileType = static_cast(std::clamp(type, 0, TILE_TYPE::TILE_MAX)); +} + +void WorldGraphVisualNode_Tile::SetRelativeX(int offset) +{ + static_cast(GetInternalNode())->RelativeX = static_cast(std::clamp(offset, -WorldNodeParameters::MaxQueryOffset, WorldNodeParameters::MaxQueryOffset)); +} + +void WorldGraphVisualNode_Tile::SetRelativeY(int offset) +{ + static_cast(GetInternalNode())->RelativeY = static_cast(std::clamp(offset, -WorldNodeParameters::MaxQueryOffset, WorldNodeParameters::MaxQueryOffset)); +} + +int WorldGraphVisualNode_Tile::GetType() const +{ + return static_cast(GetInternalNode())->TileType; +} + +int WorldGraphVisualNode_Tile::GetRelativeX() const +{ + return static_cast(GetInternalNode())->RelativeX; +} + +int WorldGraphVisualNode_Tile::GetRelativeY() const +{ + return static_cast(GetInternalNode())->RelativeY; +} + +void WorldGraphVisualNode_TileDistance::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_type"), &WorldGraphVisualNode_TileDistance::GetType); + ClassDB::bind_method(D_METHOD("get_range"), &WorldGraphVisualNode_TileDistance::GetRange); + + ClassDB::bind_method(D_METHOD("set_type", "type"), &WorldGraphVisualNode_TileDistance::SetType); + ClassDB::bind_method(D_METHOD("set_range", "range"), &WorldGraphVisualNode_TileDistance::SetRange); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Air,Filler,Liquid,Ore,Npc,Plant"), "set_type", "get_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "range", PROPERTY_HINT_RANGE, "1,3"), "set_range", "get_range"); +} + +WorldGraphVisualNode_TileDistance::WorldGraphVisualNode_TileDistance() +{ + SetInternalNode(std::make_unique()); +} + +void WorldGraphVisualNode_TileDistance::SetType(int type) +{ + static_cast(GetInternalNode())->TileType = static_cast(type); +} + +void WorldGraphVisualNode_TileDistance::SetRange(int range) +{ + static_cast(GetInternalNode())->Range = range; +} + +int WorldGraphVisualNode_TileDistance::GetType() const +{ + return static_cast(GetInternalNode())->TileType; +} + +int WorldGraphVisualNode_TileDistance::GetRange() const +{ + return static_cast(GetInternalNode())->Range; +} diff --git a/src/Data/WorldSettings.cpp b/src/Data/WorldSettings.cpp new file mode 100644 index 0000000..efddf3e --- /dev/null +++ b/src/Data/WorldSettings.cpp @@ -0,0 +1,652 @@ +#include "Data/WorldSettings.h" +#include "Util/Helpers.h" +#include "core/io/resource_saver.h" +#include "core/config/project_settings.h" +#include "servers/rendering_server.h" +#include +#include "Core/Chunk.h" +#include "Data/LayerConfigs.h" +#include "Data/Item.h" +#include "Core/WorldGenerator.h" +#include "Util/RandomPicker.h" + +// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + +// using namespace godot; + +// void FactoryWorldSettings::_bind_methods() +// { +// std::function test{ std::divides() }; +// ClassDB::bind_method(D_METHOD("get_recipes"), &FactoryWorldSettings::GetRecipes); +// ClassDB::bind_method(D_METHOD("get_archetypes"), &FactoryWorldSettings::GetPlaceableArchetypes); +// ClassDB::bind_method(D_METHOD("get_tiles"), &FactoryWorldSettings::GetTileConfigs); +// ClassDB::bind_method(D_METHOD("get_layers"), &FactoryWorldSettings::GetLayerConfigs); + +// ClassDB::bind_method(D_METHOD("set_recipes", "recipes"), &FactoryWorldSettings::SetRecipes); +// ClassDB::bind_method(D_METHOD("set_archetypes", "archetypes"), &FactoryWorldSettings::SetPlaceableArchetypes); +// ClassDB::bind_method(D_METHOD("set_tiles", "tiles"), &FactoryWorldSettings::SetTileConfigs); +// ClassDB::bind_method(D_METHOD("set_layers", "tiles"), &FactoryWorldSettings::SetLayerConfigs); + +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "recipes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("RecipeConfig")), "set_recipes", "get_recipes"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "archetypes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("PlaceableArchetype")), "set_archetypes", "get_archetypes"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "tiles", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("TileConfig")), "set_tiles", "get_tiles"); +// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "layers", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("LayerConfig")), "set_layers", "get_layers"); +// } + +template +void AddUnique(Vector& target, const Vector& source) +{ + for (auto& src : source) + { + for (auto& tar : target) + { + if (src == tar || src.is_null()) + continue; + } + target.push_back(src); + } +} + +void FactoryWorldSettings::Merge(Ref settings) +{ + // merge layers + AddUnique(LayerConfigs, settings->LayerConfigs); + + AddUnique(TileConfigs, settings->TileConfigs); + + AddUnique(Recipes, settings->Recipes); + AddUnique(Archetypes, settings->Archetypes); + //AddUnique(Placeables, settings->Placeables); + AddUnique(Items, settings->Items); + + Initialize(); +} + +void GetSubResources(Resource* resource, List& buffer, const std::function& callback); + +bool CheckVariant(Variant var, List& buffer, const std::function& callback) +{ + if (var.get_type() == Variant::Type::OBJECT) + { + auto resourceVar = Object::cast_to(var); + if (resourceVar) + { + callback(resourceVar); + + GetSubResources(resourceVar, buffer, callback); + + return true; + } + } + return false; +} + +void GetSubResources(Resource* resource, List& buffer, const std::function& callback) +{ + resource->get_property_list(&buffer); + + while (!buffer.is_empty()) + { + auto property = buffer.front(); + auto className = property->get().class_name; + StringName propertyName = property->get().name; + auto getterName = ClassDB::get_property_getter(className, propertyName); + auto propertyVal = resource->call(getterName); + + if (CheckVariant(propertyVal, buffer, callback)) + { + auto packedScene = Object::cast_to(propertyVal); + if (packedScene) + { + auto state = packedScene->get_state(); + for (int i{}; i < state->get_node_count(); ++i) + for (int j{}; j < state->get_node_property_count(i); ++j) + { + CheckVariant(state->get_node_property_value(i, j), buffer, callback); + } + } + + if (propertyVal.is_array()) + { + Array propertyArray = propertyVal; + for (auto entry : propertyArray) + { + CheckVariant(entry, buffer, callback); + } + } + } + + buffer.pop_front(); + } +} + +void AddTextureToSheets(HashMap>> sheets, Ref texture) +{ + if (texture.is_null()) return; + + DEV_ASSERT(texture->get_width() == texture->get_height()); + if (!sheets.has(texture->get_width())) + { + // Ref tileSet{}; + // tileSet.instantiate(); + // tileSet->set_texture_region_size(texture->get_size()); + // sheets[texture->get_width()] = tileSet; + + sheets.insert(texture->get_width(), {}); + } + + auto& sheet = sheets[texture->get_width()]; + + sheet.push_back(texture); +} + +void FactoryWorldSettings::Initialize() +{ + InitializeResources(); + InitializeLayers(); + InitializeWorldGenerators(); + InitializeTexturesSheets(); + InitializeItemGraph(); +} + +int FactoryWorldSettings::GetStartHeight() const +{ + return Chunk::ChunkSize * LayerConfigs[0]->StartChunk; +} + +Vector FactoryWorldSettings::GetChunkUnlockCosts(int x, int y) const +{ + return GetChunkUnlockCosts(ChunkKey{x, y}); +} + +constexpr double Square(double x) +{ + return x * x; +} + +constexpr double BellCurve(double x, double mean = 0, double deviation = 0.4) +{ + constexpr double Sqrt2Pi = 2.506628274631000502415765284811; + return (1/(deviation * Sqrt2Pi)) * exp(-0.5 * Square((x - mean) / deviation)); +} + +double GetRandomDouble(uint64_t& val) +{ + double randomVal{ reinterpret_cast(val) }; + while (isnan(randomVal) || std::isnormal(randomVal)) + { + val = std::hash{}(val); + randomVal = reinterpret_cast(val); + } + return randomVal; +} + +Vector FactoryWorldSettings::GetChunkUnlockCosts(ChunkKey chunk) const +{ + HashMap,int> LayerCounts{}; + + for (int y{ LayerConfigs[0]->StartChunk }; y > chunk.Y; --y) + { + auto key = chunk; + key.Y = y; + ++LayerCounts[GetLayer(key)]; + } + + for (int x{ }; x < std::abs(chunk.X); ++x) + { + auto key = chunk; + key.X = chunk.X < 0 ? -x : x; + ++LayerCounts[GetLayer(key)]; + } + + std::vector::Entry> possibleItems{}; + + possibleItems.push_back(RandomPickerD::Entry{ nullptr, BellCurve(0, chunk.X + chunk.Y) }); + possibleItems.push_back(RandomPickerD::Entry{ nullptr, BellCurve(0, chunk.X + chunk.Y) }); + for (auto [layer, count] : LayerCounts) + { + int maxComplexity{}; + for (auto item : layer->UnlockedItems) + { + maxComplexity = std::max(maxComplexity, ItemComplexity[item]); + } + + for (auto item : layer->UnlockedItems) + { + auto complexity = ItemComplexity[item]; + possibleItems.push_back(RandomPickerD::Entry{ item.ptr(), BellCurve(complexity, std::min(count / 3, maxComplexity)) * count }); + } + } + + RandomPickerD itemPicker{ possibleItems }; + + uint64_t chunkHash = chunk.hash64(); + + return Vector + { + itemPicker.GetAndRemoveRandom(GetRandomDouble(chunkHash)), + itemPicker.GetAndRemoveRandom(GetRandomDouble(chunkHash)), + itemPicker.GetAndRemoveRandom(GetRandomDouble(chunkHash)) + }; +} + +Ref FactoryWorldSettings::GetLayer(ChunkKey chunk) const +{ + for (auto layer : LayerConfigs) + { + if (layer->StartChunk <= chunk.Y) + return layer; + } +} + +void FactoryWorldSettings::InitializeResources() +{ + List buffer{}; + + struct MetaData + { + RecipeConfig* CurrentRecipe{}; + Archetype* CurrentArchetype{}; + LayerConfig* CurrentLayer{}; + }; + MetaData data{}; + + std::function resourceChecker = [this, &data](Resource* res) + { + { + auto item = Object::cast_to(res); + if (item && item->GetID() == -1) + { + Items.push_back(item); + item->Item.ItemID = static_cast(Items.size() - 1); + } + if (data.CurrentLayer) + { + data.CurrentLayer->UnlockedItems.push_back_unique(item); + } + } + { + auto tile = Object::cast_to(res); + if (tile && !TileConfigs.has(tile)) + { + TileConfigs.push_back(tile); + tile->TileData.SetID(static_cast(TileConfigs.size() - 1)); + } + } + { + auto recipe = Object::cast_to(res); + if (recipe) + { + data.CurrentRecipe = recipe; + if (!Recipes.has(recipe)) + { + Recipes.push_back(recipe); + recipe->SetID(Recipes.size() - 1); + } + if (data.CurrentArchetype) + { + recipe->RecipeSources.push_back_unique(data.CurrentArchetype); + } + if (data.CurrentLayer) + { + data.CurrentLayer->UnlockedRecipes.push_back_unique(recipe); + for (auto result : recipe->Results) + data.CurrentLayer->UnlockedItems.push_back_unique(result); + } + } + } + { + auto archetype = Object::cast_to(res); + if (archetype && !Archetypes.has(archetype)) + { + data.CurrentArchetype = archetype; + data.CurrentRecipe = nullptr; + Archetypes.push_back(archetype); + archetype->SetID(Archetypes.size() - 1); + } + if (data.CurrentLayer) + { + data.CurrentLayer->UnlockedBuildings.push_back_unique(archetype); + } + } + { + auto tile = Object::cast_to(res); + if (tile) + { + TileConfigs.push_back_unique(tile); + } + } + }; + + for (int i{}; i < LayerConfigs.size(); ++i) + { + data.CurrentLayer = LayerConfigs[i].ptr(); + data.CurrentArchetype = nullptr; + data.CurrentRecipe = nullptr; + GetSubResources(LayerConfigs[i].ptr(), buffer, resourceChecker); + } + data.CurrentLayer = nullptr; + data.CurrentArchetype = nullptr; + data.CurrentRecipe = nullptr; + + for (int i{}; i < PlaceableArchetypes.size(); ++i) + { + data.CurrentArchetype = PlaceableArchetypes[i]->Archetype.ptr(); + GetSubResources(PlaceableArchetypes[i].ptr(), buffer, resourceChecker); + } +} + +void FactoryWorldSettings::InitializeLayers() +{ + // sort Layers + struct LayerSorter + { + bool operator()(const Ref& lhs, const Ref& rhs) const + { + return lhs->StartChunk > rhs->StartChunk; + } + }; + LayerConfigs.sort_custom(); + + struct TileSorter + { + bool operator()(const Ref& lhs, const Ref& rhs) const + { + return lhs->Tile->GetType() < rhs->Tile->GetType(); + } + }; + for (auto& layer : LayerConfigs) + { + layer->Tiles.sort_custom(); + } +} + +void FactoryWorldSettings::InitializeWorldGenerators() +{ + // Compile World Generator + Vector> graphs; + for (auto& layer : LayerConfigs) + for (auto& tileGenerator : layer->Tiles) + if (!LayerConfigs.has(tileGenerator->TileGenerator)) + LayerConfigs.push_back(tileGenerator->TileGenerator); + + WorldGenerator = WorldGraph{ graphs }; +} + +void FactoryWorldSettings::InitializeTexturesSheets() +{ + // Gather all textures + TileSet = {}; + HashMap>> Sheets{}; + + for (auto tile : TileConfigs) + { + for (auto texture : tile->PossibleTextures) + AddTextureToSheets(Sheets, texture); + + for (auto transition : tile->NeighborTransitions) + for (auto texture : transition->PossibleTextures) + AddTextureToSheets(Sheets, texture); + } + + // Make sheets + for (auto& [size, textures] : Sheets) + { + constexpr int32_t MaxTextureSizeBits = 12; // 4096 + const int32_t individualTextureSizeBits = (int32_t)std::ceil(std::log2(size)); + const int32_t textureAmountBits = ((int32_t)std::ceil(std::log2(textures.size())) + 1) / 2; + + const int32_t SheetsAmountBits = std::max(0, individualTextureSizeBits + textureAmountBits - MaxTextureSizeBits); + const int32_t SheetDimensionBits = std::max(0, individualTextureSizeBits + textureAmountBits); + + const int32_t SheetsAmount = 1 << SheetsAmountBits; + const int32_t SheetDimensions = 1 << SheetDimensionBits; + const int32_t TexturesPerSheet = 1 << (textureAmountBits * 2); + const int32_t TexturesPerRow = 1 << textureAmountBits; + + for (int sheetIndex{}; sheetIndex < SheetsAmount; ++sheetIndex) + { + Ref sheet{}; + sheet.instantiate(); + sheet->initialize_data(SheetDimensions, SheetDimensions, true, Image::FORMAT_RGBA8); + for (int i{sheetIndex * TexturesPerSheet}; i < textures.size() && i < (sheetIndex + 1) * TexturesPerSheet; ++i) + { + int x = textures[i]->AtlasX = i % TexturesPerRow; + int y = textures[i]->AtlasY = (i / TexturesPerRow) % TexturesPerRow; + textures[i]->AtlasIndex = TileSet->get_source_count(); + + auto texture = textures[i]->Texture; + Image sourceImage = Image{}; + sheet->blit_rect(texture, Rect2i{Vector2i{}, texture->get_size()}, Vector2i{x, y}); + } + + Ref tileSetSource{}; + tileSetSource.instantiate(); + tileSetSource->set_texture_region_size(Vector2i{size, size}); + tileSetSource->set_texture(ImageTexture::create_from_image(sheet)); + + TileSet->add_source(tileSetSource); + } + } +} + +Ref IsItemFromRecipe(Ref layer, Ref item) +{ + for (auto recipe : layer->UnlockedRecipes) + { + for (auto result : recipe->Results) + { + if (result->Item == item) + { + return recipe; + } + } + } + return {}; +} + +void FactoryWorldSettings::InitializeItemGraph() +{ + for (auto layer : LayerConfigs) + { + for (bool repeat{}; repeat;) + { + for (auto item : layer->UnlockedItems) + { + auto recipe = IsItemFromRecipe(layer, item); + if (recipe.is_valid()) + { + int maxComplexity{}; + for (auto ingredient : recipe->Ingredients) + { + if (ItemComplexity.has(ingredient)) + { + maxComplexity = std::max(maxComplexity, ItemComplexity[ingredient]); + } + else + { + repeat = true; + } + } + ItemComplexity[item] = maxComplexity; + } + else + { + ItemComplexity[item] = 0; + } + } + } + } + +} + +#define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + +void LayerConfig::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_start_chunk"), &LayerConfig::GetStartChunk); + ClassDB::bind_method(D_METHOD("get_tiles"), &LayerConfig::GetTiles); + ClassDB::bind_method(D_METHOD("get_registered_nodes"), &LayerConfig::GetRegisteredNodes); + ClassDB::bind_method(D_METHOD("get_archetypes"), &LayerConfig::GetArchetypes); + + ClassDB::bind_method(D_METHOD("set_start_chunk", "height"), &LayerConfig::SetStartChunk); + ClassDB::bind_method(D_METHOD("set_tiles", "tiles"), &LayerConfig::SetTiles); + ClassDB::bind_method(D_METHOD("set_registered_nodes", "nodes"), &LayerConfig::SetRegisteredNodes); + ClassDB::bind_method(D_METHOD("set_archetypes", "archetypes"), &LayerConfig::SetArchetypes); + + ClassDB::bind_method(D_METHOD("register_visual_node", "node"), &LayerConfig::RegisterVisualNode); + ClassDB::bind_method(D_METHOD("remove_visual_node", "node"), &LayerConfig::RemoveVisualNode); + ClassDB::bind_method(D_METHOD("has_visual_node", "node"), &LayerConfig::HasVisualNode); + + ClassDB::bind_method(D_METHOD("is_valid"), &LayerConfig::IsValid); + ClassDB::bind_method(D_METHOD("can_connect", "connect"), &LayerConfig::CanConnect); + ClassDB::bind_method(D_METHOD("create_texture", "chunk", "seed"), &LayerConfig::CreateTexture); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "start_chunk"), "set_start_chunk", "get_start_chunk"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "tile", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("LayerTileConfig")), "set_tiles", "get_tiles"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "registered_nodes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("WorldGraphVisualNodeBase")), "set_registered_nodes", "get_registered_nodes"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spawning_archetypes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("Archetype")), "set_archetypes", "get_archetypes"); +} + +void LayerConfig::SetTiles(TypedArray tiles) +{ + Tiles = TypedArrayToVector(tiles); + + struct TileSorter + { + bool operator()(const Ref& lhs, const Ref& rhs) const + { + return lhs->Tile->TileData.GetType() < rhs->Tile->TileData.GetType(); + } + }; + + for (auto& tile : Tiles) + { + if (tile.is_null() || tile->Tile.is_null()) return; + } + Tiles.sort_custom(); +} + +void LayerTileConfig::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_generator"), &LayerTileConfig::GetTileGenerator); + ClassDB::bind_method(D_METHOD("get_tile"), &LayerTileConfig::GetTile); + ClassDB::bind_method(D_METHOD("get_preview_color"), &LayerTileConfig::GetPreviewColor); + + ClassDB::bind_method(D_METHOD("set_generator", "generator"), &LayerTileConfig::SetTileGenerator); + ClassDB::bind_method(D_METHOD("set_tile", "tile"), &LayerTileConfig::SetTile); + ClassDB::bind_method(D_METHOD("set_preview_color", "color"), &LayerTileConfig::SetPreviewColor); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "generator", PROPERTY_HINT_RESOURCE_TYPE, "WorldGraphVisualNodeBase"), "set_generator", "get_generator"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile", PROPERTY_HINT_RESOURCE_TYPE, "TileConfig"), "set_tile", "get_tile"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "preview_color"), "set_preview_color", "get_preview_color"); +} + +bool IsValid_Recursive(Vector>& stack, Ref node) +{ + if (node.is_null()) return false; + + for (auto& stackNode : stack) + { + if (stackNode == node) + return false; + } + + if (stack.size() > 256) + return false; + + stack.push_back(node); + + for (auto& input : node->InputNodes) + { + if (input.is_valid() && + input->IsValid() && + input->GetInternalNode()->GetInputTypes().size() == input->InputNodes.size()) + { + if (!IsValid_Recursive(stack, input)) + { + return false; + } + } + else + { + return false; + } + } + + if (stack[stack.size() - 1] != node) return false; + stack.remove_at(stack.size() - 1); + + return true; +} + +bool LayerConfig::IsValid() const +{ + Vector> nodeStack{}; + + for (auto tile : Tiles) + { + if (tile.is_null() || tile->Tile.is_null() || tile->TileGenerator.is_null() || !tile->TileGenerator->IsValid() || !IsValid_Recursive(nodeStack, tile->TileGenerator)) return false; + } + return true; +} + +bool LayerConfig::CanConnect(Ref from, Ref to) const +{ + if (from == to) return false; + + for (auto input : to->InputNodes) + { + if (input.is_valid() && !CanConnect(from, input)) return false; + } + + return true; +} + +Ref LayerConfig::CreateTexture(Vector2i chunk, int seed) const +{ + if (!IsValid()) return {}; + + Vector> inputs{}; + Vector PreviewColors{}; + + for (int i{}; i < Tiles.size(); ++i) + { + auto& input = Tiles[i]; + inputs.push_back(input->TileGenerator); + input->Tile->TileData.SetID(i); + PreviewColors.push_back(input->PreviewColor); + } + + Ref settings{}; + auto duplicated = duplicate(); + auto layerCopy = Ref(Object::cast_to(duplicated.ptr())); + layerCopy->StartChunk = -5; + + settings->LayerConfigs = {}; + settings->LayerConfigs.push_back(layerCopy); + settings->Initialize(); + + Ref texture{}; + texture.instantiate(); + + ChunkGenerator::GenerateChunk(settings, ChunkKey{0, 0}, seed, [PreviewColors, texture] (std::unique_ptr&& chunk) + { + Ref image; + image.instantiate(Chunk::ChunkSize, Chunk::ChunkSize, false, Image::FORMAT_RGB8); + + for (int y{}; y < Chunk::ChunkSize; ++y) + { + for (int x{}; x < Chunk::ChunkSize; ++x) + { + auto tileID = chunk->Tiles[y * Chunk::ChunkSize + x].GetID(); + if (tileID < PreviewColors.size()) image->set_pixel(x, Chunk::ChunkSize - y - 1, PreviewColors[tileID]); + else image->set_pixel(x, Chunk::ChunkSize - y - 1, Color{1, 0, 1, 1}); + } + } + + texture->set_image(image); + }); + + return texture; +} \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt deleted file mode 100644 index 1fd77cb..0000000 --- a/src/core/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Factory Core library -add_library(factory_core STATIC - factory_core.cpp -) - -# Add alias for consistent usage -add_library(factory::core ALIAS factory_core) - -target_include_directories(factory_core - PUBLIC - $ - $ -) - -target_link_libraries(factory_core - PUBLIC - flecs::flecs_static -) - -# Set compile features -target_compile_features(factory_core PUBLIC cxx_std_17) diff --git a/src/core/factory_core.cpp b/src/core/factory_core.cpp deleted file mode 100644 index 4d6896e..0000000 --- a/src/core/factory_core.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "factory_core.hpp" - -// Implementation file for factory_core -// Currently empty as Core is header-only, but needed for static library target -// Future implementations will go here diff --git a/src/core/factory_core.hpp b/src/core/factory_core.hpp deleted file mode 100644 index 4cc712a..0000000 --- a/src/core/factory_core.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -namespace factory { - -// Core engine class wrapping flecs world -class Core { -public: - Core() : world_() {} - - // Access the underlying flecs world - flecs::world& world() { return world_; } - const flecs::world& world() const { return world_; } - - // Progress the simulation by one tick - bool progress(float delta_time = 0.0f) { - return world_.progress(delta_time); - } - -private: - flecs::world world_; -}; - -} // namespace factory diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..9ce7fb1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,24 @@ + +#include + +#include "Components/Configs/WorldConfig.hpp" +#include "Core/WorldInstance.h" + +int main() +{ + + WorldConfig config{}; + + config.RegisterItem("Stone"); + config.RegisterItem("Wood"); + config.RegisterItem("Stick"); + config.RegisterItem("Copper Ore"); + + + WorldInstance worldInstance{ config }; + + + std::cout << "test\n"; + + +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index cb9bc6d..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Test executable -add_executable(factory_core_tests - test_main.cpp - test_example.cpp -) - -target_link_libraries(factory_core_tests - PRIVATE - factory_core - doctest::doctest -) - -# Register with CTest -include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) -doctest_discover_tests(factory_core_tests) diff --git a/tests/test_SharedBuffer.cpp b/tests/test_SharedBuffer.cpp new file mode 100644 index 0000000..4ff7a96 --- /dev/null +++ b/tests/test_SharedBuffer.cpp @@ -0,0 +1,431 @@ +#include +#include +#include "Util/SharedBuffer.h" +#include +#include +#include + +struct SimpleMetaData +{ + int Id{}; + float Value{}; +}; + +TEST_SUITE("SharedBuffer") +{ + TEST_CASE("default construction") + { + SharedBuffer buf; + CHECK_FALSE(buf); + } + + TEST_CASE("sized construction") + { + SimpleMetaData meta{ 42, 3.14f }; + SharedBuffer buf(5, meta); + + CHECK(buf); + CHECK(buf.GetSize() == 5); + CHECK(buf.GetMetaData()->Id == 42); + CHECK(buf.GetMetaData()->Value == doctest::Approx(3.14f)); + + for (uint32_t i = 0; i < buf.GetSize(); ++i) + CHECK(buf[i] == 0); + } + + TEST_CASE("element access and mutation") + { + SimpleMetaData meta{}; + SharedBuffer buf(3, meta); + + buf[0] = 10; + buf[1] = 20; + buf[2] = 30; + + CHECK(buf[0] == 10); + CHECK(buf[1] == 20); + CHECK(buf[2] == 30); + } + + TEST_CASE("Ptr() access") + { + SimpleMetaData meta{}; + SharedBuffer buf(3, meta); + buf[0] = 100; + + int* ptr = buf.Ptr(); + CHECK(ptr[0] == 100); + + ptr[1] = 200; + CHECK(buf[1] == 200); + } + + TEST_CASE("GetData() returns valid span") + { + SimpleMetaData meta{}; + SharedBuffer buf(4, meta); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + buf[3] = 4; + + auto span = buf.GetData(); + CHECK(span.size() == 4); + CHECK(span[0] == 1); + CHECK(span[3] == 4); + } + + TEST_CASE("const access") + { + SimpleMetaData meta{ 7, 1.0f }; + SharedBuffer buf(2, meta); + buf[0] = 99; + + const auto& cbuf = buf; + CHECK(cbuf[0] == 99); + CHECK(cbuf.GetSize() == 2); + CHECK(cbuf.Ptr()[0] == 99); + CHECK(cbuf.GetMetaData()->Id == 7); + CHECK(cbuf.GetData().size() == 2); + } + + TEST_CASE("copy constructor shares data") + { + SimpleMetaData meta{}; + SharedBuffer a(3, meta); + a[0] = 42; + + SharedBuffer b(a); + + CHECK(b); + CHECK(b.GetSize() == 3); + CHECK(b[0] == 42); + + // They share the same underlying data + CHECK(a.Ptr() == b.Ptr()); + + // Mutation through one is visible in the other + a[1] = 77; + CHECK(b[1] == 77); + } + + TEST_CASE("copy assignment shares data") + { + SimpleMetaData meta{}; + SharedBuffer a(2, meta); + a[0] = 10; + + SharedBuffer b; + b = a; + + CHECK(b); + CHECK(b[0] == 10); + CHECK(a.Ptr() == b.Ptr()); + } + + TEST_CASE("copy assignment from non-empty to non-empty") + { + SimpleMetaData meta1{}; + SimpleMetaData meta2{}; + SharedBuffer a(2, meta1); + SharedBuffer b(3, meta2); + a[0] = 1; + b[0] = 2; + + int* oldBPtr = b.Ptr(); + b = a; + + CHECK(b.GetSize() == 2); + CHECK(b[0] == 1); + CHECK(b.Ptr() == a.Ptr()); + CHECK(b.Ptr() != oldBPtr); + } + + TEST_CASE("self copy assignment") + { + SimpleMetaData meta{}; + SharedBuffer a(2, meta); + a[0] = 55; + + a = a; + + CHECK(a); + CHECK(a[0] == 55); + CHECK(a.GetSize() == 2); + } + + TEST_CASE("move constructor transfers ownership") + { + SimpleMetaData meta{}; + SharedBuffer a(3, meta); + a[0] = 42; + int* origPtr = a.Ptr(); + + SharedBuffer b(std::move(a)); + + CHECK(b); + CHECK(b[0] == 42); + CHECK(b.Ptr() == origPtr); + CHECK_FALSE(a); // source is empty + } + + TEST_CASE("move assignment transfers ownership") + { + SimpleMetaData meta{}; + SharedBuffer a(3, meta); + a[0] = 42; + int* origPtr = a.Ptr(); + + SharedBuffer b; + b = std::move(a); + + CHECK(b); + CHECK(b[0] == 42); + CHECK(b.Ptr() == origPtr); + CHECK_FALSE(a); + } + + TEST_CASE("self move assignment") + { + SimpleMetaData meta{}; + SharedBuffer a(2, meta); + a[0] = 55; + + a = std::move(a); + + CHECK(a); + CHECK(a[0] == 55); + } + + TEST_CASE("reference counting - last copy cleans up") + { + SimpleMetaData meta{}; + int* ptr; + + { + SharedBuffer a(3, meta); + a[0] = 1; + ptr = a.Ptr(); + + { + SharedBuffer b(a); + CHECK(b.Ptr() == ptr); + // b goes out of scope - should NOT free since a still alive + } + + // a should still be valid + CHECK(a); + CHECK(a[0] == 1); + } + // a goes out of scope here - memory freed (no way to check, but no crash) + } + + TEST_CASE("multiple copies and sequential destruction") + { + SimpleMetaData meta{}; + SharedBuffer a(2, meta); + a[0] = 100; + + SharedBuffer b(a); + SharedBuffer c(b); + SharedBuffer d(c); + + CHECK(a.Ptr() == d.Ptr()); + + // Destroy in various orders + b = SharedBuffer(); // release b's ref + CHECK_FALSE(b); + CHECK(a[0] == 100); // a still valid + + d = SharedBuffer(); // release d's ref + CHECK(a[0] == 100); // a still valid + CHECK(c[0] == 100); // c still valid + } + + TEST_CASE("reassignment releases old buffer") + { + SimpleMetaData meta{}; + SharedBuffer a(2, meta); + SharedBuffer b(3, meta); + + a[0] = 1; + b[0] = 2; + + // a had sole ownership of its buffer; assigning b should free old buffer + a = b; + CHECK(a[0] == 2); + CHECK(a.GetSize() == 3); + } + + TEST_CASE("works with non-trivial element types") + { + struct Meta { int x{}; }; + + SharedBuffer buf(3, Meta{ 1 }); + buf[0] = "hello"; + buf[1] = "world"; + buf[2] = "test"; + + CHECK(buf[0] == "hello"); + CHECK(buf[1] == "world"); + CHECK(buf[2] == "test"); + + // Copy and verify strings are shared + SharedBuffer copy(buf); + CHECK(copy[0] == "hello"); + + buf[0] = "modified"; + CHECK(copy[0] == "modified"); // shared data + } + + TEST_CASE("copy of non-trivial types cleans up properly") + { + struct Meta { int x{}; }; + + { + SharedBuffer a(2, Meta{}); + a[0] = "aaa"; + a[1] = "bbb"; + + { + SharedBuffer b(a); + CHECK(b[0] == "aaa"); + } + // b destroyed, a still valid + CHECK(a[0] == "aaa"); + } + // a destroyed, strings cleaned up (no leak/crash) + } + + TEST_CASE("zero-size buffer") + { + SimpleMetaData meta{ 1, 2.0f }; + SharedBuffer buf(0, meta); + + CHECK(buf); + CHECK(buf.GetSize() == 0); + CHECK(buf.GetMetaData()->Id == 1); + CHECK(buf.GetData().empty()); + } + + TEST_CASE("metadata mutation") + { + SimpleMetaData meta{ 1, 0.0f }; + SharedBuffer buf(1, meta); + + buf.GetMetaData()->Id = 99; + buf.GetMetaData()->Value = 1.5f; + + CHECK(buf.GetMetaData()->Id == 99); + CHECK(buf.GetMetaData()->Value == doctest::Approx(1.5f)); + } + + TEST_CASE("metadata shared between copies") + { + SimpleMetaData meta{ 10, 0.0f }; + SharedBuffer a(1, meta); + SharedBuffer b(a); + + a.GetMetaData()->Id = 50; + CHECK(b.GetMetaData()->Id == 50); + } + + TEST_CASE("bool conversion") + { + SharedBuffer empty; + CHECK_FALSE(empty); + + SimpleMetaData meta{}; + SharedBuffer valid(1, meta); + CHECK(valid); + + SharedBuffer moved(std::move(valid)); + CHECK_FALSE(valid); + CHECK(moved); + } + + TEST_CASE("copy from default-constructed buffer") + { + SharedBuffer empty; + SharedBuffer copy(empty); + CHECK_FALSE(copy); + } + + TEST_CASE("assign default-constructed buffer") + { + SimpleMetaData meta{}; + SharedBuffer a(2, meta); + SharedBuffer empty; + + a = empty; + CHECK_FALSE(a); + } + + TEST_CASE("move from default-constructed buffer") + { + SharedBuffer empty; + SharedBuffer moved(std::move(empty)); + CHECK_FALSE(moved); + } + + TEST_CASE("large buffer") + { + SimpleMetaData meta{}; + constexpr int N = 10000; + SharedBuffer buf(N, meta); + + for (int i = 0; i < N; ++i) + buf[i] = i * 2; + + for (int i = 0; i < N; ++i) + CHECK(buf[i] == i * 2); + } + + TEST_CASE("chain of assignments") + { + SimpleMetaData meta{}; + SharedBuffer a(1, meta); + SharedBuffer b(1, meta); + SharedBuffer c(1, meta); + + a[0] = 1; + b[0] = 2; + c[0] = 3; + + a = b; + b = c; + + CHECK(a[0] == 2); + CHECK(b[0] == 3); + CHECK(c[0] == 3); + CHECK(b.Ptr() == c.Ptr()); + } + + TEST_CASE("concurrent read access from copies") + { + SimpleMetaData meta{}; + SharedBuffer buf(100, meta); + for (uint32_t i = 0; i < 100; ++i) + buf[i] = static_cast(i); + + auto copy1 = buf; + auto copy2 = buf; + + std::thread t1([©1]() { + int sum = 0; + for (uint32_t i = 0; i < copy1.GetSize(); ++i) + sum += copy1[i]; + CHECK(sum == 4950); + }); + + std::thread t2([©2]() { + int sum = 0; + for (uint32_t i = 0; i < copy2.GetSize(); ++i) + sum += copy2[i]; + CHECK(sum == 4950); + }); + + t1.join(); + t2.join(); + } +} diff --git a/tests/test_example.cpp b/tests/test_example.cpp deleted file mode 100644 index 363ac9d..0000000 --- a/tests/test_example.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include "factory_core.hpp" - -TEST_CASE("Core initialization") { - factory::Core core; - - SUBCASE("World is valid after construction") { - CHECK(core.world().id() != 0); - } - - SUBCASE("Progress returns true") { - CHECK(core.progress() == true); - } -} - -TEST_CASE("Basic ECS operations") { - factory::Core core; - auto& world = core.world(); - - SUBCASE("Can create entity") { - auto entity = world.entity(); - CHECK(entity.is_valid()); - } - - SUBCASE("Can create entity with name") { - auto entity = world.entity("test_entity"); - CHECK(entity.is_valid()); - CHECK(entity.name() == std::string("test_entity")); - } -}