├── .codacy.yml ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CMake ├── Platform.cmake ├── ThirdParty.cmake └── Util.cmake ├── CMakeLists.txt ├── CMakeSettings.json ├── Doxyfile ├── LICENSE ├── README.md ├── Scripts ├── Build.bat ├── Build.sh ├── Setup.bat └── Setup.sh ├── Source ├── CMakeLists.txt └── Runtime │ ├── CMakeLists.txt │ ├── Core │ ├── CMakeLists.txt │ ├── include │ │ ├── Assertion.h │ │ ├── BSMath.h │ │ ├── CharSet.h │ │ ├── ConfigFile.h │ │ ├── Core.h │ │ ├── Delegate.h │ │ ├── DelegateInst.h │ │ ├── Event.h │ │ ├── Json.h │ │ ├── Logger.h │ │ ├── Name.h │ │ ├── Platform.h │ │ ├── ReservedName.inl │ │ └── Timer.h │ └── src │ │ ├── Assertion.cpp │ │ ├── CharSet.cpp │ │ ├── ConfigFile.cpp │ │ ├── Logger.cpp │ │ ├── Name.cpp │ │ ├── PlatformWindow.cpp │ │ └── Timer.cpp │ ├── Engine │ ├── CMakeLists.txt │ ├── include │ │ ├── Engine.h │ │ ├── Entity.h │ │ ├── Scene.h │ │ ├── SceneManager.h │ │ └── Transform.h │ └── src │ │ ├── Engine.cpp │ │ ├── Entity.cpp │ │ ├── Scene.cpp │ │ ├── SceneManager.cpp │ │ └── Transform.cpp │ ├── Framework │ ├── CMakeLists.txt │ ├── include │ │ ├── Accessor.h │ │ ├── Component.h │ │ └── Manager.h │ └── src │ │ ├── Component.cpp │ │ └── Manager.cpp │ ├── Input │ ├── CMakeLists.txt │ ├── include │ │ ├── InputCode.h │ │ └── InputManager.h │ └── src │ │ ├── InputCode.cpp │ │ └── InputManager.cpp │ ├── Launch │ ├── CMakeLists.txt │ ├── Launch.cpp │ └── LaunchWindows.cpp │ ├── Plugin │ ├── CMakeLists.txt │ ├── include │ │ └── PluginManager.h │ └── src │ │ └── PluginManager.cpp │ ├── RHI │ ├── include │ │ ├── Mesh.h │ │ ├── RHI.h │ │ ├── RHIDef.h │ │ ├── Shader.h │ │ └── Texture.h │ └── src │ │ └── RHI.cpp │ ├── Render │ ├── include │ │ └── RenderManager.h │ └── src │ │ └── RenderManager.cpp │ ├── Thread │ ├── CMakeLists.txt │ ├── include │ │ └── ThreadManager.h │ └── src │ │ └── ThreadManager.cpp │ └── Window │ ├── CMakeLists.txt │ ├── include │ └── WindowManager.h │ └── src │ └── WindowManager.cpp ├── Tests ├── CMakeLists.txt ├── CoreTest.cpp └── TestMain.cpp ├── vcpkg-configuration.json └── vcpkg.json /.codacy.yml: -------------------------------------------------------------------------------- 1 | # codacy configuration file 2 | 3 | --- 4 | 5 | exclude_paths: 6 | - 'External/**' -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This script was created using https://github.com/lukka/CppBuildTasks-Validation/blob/master/.github/workflows/hosted-pure-workflow.yml 2 | 3 | name: BSEngine Test 4 | 5 | on: 6 | push: 7 | branches: [ master ] 8 | paths-ignore: 9 | - '**/README.md' 10 | pull_request: 11 | branches: [ master ] 12 | 13 | env: 14 | VCPKG_ROOT: ${{ github.workspace }}/ThirdParty/vcpkg 15 | CMAKE_BUILD_DIR: ${{ github.workspace }}/Binaries/ 16 | BUILD_TYPE: Release 17 | 18 | jobs: 19 | build_windows: 20 | runs-on: windows-latest 21 | env: 22 | triplet: x64-windows 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | with: 27 | submodules: true 28 | # Init my submodule (vcpkg, BSMath, BSBase) 29 | - uses: snickerbockers/submodules-init@v4 30 | - uses: lukka/get-cmake@latest 31 | - name: Restore vcpkg and its artifacts. 32 | uses: actions/cache@v2 33 | with: 34 | # The first path is where vcpkg generates artifacts while consuming the vcpkg.json manifest file. 35 | # The second path is the location of vcpkg (it contains the vcpkg executable and data files). 36 | # The other paths starting with '!' are exclusions: they contain termporary files generated during the build of the installed packages. 37 | path: | 38 | ${{ env.CMAKE_BUILD_DIR }}/vcpkg_installed/ 39 | ${{ env.VCPKG_ROOT }} 40 | ${{ env.VCPKG_ROOT }}/buildtrees 41 | ${{ env.VCPKG_ROOT }}/packages 42 | ${{ env.VCPKG_ROOT }}/downloads 43 | # The key is composed in a way that it gets properly invalidated: this must happen whenever vcpkg's Git commit id changes, or the list of packages changes. In this case a cache miss must happen and a new entry with a new key with be pushed to GitHub the cache service. 44 | # The key includes: hash of the vcpkg.json file, the hash of the vcpkg Git commit id, and the used vcpkg's triplet. The vcpkg's commit id would suffice, but computing an hash out it does not harm. 45 | # Note: given a key, the cache content is immutable. If a cache entry has been created improperly, in order the recreate the right content the key must be changed as well, and it must be brand new (i.e. not existing already). 46 | key: | 47 | ${{ hashFiles( 'vcpkg.json' ) }}-${{ hashFiles( '.git/modules/vcpkg/HEAD' ) }}-${{ env.triplet }}-invalidate 48 | # On Windows runners, let's ensure to have the Developer Command Prompt environment setup correctly. As used here the Developer Command Prompt created is targeting x64 and using the default the Windows SDK. 49 | - uses: ilammy/msvc-dev-cmd@v1 50 | # Run CMake to generate Ninja project files, using the vcpkg's toolchain file to resolve and install the dependencies as specified in vcpkg.json. 51 | - name: Install dependencies and generate project files 52 | run: ${{ github.workspace }}/Scripts/Setup.bat ${{ env.BUILD_TYPE }} 53 | # Build the whole project with Ninja (which is spawn by CMake). 54 | - name: Build 55 | run: ${{ github.workspace }}/Scripts/Build.bat ${{ env.BUILD_TYPE }} 56 | - name: Run Test 57 | working-directory: ${{ github.workspace }}/Binaries/${{ env.BUILD_TYPE }} 58 | run: ./BSEngine-Tests -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/c++,windows,visualstudio,visualstudiocode 3 | # Edit at https://www.gitignore.io/?templates=c++,windows,visualstudio,visualstudiocode 4 | 5 | ### C++ ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | ### VisualStudioCode ### 40 | .vscode/* 41 | !.vscode/settings.json 42 | !.vscode/tasks.json 43 | !.vscode/launch.json 44 | !.vscode/extensions.json 45 | 46 | ### VisualStudioCode Patch ### 47 | # Ignore all local history of files 48 | .history 49 | 50 | ### Windows ### 51 | # Windows thumbnail cache files 52 | Thumbs.db 53 | Thumbs.db:encryptable 54 | ehthumbs.db 55 | ehthumbs_vista.db 56 | 57 | # Dump file 58 | *.stackdump 59 | 60 | # Folder config file 61 | [Dd]esktop.ini 62 | 63 | # Recycle Bin used on file shares 64 | $RECYCLE.BIN/ 65 | 66 | # Windows Installer files 67 | *.cab 68 | *.msi 69 | *.msix 70 | *.msm 71 | *.msp 72 | 73 | # Windows shortcuts 74 | *.lnk 75 | 76 | ### VisualStudio ### 77 | ## Ignore Visual Studio temporary files, build results, and 78 | ## files generated by popular Visual Studio add-ons. 79 | ## 80 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 81 | 82 | # User-specific files 83 | *.rsuser 84 | *.suo 85 | *.user 86 | *.userosscache 87 | *.sln.docstates 88 | 89 | # User-specific files (MonoDevelop/Xamarin Studio) 90 | *.userprefs 91 | 92 | # Mono auto generated files 93 | mono_crash.* 94 | 95 | # Build results 96 | [Dd]ebug/ 97 | [Dd]ebugPublic/ 98 | [Rr]elease/ 99 | [Rr]eleases/ 100 | x64/ 101 | x86/ 102 | [Aa][Rr][Mm]/ 103 | [Aa][Rr][Mm]64/ 104 | bld/ 105 | [Bb]in/ 106 | [Oo]bj/ 107 | [Ll]og/ 108 | 109 | # Visual Studio 2015/2017 cache/options directory 110 | .vs/ 111 | # Uncomment if you have tasks that create the project's static files in wwwroot 112 | #wwwroot/ 113 | 114 | # Visual Studio 2017 auto generated files 115 | Generated\ Files/ 116 | 117 | # MSTest test Results 118 | [Tt]est[Rr]esult*/ 119 | [Bb]uild[Ll]og.* 120 | 121 | # NUnit 122 | *.VisualState.xml 123 | TestResult.xml 124 | nunit-*.xml 125 | 126 | # Build Results of an ATL Project 127 | [Dd]ebugPS/ 128 | [Rr]eleasePS/ 129 | dlldata.c 130 | 131 | # Benchmark Results 132 | BenchmarkDotNet.Artifacts/ 133 | 134 | # .NET Core 135 | project.lock.json 136 | project.fragment.lock.json 137 | artifacts/ 138 | 139 | # StyleCop 140 | StyleCopReport.xml 141 | 142 | # Files built by Visual Studio 143 | *_i.c 144 | *_p.c 145 | *_h.h 146 | *.ilk 147 | *.meta 148 | *.iobj 149 | *.pdb 150 | *.ipdb 151 | *.pgc 152 | *.pgd 153 | *.rsp 154 | *.sbr 155 | *.tlb 156 | *.tli 157 | *.tlh 158 | *.tmp 159 | *.tmp_proj 160 | *_wpftmp.csproj 161 | *.log 162 | *.vspscc 163 | *.vssscc 164 | .builds 165 | *.pidb 166 | *.svclog 167 | *.scc 168 | 169 | # Chutzpah Test files 170 | _Chutzpah* 171 | 172 | # Visual C++ cache files 173 | ipch/ 174 | *.aps 175 | *.ncb 176 | *.opendb 177 | *.opensdf 178 | *.sdf 179 | *.cachefile 180 | *.VC.db 181 | *.VC.VC.opendb 182 | 183 | # Visual Studio profiler 184 | *.psess 185 | *.vsp 186 | *.vspx 187 | *.sap 188 | 189 | # Visual Studio Trace Files 190 | *.e2e 191 | 192 | # TFS 2012 Local Workspace 193 | $tf/ 194 | 195 | # Guidance Automation Toolkit 196 | *.gpState 197 | 198 | # ReSharper is a .NET coding add-in 199 | _ReSharper*/ 200 | *.[Rr]e[Ss]harper 201 | *.DotSettings.user 202 | 203 | # JustCode is a .NET coding add-in 204 | .JustCode 205 | 206 | # TeamCity is a build add-in 207 | _TeamCity* 208 | 209 | # DotCover is a Code Coverage Tool 210 | *.dotCover 211 | 212 | # AxoCover is a Code Coverage Tool 213 | .axoCover/* 214 | !.axoCover/settings.json 215 | 216 | # Visual Studio code coverage results 217 | *.coverage 218 | *.coveragexml 219 | 220 | # NCrunch 221 | _NCrunch_* 222 | .*crunch*.local.xml 223 | nCrunchTemp_* 224 | 225 | # MightyMoose 226 | *.mm.* 227 | AutoTest.Net/ 228 | 229 | # Web workbench (sass) 230 | .sass-cache/ 231 | 232 | # Installshield output folder 233 | [Ee]xpress/ 234 | 235 | # DocProject is a documentation generator add-in 236 | DocProject/buildhelp/ 237 | DocProject/Help/*.HxT 238 | DocProject/Help/*.HxC 239 | DocProject/Help/*.hhc 240 | DocProject/Help/*.hhk 241 | DocProject/Help/*.hhp 242 | DocProject/Help/Html2 243 | DocProject/Help/html 244 | 245 | # Click-Once directory 246 | publish/ 247 | 248 | # Publish Web Output 249 | *.[Pp]ublish.xml 250 | *.azurePubxml 251 | # Note: Comment the next line if you want to checkin your web deploy settings, 252 | # but database connection strings (with potential passwords) will be unencrypted 253 | *.pubxml 254 | *.publishproj 255 | 256 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 257 | # checkin your Azure Web App publish settings, but sensitive information contained 258 | # in these scripts will be unencrypted 259 | PublishScripts/ 260 | 261 | # NuGet Packages 262 | *.nupkg 263 | # NuGet Symbol Packages 264 | *.snupkg 265 | # The packages folder can be ignored because of Package Restore 266 | **/[Pp]ackages/* 267 | # except build/, which is used as an MSBuild target. 268 | !**/[Pp]ackages/build/ 269 | # Uncomment if necessary however generally it will be regenerated when needed 270 | #!**/[Pp]ackages/repositories.config 271 | # NuGet v3's project.json files produces more ignorable files 272 | *.nuget.props 273 | *.nuget.targets 274 | 275 | # Microsoft Azure Build Output 276 | csx/ 277 | *.build.csdef 278 | 279 | # Microsoft Azure Emulator 280 | ecf/ 281 | rcf/ 282 | 283 | # Windows Store app package directories and files 284 | AppPackages/ 285 | BundleArtifacts/ 286 | Package.StoreAssociation.xml 287 | _pkginfo.txt 288 | *.appx 289 | *.appxbundle 290 | *.appxupload 291 | 292 | # Visual Studio cache files 293 | # files ending in .cache can be ignored 294 | *.[Cc]ache 295 | # but keep track of directories ending in .cache 296 | !?*.[Cc]ache/ 297 | 298 | # Others 299 | ClientBin/ 300 | ~$* 301 | *~ 302 | *.dbmdl 303 | *.dbproj.schemaview 304 | *.jfm 305 | *.pfx 306 | *.publishsettings 307 | orleans.codegen.cs 308 | 309 | # Including strong name files can present a security risk 310 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 311 | #*.snk 312 | 313 | # Since there are multiple workflows, uncomment next line to ignore bower_components 314 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 315 | #bower_components/ 316 | 317 | # RIA/Silverlight projects 318 | Generated_Code/ 319 | 320 | # Backup & report files from converting an old project file 321 | # to a newer Visual Studio version. Backup files are not needed, 322 | # because we have git ;-) 323 | _UpgradeReport_Files/ 324 | Backup*/ 325 | UpgradeLog*.XML 326 | UpgradeLog*.htm 327 | ServiceFabricBackup/ 328 | *.rptproj.bak 329 | 330 | # SQL Server files 331 | *.mdf 332 | *.ldf 333 | *.ndf 334 | 335 | # Business Intelligence projects 336 | *.rdl.data 337 | *.bim.layout 338 | *.bim_*.settings 339 | *.rptproj.rsuser 340 | *- [Bb]ackup.rdl 341 | *- [Bb]ackup ([0-9]).rdl 342 | *- [Bb]ackup ([0-9][0-9]).rdl 343 | 344 | # Microsoft Fakes 345 | FakesAssemblies/ 346 | 347 | # GhostDoc plugin setting file 348 | *.GhostDoc.xml 349 | 350 | # Node.js Tools for Visual Studio 351 | .ntvs_analysis.dat 352 | node_modules/ 353 | 354 | # Visual Studio 6 build log 355 | *.plg 356 | 357 | # Visual Studio 6 workspace options file 358 | *.opt 359 | 360 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 361 | *.vbw 362 | 363 | # Visual Studio LightSwitch build output 364 | **/*.HTMLClient/GeneratedArtifacts 365 | **/*.DesktopClient/GeneratedArtifacts 366 | **/*.DesktopClient/ModelManifest.xml 367 | **/*.Server/GeneratedArtifacts 368 | **/*.Server/ModelManifest.xml 369 | _Pvt_Extensions 370 | 371 | # Paket dependency manager 372 | .paket/paket.exe 373 | paket-files/ 374 | 375 | # FAKE - F# Make 376 | .fake/ 377 | 378 | # CodeRush personal settings 379 | .cr/personal 380 | 381 | # Python Tools for Visual Studio (PTVS) 382 | __pycache__/ 383 | *.pyc 384 | 385 | # Cake - Uncomment if you are using it 386 | # tools/** 387 | # !tools/packages.config 388 | 389 | # Tabs Studio 390 | *.tss 391 | 392 | # Telerik's JustMock configuration file 393 | *.jmconfig 394 | 395 | # BizTalk build output 396 | *.btp.cs 397 | *.btm.cs 398 | *.odx.cs 399 | *.xsd.cs 400 | 401 | # OpenCover UI analysis results 402 | OpenCover/ 403 | 404 | # Azure Stream Analytics local run output 405 | ASALocalRun/ 406 | 407 | # MSBuild Binary and Structured Log 408 | *.binlog 409 | 410 | # NVidia Nsight GPU debugger configuration file 411 | *.nvuser 412 | 413 | # MFractors (Xamarin productivity tool) working folder 414 | .mfractor/ 415 | 416 | # Local History for Visual Studio 417 | .localhistory/ 418 | 419 | # BeatPulse healthcheck temp database 420 | healthchecksdb 421 | 422 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 423 | MigrationBackup/ 424 | 425 | # End of https://www.gitignore.io/api/c++,windows,visualstudio,visualstudiocode 426 | 427 | # Visual studio sln, proj file 428 | *.sln 429 | *.vcxproj 430 | *.vcxproj.filters 431 | *.vcxproj.user 432 | 433 | # Custom build, temp folder 434 | [Bb]inaries/ 435 | [Ii]nstall/ 436 | [Bb]uild/ 437 | 438 | # External library, use script 439 | vcpkg/ 440 | 441 | # Auyo installed third party 442 | Sources/ThirdParty/fmt/ 443 | Sources/ThirdParty/spdlog/ 444 | Sources/ThirdParty/rapidJSON/ 445 | 446 | # Document Path 447 | Document/ 448 | 449 | # vcpkg 450 | Scripts/vcpkg -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vcpkg"] 2 | path = vcpkg 3 | url = https://github.com/Microsoft/vcpkg.git 4 | -------------------------------------------------------------------------------- /CMake/Platform.cmake: -------------------------------------------------------------------------------- 1 | # Create defines based on PLATFORM 2 | if (CMAKE_SYSTEM_NAME MATCHES "Windows") 3 | set (PLATFORM_WINDOWS TRUE) 4 | set (PLATFORM WINDOWS) 5 | elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") 6 | set (PLATFORM_LINUX TRUE) 7 | set (PLATFORM LINUX) 8 | elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") 9 | set (PLATFORM_MAC TRUE) 10 | set (PLATFORM MAC) 11 | endif () 12 | 13 | # Determine compiler 14 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 15 | set (DLL_IMPORT "__declspec(dllimport)" CACHE INTERNAL "DLL Import") 16 | set (DLL_EXPORT "__declspec(dllexport)" CACHE INTERNAL "DLL Export") 17 | 18 | set (COMPILER_MSVC TRUE) 19 | set (COMPILER MSVC) 20 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 21 | set (DLL_IMPORT "__attribute__((visibility(\"default\")))" CACHE INTERNAL "DLL Import") 22 | set (DLL_EXPORT "__attribute__((visibility(\"default\")))" CACHE INTERNAL "DLL Export") 23 | 24 | set (COMPILER_CLANG TRUE) 25 | set (COMPILER CLANG) 26 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 27 | set (DLL_IMPORT "__attribute__((visibility(\"default\")))" CACHE INTERNAL "DLL Import") 28 | set (DLL_EXPORT "__attribute__((visibility(\"default\")))" CACHE INTERNAL "DLL Export") 29 | 30 | set (COMPILER_GCC TRUE) 31 | set (COMPILER GCC) 32 | endif() 33 | 34 | # Determine target Architecture 35 | if ((CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "x86") AND CMAKE_SIZEOF_VOID_P EQUAL 8) 36 | set (ARCH_x64 TRUE) 37 | set (ARCH x64) 38 | elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" AND CMAKE_SIZEOF_VOID_P EQUAL 4) 39 | set (ARCH_x86 TRUE) 40 | set (ARCH x86) 41 | endif () 42 | 43 | add_compile_definitions (PLATFORM=${PLATFORM} ${PLATFORM} COMPILER=${COMPILER} ${COMPILER} ARCH=${ARCH} ${ARCH}) 44 | add_compile_definitions (DLL_IMPORT=${DLL_IMPORT} DLL_EXPORT=${DLL_EXPORT}) -------------------------------------------------------------------------------- /CMake/ThirdParty.cmake: -------------------------------------------------------------------------------- 1 | find_package (BSBase CONFIG REQUIRED) 2 | find_package (BSMath CONFIG REQUIRED) 3 | find_package (fmt CONFIG REQUIRED) 4 | find_package (nlohmann_json CONFIG REQUIRED) 5 | find_package (spdlog CONFIG REQUIRED) 6 | find_package (utf8cpp CONFIG REQUIRED) 7 | 8 | if (CMAKE_SYSTEM_NAME MATCHES "Linux") 9 | find_package (Threads REQUIRED) 10 | endif () 11 | 12 | if (NOT DEFINED BUILD_GAME) 13 | find_package(GTest) 14 | endif () -------------------------------------------------------------------------------- /CMake/Util.cmake: -------------------------------------------------------------------------------- 1 | function (register_library) 2 | get_filename_component (MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 3 | 4 | # This is unnecessary, but added for convenience in the editor. 5 | file (GLOB_RECURSE INCS "*.h") 6 | file (GLOB_RECURSE SRCS "*.cpp") 7 | 8 | add_library (${MODULE_NAME} SHARED ${INCS} ${SRCS}) 9 | target_include_directories (${MODULE_NAME} PUBLIC "include") 10 | 11 | string (TOUPPER ${MODULE_NAME}_API API) 12 | target_compile_definitions (${MODULE_NAME} PRIVATE ${API}=${DLL_EXPORT} INTERFACE ${API}=${DLL_IMPORT}) 13 | 14 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/pch.h") 15 | target_precompile_headers (${MODULE_NAME} PUBLIC include/pch.h) 16 | endif () 17 | 18 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/pch.h") 19 | target_precompile_headers (${MODULE_NAME} PRIVATE src/pch.h) 20 | endif () 21 | endfunction () -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.12) 2 | 3 | project (BSEngine 4 | VERSION 0.1.0 5 | LANGUAGES CXX 6 | DESCRIPTION "Game Engine made by blAs1N" 7 | HOMEPAGE_URL "https://github.com/blAs1N/BS_Engine" 8 | ) 9 | 10 | add_compile_definitions (GAME_NAME=${CMAKE_PROJECT_NAME}) 11 | 12 | if (NOT ${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) 13 | set (BUILD_GAME TRUE) 14 | endif () 15 | 16 | set (CMAKE_CXX_STANDARD 17) 17 | set (CMAKE_DIR "${PROJECT_SOURCE_DIR}/CMake") 18 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_BINARY_DIR}>) 19 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_BINARY_DIR}>) 20 | 21 | include (CMake/Platform.cmake) 22 | include (CMake/ThirdParty.cmake) 23 | include (CMake/Util.cmake) 24 | 25 | set_property (GLOBAL PROPERTY USE_FOLDERS ON) 26 | 27 | add_subdirectory (Source) 28 | 29 | if (NOT DEFINED BUILD_GAME) 30 | add_subdirectory (Tests) 31 | endif () 32 | 33 | if (${PLATFORM_WINDOWS}) 34 | install (DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} DESTINATION "." FILES_MATCHING PATTERN "*.dll") 35 | else () 36 | install (DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} DESTINATION "." FILES_MATCHING PATTERN "*.so") 37 | endif () -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "buildRoot": "${projectDir}\\Binaries\\${name}", 8 | "cmakeToolchain": "${projectDir}\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake", 9 | "cmakeCommandArgs": "", 10 | "buildCommandArgs": "-v", 11 | "ctestCommandArgs": "", 12 | "inheritEnvironments": [ "msvc_x64_x64" ], 13 | "variables": [ 14 | { 15 | "name": "VCPKG_FEATURE_FLAGS", 16 | "value": "manifests,registries", 17 | "type": "STRING" 18 | } 19 | ] 20 | }, 21 | { 22 | "name": "Win64-Development", 23 | "generator": "Ninja", 24 | "configurationType": "RelWithDebInfo", 25 | "buildRoot": "${projectDir}\\Binaries\\${name}", 26 | "cmakeToolchain": "${projectDir}\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake", 27 | "cmakeCommandArgs": "", 28 | "buildCommandArgs": "-v", 29 | "ctestCommandArgs": "", 30 | "inheritEnvironments": [ "msvc_x64_x64" ], 31 | "variables": [ 32 | { 33 | "name": "VCPKG_FEATURE_FLAGS", 34 | "value": "manifests,registries", 35 | "type": "STRING" 36 | } 37 | ] 38 | }, 39 | { 40 | "name": "Win64-Shipping", 41 | "generator": "Ninja", 42 | "configurationType": "Release", 43 | "buildRoot": "${projectDir}\\Binaries\\${name}", 44 | "cmakeToolchain": "${projectDir}\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake", 45 | "cmakeCommandArgs": "", 46 | "buildCommandArgs": "-v", 47 | "ctestCommandArgs": "", 48 | "inheritEnvironments": [ "msvc_x64_x64" ], 49 | "variables": [ 50 | { 51 | "name": "VCPKG_FEATURE_FLAGS", 52 | "value": "manifests,registries", 53 | "type": "STRING" 54 | } 55 | ] 56 | } 57 | ] 58 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 blAs1N 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BS_Engine 2 | 3 | ![BSEngine Test](https://github.com/blAs1N/BSEngine/workflows/BSEngine%20Test/badge.svg) 4 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/1fc8e08bafaa476986f2197e56cd4137)](https://www.codacy.com/gh/blAs1N/BSEngine/dashboard?utm_source=github.com&utm_medium=referral&utm_content=blAs1N/BSEngine&utm_campaign=Badge_Grade) 5 | 6 | It is my own game engine project. 7 | This project started on March 30, 2021. 8 | -------------------------------------------------------------------------------- /Scripts/Build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd %~dp0 4 | 5 | if "%1%" == "" ( 6 | set BUILD_TYPE=Release 7 | ) else ( 8 | set BUILD_TYPE=%1 9 | ) 10 | 11 | cmake --build ../Binaries/%BUILD_TYPE% --config %BUILD_TYPE% 12 | 13 | popd -------------------------------------------------------------------------------- /Scripts/Build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | DIR=$(pwd) 6 | cd $(dirname "$0") 7 | 8 | if [ $# -ge 1 ] ; then 9 | BUILD_TYPE=$1 10 | else 11 | BUILD_TYPE=Release 12 | fi 13 | 14 | cmake --build ../Binaries/${BUILD_TYPE} --config ${BUILD_TYPE} 15 | 16 | cd "${DIR}" -------------------------------------------------------------------------------- /Scripts/Setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd %~dp0 4 | 5 | set TOOLCHAIN_FILE=%CD%/../vcpkg/scripts/buildsystems/vcpkg.cmake 6 | 7 | if "%1%" == "" ( 8 | set BUILD_TYPE=Release 9 | ) else ( 10 | set BUILD_TYPE=%1 11 | ) 12 | 13 | cd .. 14 | if not exist Binaries\%BUILD_TYPE%\ ( 15 | mkdir Binaries\%BUILD_TYPE% 16 | ) 17 | cd Binaries\%BUILD_TYPE% 18 | 19 | cmake ../.. -DCMAKE_TOOLCHAIN_FILE=%TOOLCHAIN_FILE% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DVCPKG_FEATURE_FLAGS=manifests,registries 20 | popd -------------------------------------------------------------------------------- /Scripts/Setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | DIR=$(pwd) 6 | cd $(dirname "$0") 7 | 8 | TOOLCHAIN_FILE="$(pwd)"/../vcpkg/scripts/buildsystems/vcpkg.cmake 9 | 10 | if [ $# -ge 1 ] ; then 11 | BUILD_TYPE=$1 12 | else 13 | BUILD_TYPE=Release 14 | fi 15 | 16 | cd .. 17 | mkdir -p Binaries/$BUILD_TYPE 18 | cd Binaries/$BUILD_TYPE 19 | 20 | cmake ../.. -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DVCPKG_FEATURE_FLAGS=manifests,registries 21 | 22 | cd "${DIR}" -------------------------------------------------------------------------------- /Source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source 2 | 3 | add_subdirectory (Runtime) -------------------------------------------------------------------------------- /Source/Runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime 2 | 3 | string (REPLACE "/DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") 4 | string (REPLACE "/DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") 5 | string (REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") 6 | string (REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") 7 | 8 | add_subdirectory (Core) 9 | add_subdirectory (Engine) 10 | add_subdirectory (Framework) 11 | add_subdirectory (Input) 12 | add_subdirectory (Launch) 13 | add_subdirectory (Plugin) 14 | add_subdirectory (Render) 15 | add_subdirectory (RHI) 16 | add_subdirectory (Thread) 17 | add_subdirectory (Window) -------------------------------------------------------------------------------- /Source/Runtime/Core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Core 2 | 3 | register_library () 4 | 5 | target_link_libraries (Core PUBLIC BSBase::BSBase BSMath::BSMath fmt::fmt-header-only PRIVATE nlohmann_json::nlohmann_json spdlog::spdlog utf8cpp) -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Assertion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BSBase/Type.h" 4 | #include "fmt/format.h" 5 | #include "Platform.h" 6 | 7 | #ifdef NDEBUG 8 | 9 | # define Assert(expr) 10 | # define AssertMsg(expr, fmt, ...) 11 | 12 | # define Ensure(expr) (expr) 13 | # define EnsureMsg(expr, fmt, ...) (expr) 14 | 15 | #else 16 | 17 | namespace Impl 18 | { 19 | CORE_API void LogToFail(bool isCritical, const Char* expr, const Char* file, BSBase::int32 line, const String& msg) noexcept; 20 | 21 | template 22 | void LogToFail(bool isCritical, const Char* expr, const Char* file, BSBase::int32 line, const Str& format, Args... args) noexcept 23 | { 24 | LogToFail(isCritical, expr, file, line, fmt::format(format, args...)); 25 | } 26 | } 27 | 28 | # ifdef WINDOWS 29 | # define DEBUG_BREAK() (void)(::IsDebugging() && (::__debugbreak(), true)) 30 | # else 31 | # include 32 | # define DEBUG_BREAK() (void)(::IsDebugging() && (::std::raise(SIGTRAP), true)) 33 | # endif 34 | 35 | # define AssertMsg(expr, fmt, ...) (void)(!!(expr) || (Impl::LogToFail(true, \ 36 | u#expr, ADD_PREFIX(u, __FILE__), __LINE__, fmt, ##__VA_ARGS__), DEBUG_BREAK(), false)) 37 | 38 | # define Assert(expr) AssertMsg(expr, STR("")) 39 | 40 | # define EnsureMsg(expr, fmt, ...) (!!(expr) || (Impl::LogToFail(false, \ 41 | u#expr, ADD_PREFIX(u, __FILE__), __LINE__, fmt, ##__VA_ARGS__), DEBUG_BREAK(), false)) 42 | 43 | # define Ensure(expr) EnsureMsg(expr, STR("")) 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/BSMath.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BSMath/Color.h" 4 | #include "BSMath/Creator.h" 5 | 6 | using namespace BSMath; 7 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/CharSet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "BSBase/Base.h" 12 | 13 | #ifdef STR 14 | # undef STR 15 | #endif 16 | 17 | #define STR_IMPL(x) u##x 18 | #define STR(x) STR_IMPL(x) 19 | 20 | using Char = char16_t; 21 | using String = std::u16string; 22 | using StringView = std::u16string_view; 23 | 24 | using Ios = std::basic_ios; 25 | using StreamBuf = std::basic_streambuf; 26 | using FileBuf = std::basic_filebuf; 27 | using StringBuf = std::basic_stringbuf; 28 | 29 | using Istream = std::basic_istream; 30 | using Ostream = std::basic_istream; 31 | using IOStream = std::basic_iostream; 32 | 33 | using IfStream = std::basic_ifstream; 34 | using OfStream = std::basic_ofstream; 35 | using FStream = std::basic_fstream; 36 | 37 | using IStringStream = std::basic_istringstream; 38 | using OStringStream = std::basic_ostringstream; 39 | using StringStream = std::basic_stringstream; 40 | 41 | namespace Impl 42 | { 43 | NO_ODR std::string ToString(std::string_view from) { return std::string(from.begin(), from.end()); } 44 | CORE_API std::string ToString(std::wstring_view from); 45 | CORE_API std::string ToString(std::u16string_view from); 46 | CORE_API std::string ToString(std::u32string_view from); 47 | 48 | CORE_API std::wstring ToWString(std::string_view from); 49 | 50 | NO_ODR std::wstring ToWString(std::wstring_view from) { return std::wstring(from.begin(), from.end()); } 51 | 52 | // sizeof(wchar_t) must be 1byte or 2byte 53 | CORE_API std::wstring ToWString(std::u16string_view from); 54 | 55 | // sizeof(wchar_t) must be 1byte 56 | CORE_API std::wstring ToWString(std::u32string_view from); 57 | 58 | CORE_API std::u16string ToU16String(std::string_view from); 59 | 60 | // sizeof(wchar_t) must be 1byte or 2byte 61 | CORE_API std::u16string ToU16String(std::wstring_view from); 62 | 63 | NO_ODR std::u16string ToU16String(std::u16string_view from) { return std::u16string(from.begin(), from.end()); } 64 | 65 | CORE_API std::u32string ToU32String(std::string_view from); 66 | 67 | // sizeof(wchar_t) must be 1byte 68 | CORE_API std::u32string ToU32String(std::wstring_view from); 69 | } 70 | 71 | template 72 | std::basic_string CastCharSet(std::basic_string_view from) 73 | { 74 | static_assert((sizeof(From) == sizeof(To)) || (sizeof(From) < 2 || 75 | sizeof(To) < 2), "Mutual conversion between utf16 and utf32 is not supported"); 76 | 77 | if constexpr (std::is_same_v) 78 | return Impl::ToString(from); 79 | else if constexpr (std::is_same_v) 80 | return Impl::ToWString(from); 81 | else if constexpr (std::is_same_v) 82 | return Impl::ToU16String(from); 83 | else if constexpr (std::is_same_v) 84 | return Impl::ToU32String(from); 85 | else 86 | static_assert(false, "Unknown type!"); 87 | } 88 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/ConfigFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "BSBase/Type.h" 5 | #include "CharSet.h" 6 | 7 | class ConfigFile final 8 | { 9 | class ConfigSection final 10 | { 11 | public: 12 | ConfigSection(const std::unordered_map& inData) 13 | : data(inData) {} 14 | 15 | ConfigSection(std::unordered_map&& inData) noexcept 16 | : data(std::move(inData)) {} 17 | 18 | [[nodiscard]] const String* operator[](const String& key) const noexcept; 19 | 20 | private: 21 | std::unordered_map data; 22 | }; 23 | 24 | public: 25 | CORE_API ConfigFile() = default; 26 | 27 | CORE_API ConfigFile(const String& fileName) 28 | : ConfigFile{} 29 | { 30 | LoadFromFile(fileName); 31 | } 32 | 33 | CORE_API bool LoadFromFile(const String& fileName) noexcept; 34 | CORE_API void Clear() noexcept; 35 | 36 | [[nodiscard]] CORE_API bool IsAvailable() const noexcept { return isAvailable; } 37 | [[nodiscard]] CORE_API operator bool() const noexcept { return isAvailable; } 38 | 39 | [[nodiscard]] CORE_API bool IsExistSection(const String& section) const noexcept 40 | { 41 | return data.find(section) != data.cend(); 42 | } 43 | 44 | [[nodiscard]] CORE_API const String* operator()(const String& sectionName, const String& keyName) const noexcept; 45 | 46 | private: 47 | std::unordered_map data; 48 | BSBase::uint8 isAvailable : 1; 49 | }; 50 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BSBase/Result.h" 4 | #include "BSBase/Util.h" 5 | #include "Assertion.h" 6 | #include "BSMath.h" 7 | #include "CharSet.h" 8 | #include "ConfigFile.h" 9 | #include "Delegate.h" 10 | #include "Event.h" 11 | #include "Json.h" 12 | #include "Logger.h" 13 | #include "Name.h" 14 | #include "Platform.h" 15 | #include "Timer.h" 16 | 17 | using namespace BSBase; 18 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Delegate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BSBase/Type.h" 4 | #include "DelegateInst.h" 5 | 6 | template 7 | class Delegate; 8 | 9 | template 10 | class Delegate final 11 | { 12 | public: 13 | Delegate() noexcept : storage{ nullptr }, size(0) {} 14 | 15 | Delegate(std::nullptr_t) noexcept : Delegate() {} 16 | 17 | Delegate(const Delegate& other) noexcept : Delegate() 18 | { 19 | if (const auto* inst = other.GetInst()) 20 | { 21 | size = other.size; 22 | if (size > sizeof(storage)) 23 | { 24 | storage[0] = malloc(size); 25 | memcpy(storage[0], inst, size); 26 | } 27 | else 28 | { 29 | memcpy(storage, inst, size); 30 | } 31 | } 32 | } 33 | 34 | Delegate(Delegate&& other) noexcept : Delegate() 35 | { 36 | memcpy(this, &other, sizeof(*this)); 37 | memset(&other, 0, sizeof(other)); 38 | } 39 | 40 | Delegate& operator=(const Delegate& other) noexcept 41 | { 42 | if (*this == other) return *this; 43 | 44 | Clear(); 45 | 46 | if (const auto* inst = other.GetInst()) 47 | { 48 | size = other.size; 49 | if (size > sizeof(storage)) 50 | { 51 | storage[0] = malloc(size); 52 | memcpy(storage[0], inst, size); 53 | } 54 | else 55 | { 56 | memcpy(storage, inst, size); 57 | } 58 | } 59 | 60 | return *this; 61 | } 62 | 63 | Delegate& operator=(Delegate&& other) noexcept 64 | { 65 | if (*this == other) return *this; 66 | 67 | Clear(); 68 | 69 | memcpy(this, &other, sizeof(*this)); 70 | memset(&other, 0, sizeof(other)); 71 | return *this; 72 | } 73 | 74 | Delegate(R(*fn)(Args...)) : Delegate() 75 | { 76 | CreateInstance>(fn); 77 | } 78 | 79 | template 80 | Delegate(T* obj, R(T::* fn)(Args...)) : Delegate() 81 | { 82 | CreateInstance>(obj, fn); 83 | } 84 | 85 | template 86 | Delegate(T* obj, R(T::* fn)(Args...) const) : Delegate() 87 | { 88 | CreateInstance>(obj, fn); 89 | } 90 | 91 | template 92 | Delegate(Func&& fn) : Delegate() 93 | { 94 | CreateInstance>(std::forward(fn)); 95 | } 96 | 97 | ~Delegate() { Clear(); } 98 | 99 | R operator()(Args... args) const 100 | { 101 | return IsBound() ? GetInst()->Execute(args...) : R(); 102 | } 103 | 104 | void Clear() noexcept 105 | { 106 | if (const auto heap = GetHeap()) 107 | free(heap); 108 | 109 | memset(this, 0, sizeof(*this)); 110 | } 111 | 112 | [[nodiscard]] bool IsBound() const noexcept { return size; } 113 | [[nodiscard]] operator bool() const noexcept { return IsBound(); } 114 | 115 | [[nodiscard]] friend bool operator==(const Delegate& lhs, const Delegate& rhs) 116 | { 117 | if (lhs.size != rhs.size) 118 | return false; 119 | 120 | const auto lhsInst = lhs.GetInst(); 121 | const auto rhsInst = rhs.GetInst(); 122 | 123 | if (!lhsInst) return !rhsInst; 124 | if (!rhsInst) return false; 125 | 126 | return !memcmp(lhsInst, rhsInst, lhs.size); 127 | } 128 | 129 | private: 130 | template 131 | void CreateInstance(Args&&... args) 132 | { 133 | if constexpr (sizeof(T) > sizeof(storage)) 134 | { 135 | storage[0] = malloc(sizeof(T)); 136 | new(storage[0]) T{ std::forward(args)... }; 137 | } 138 | else 139 | new(storage) T{ std::forward(args)... }; 140 | 141 | size = sizeof(T); 142 | } 143 | 144 | [[nodiscard]] Impl::DelegateInstBase* GetInst() noexcept 145 | { 146 | return const_cast*>(static_cast(this)->GetInst()); 147 | } 148 | 149 | [[nodiscard]] const Impl::DelegateInstBase* GetInst() const noexcept 150 | { 151 | if (!size) return nullptr; 152 | 153 | if (const auto heap = GetHeap()) 154 | return reinterpret_cast*>(heap); 155 | 156 | return reinterpret_cast*>(&storage); 157 | } 158 | 159 | [[nodiscard]] void* GetHeap() noexcept 160 | { 161 | return (size > sizeof(storage)) ? storage[0] : nullptr; 162 | } 163 | 164 | [[nodiscard]] const void* GetHeap() const noexcept 165 | { 166 | return (size > sizeof(storage)) ? storage[0] : nullptr; 167 | } 168 | 169 | private: 170 | constexpr static auto StorageSize = 3; 171 | void* storage[StorageSize]; 172 | BSBase::uint32 size; 173 | }; 174 | 175 | template 176 | [[nodiscard]] bool operator!=(const Delegate& lhs, const Delegate& rhs) 177 | { 178 | return !(lhs == rhs); 179 | } 180 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/DelegateInst.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Impl 8 | { 9 | template 10 | class DelegateInstBase 11 | { 12 | public: 13 | virtual ~DelegateInstBase() = default; 14 | 15 | virtual R Execute(Args... args) const = 0; 16 | }; 17 | 18 | template 19 | class DelegateInstFunction final : public DelegateInstBase 20 | { 21 | using Func = R(*)(Args...); 22 | 23 | public: 24 | DelegateInstFunction(Func inFn) 25 | : fn(inFn) {} 26 | 27 | R Execute(Args... args) const override 28 | { 29 | return (*fn)(args...); 30 | } 31 | 32 | private: 33 | Func fn; 34 | }; 35 | 36 | template 37 | class DelegateInstMethod final : public DelegateInstBase 38 | { 39 | using Func = R(Class::*)(Args...); 40 | 41 | public: 42 | DelegateInstMethod(Class* inInst, Func inFn) 43 | : inst(inInst), fn(inFn) {} 44 | 45 | R Execute(Args... args) const override 46 | { 47 | return (inst->*fn)(args...); 48 | } 49 | 50 | private: 51 | Class* inst; 52 | Func fn; 53 | }; 54 | 55 | template 56 | class DelegateInstConstMethod final : public DelegateInstBase 57 | { 58 | static_assert(std::is_const_v); 59 | using Func = R(Class::*)(Args...) const; 60 | 61 | public: 62 | DelegateInstConstMethod(Class* inInst, Func inFn) 63 | : inst(inInst), fn(inFn) {} 64 | 65 | R Execute(Args... args) const override 66 | { 67 | return (inst->*fn)(args...); 68 | } 69 | 70 | private: 71 | Class* inst; 72 | Func fn; 73 | }; 74 | 75 | template 76 | class DelegateInstFunctor final : public DelegateInstBase 77 | { 78 | using Func = std::decay_t; 79 | 80 | public: 81 | DelegateInstFunctor(const Func& inFn) 82 | : fn(inFn) {} 83 | 84 | DelegateInstFunctor(Func&& inFn) 85 | : fn(std::move(inFn)) {} 86 | 87 | R Execute(Args... args) const override 88 | { 89 | return fn(args...); 90 | } 91 | 92 | private: 93 | std::remove_const_t fn; 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Delegate.h" 5 | 6 | template 7 | class Event; 8 | 9 | template 10 | class Event final 11 | { 12 | public: 13 | Event() noexcept = default; 14 | Event(std::nullptr_t) noexcept : Event() {} 15 | 16 | Event(const Event&) noexcept = default; 17 | Event(Event&&) noexcept = default; 18 | 19 | Event& operator=(const Event&) noexcept = default; 20 | Event& operator=(Event&&) noexcept = default; 21 | 22 | ~Event() = default; 23 | 24 | Event& operator+=(const Delegate& fn) 25 | { 26 | funcs.emplace_back(fn); 27 | return *this; 28 | } 29 | 30 | Event& operator+=(Delegate&& fn) 31 | { 32 | funcs.emplace_back(std::move(fn)); 33 | return *this; 34 | } 35 | 36 | Event& operator-=(const Delegate& fn) 37 | { 38 | funcs.erase(std::find(funcs.cbegin(), funcs.cend(), fn)); 39 | return *this; 40 | } 41 | 42 | void operator()(Args... args) const 43 | { 44 | for (auto& fn : funcs) 45 | fn(args...); 46 | } 47 | 48 | template 49 | R operator()(Fn&& mixer, const Args&... args) 50 | { 51 | if (!IsBound()) return R(); 52 | 53 | R ret = funcs[0](args...); 54 | 55 | const size_t size = Size(); 56 | for (size_t i = 1; i < size; ++i) 57 | ret = mixer(ret, funcs[i](args...)); 58 | 59 | return ret; 60 | } 61 | 62 | [[nodiscard]] operator bool() const noexcept 63 | { 64 | return !funcs.empty(); 65 | } 66 | 67 | [[nodiscard]] bool IsBound() const noexcept 68 | { 69 | return !funcs.empty(); 70 | } 71 | 72 | size_t Size() const noexcept 73 | { 74 | return funcs.size(); 75 | } 76 | 77 | void Clear() noexcept 78 | { 79 | for (auto& fn : funcs) 80 | fn.Clear(); 81 | 82 | funcs.clear(); 83 | } 84 | 85 | friend bool operator==(const Event& lhs, const Event& rhs); 86 | 87 | private: 88 | [[nodiscard]] decltype(auto) begin() noexcept 89 | { 90 | return funcs.begin(); 91 | } 92 | 93 | [[nodiscard]] decltype(auto) begin() const noexcept 94 | { 95 | return funcs.begin(); 96 | } 97 | 98 | [[nodiscard]] decltype(auto) end() noexcept 99 | { 100 | return funcs.end(); 101 | } 102 | 103 | [[nodiscard]] decltype(auto) end() const noexcept 104 | { 105 | return funcs.end(); 106 | } 107 | 108 | private: 109 | std::vector> funcs; 110 | }; 111 | 112 | template 113 | [[nodiscard]] bool operator==(const Event& lhs, const Event& rhs) 114 | { 115 | const size_t size = lhs.Size(); 116 | if (size != rhs.Size()) 117 | return false; 118 | 119 | for (size_t i = 0; i < size; ++i) 120 | if (lhs.funcs[i] != rhs.funcs[i]) 121 | return false; 122 | 123 | return true; 124 | } 125 | 126 | template 127 | [[nodiscard]] bool operator!=(const Event& lhs, const Event& rhs) 128 | { 129 | return !(lhs == rhs); 130 | } 131 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Json.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nlohmann/json.hpp" 4 | #include "BSBase/Type.h" 5 | #include "CharSet.h" 6 | 7 | using Json = nlohmann::json; 8 | using JsonValue = nlohmann::detail::value_t; 9 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "fmt/format.h" 6 | #include "spdlog/sinks/daily_file_sink.h" 7 | #include "spdlog/sinks/null_sink.h" 8 | #include "spdlog/sinks/stdout_color_sinks.h" 9 | #include "Name.h" 10 | 11 | namespace spdlog 12 | { 13 | class logger; 14 | } 15 | 16 | namespace Sink 17 | { 18 | using NullSink = spdlog::sinks::null_sink_mt; 19 | 20 | using StdoutSink = spdlog::sinks::stdout_color_sink_mt; 21 | using StderrSink = spdlog::sinks::stderr_color_sink_mt; 22 | 23 | class FileSink final : public spdlog::sinks::base_sink 24 | { 25 | using FileNameCalc = spdlog::sinks::daily_filename_calculator; 26 | 27 | public: 28 | FileSink(StringView inName, BSBase::uint8 inHour = 0u, 29 | BSBase::uint8 inMinute = 0u, bool inTruncate = false, BSBase::uint16 inMaxFile = 0u) 30 | : maxFile(inMaxFile), 31 | hour(inHour), 32 | minute(inMinute), 33 | truncate(inTruncate) 34 | { 35 | name = std::filesystem::current_path().parent_path() / "Saved" / "Logs" / inName; 36 | create_directories(name); 37 | 38 | const auto now = spdlog::log_clock::now(); 39 | const auto curFilename = FileNameCalc::calc_filename(name.string(), GetNow(now)); 40 | 41 | fileHelper.open(curFilename, truncate); 42 | timePoint = GetNextTimePoint(); 43 | 44 | if (maxFile > 0) 45 | InitFileNames(); 46 | } 47 | 48 | [[nodiscard]] String GetName() const noexcept 49 | { 50 | return name.u16string(); 51 | } 52 | 53 | private: 54 | void sink_it_(const spdlog::details::log_msg& msg) override 55 | { 56 | const auto time = msg.time; 57 | bool should_rotate = time >= timePoint; 58 | 59 | if (should_rotate) 60 | { 61 | auto filename = FileNameCalc::calc_filename(name.string(), GetNow(time)); 62 | fileHelper.open(filename, truncate); 63 | timePoint = GetNextTimePoint(); 64 | } 65 | 66 | spdlog::memory_buf_t formatted; 67 | base_sink::formatter_->format(msg, formatted); 68 | fileHelper.write(formatted); 69 | 70 | if (should_rotate && maxFile > 0) 71 | DeleteOld(); 72 | } 73 | 74 | void flush_() override 75 | { 76 | fileHelper.flush(); 77 | } 78 | 79 | void InitFileNames() 80 | { 81 | using spdlog::details::os::path_exists; 82 | 83 | filenames = decltype(filenames)(static_cast(maxFile)); 84 | std::vector names; 85 | auto now = spdlog::log_clock::now(); 86 | 87 | while (filenames.size() < maxFile) 88 | { 89 | auto filename = FileNameCalc::calc_filename(name.string(), GetNow(now)); 90 | if (!path_exists(filename)) 91 | break; 92 | 93 | names.emplace_back(filename); 94 | now -= std::chrono::hours(24); 95 | } 96 | 97 | for (auto iter = names.rbegin(); iter != names.rend(); ++iter) 98 | filenames.push_back(std::move(*iter)); 99 | } 100 | 101 | [[nodiscard]] tm GetNow(spdlog::log_clock::time_point tp) 102 | { 103 | const auto now = spdlog::log_clock::to_time_t(tp); 104 | return spdlog::details::os::localtime(now); 105 | } 106 | 107 | [[nodiscard]] spdlog::log_clock::time_point GetNextTimePoint() 108 | { 109 | const auto now = spdlog::log_clock::now(); 110 | 111 | auto date = GetNow(now); 112 | date.tm_hour = hour; 113 | date.tm_min = minute; 114 | date.tm_sec = 0; 115 | 116 | const auto time = spdlog::log_clock::from_time_t(std::mktime(&date)); 117 | return (time > now) ? time : (time + std::chrono::hours(24)); 118 | } 119 | 120 | void DeleteOld() 121 | { 122 | using spdlog::details::os::filename_to_str; 123 | using spdlog::details::os::remove_if_exists; 124 | 125 | auto crtFile = fileHelper.filename(); 126 | if (filenames.full()) 127 | { 128 | const auto oldName = std::move(filenames.front()); 129 | filenames.pop_front(); 130 | 131 | if (remove_if_exists(oldName.string())) 132 | filenames.push_back(std::move(crtFile)); 133 | } 134 | 135 | filenames.push_back(std::move(crtFile)); 136 | } 137 | 138 | private: 139 | std::filesystem::path name; 140 | spdlog::details::circular_q filenames; 141 | spdlog::log_clock::time_point timePoint; 142 | spdlog::details::file_helper fileHelper; 143 | 144 | BSBase::uint16 maxFile; 145 | BSBase::uint8 hour; 146 | BSBase::uint8 minute; 147 | BSBase::uint8 truncate : 1; 148 | }; 149 | } 150 | 151 | enum class LogVerbosity : BSBase::uint8 152 | { 153 | Debug, Log, Display, Warn, Error, Critical 154 | }; 155 | 156 | class CORE_API Logger final 157 | { 158 | public: 159 | Logger(Name name); 160 | 161 | Logger(const Logger&) = default; 162 | Logger(Logger&&) noexcept = default; 163 | 164 | Logger& operator=(const Logger&) = default; 165 | Logger& operator=(Logger&&) noexcept = default; 166 | 167 | ~Logger(); 168 | 169 | template 170 | void Log(LogVerbosity verbosity, const String& format, Args&&... args) 171 | { 172 | LogImpl(verbosity, fmt::format(format, std::forward(args)...)); 173 | } 174 | 175 | template 176 | size_t AddSink(Args&&... args) 177 | { 178 | return AddSinkImpl(std::make_shared(std::forward(args)...)); 179 | } 180 | 181 | void RemoveSink(size_t index); 182 | 183 | private: 184 | void LogImpl(LogVerbosity verbosity, String message); 185 | size_t AddSinkImpl(std::shared_ptr sink); 186 | 187 | private: 188 | std::shared_ptr impl; 189 | }; 190 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Name.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "BSBase/Type.h" 6 | #include "BSMath/Hash.h" 7 | #include "CharSet.h" 8 | 9 | enum class NameCase : BSBase::uint8 10 | { 11 | IgnoreCase, CompareCase 12 | }; 13 | 14 | #define REGISTER_NAME(name,num) name = num, 15 | enum class ReservedName : BSBase::uint32 16 | { 17 | #include "ReservedName.inl" 18 | ReserveNum 19 | }; 20 | #undef REGISTER_NAME 21 | 22 | namespace Impl 23 | { 24 | class CORE_API NameBase 25 | { 26 | public: 27 | NameBase(StringView str); 28 | NameBase(ReservedName name); 29 | 30 | NameBase(const NameBase&) = default; 31 | NameBase(NameBase&&) noexcept = default; 32 | 33 | NameBase& operator=(const NameBase&) = default; 34 | NameBase& operator=(NameBase&&) noexcept = default; 35 | 36 | ~NameBase() = default; 37 | 38 | [[nodiscard]] const ::String& ToString() const { return *ptr; } 39 | [[nodiscard]] const ::String* GetPtr() const noexcept { return ptr; } 40 | [[nodiscard]] size_t GetLength() const noexcept { return ptr->size(); } 41 | 42 | friend bool operator==(const NameBase& lhs, const NameBase& rhs) noexcept; 43 | friend bool operator<(const NameBase& lhs, const NameBase& rhs); 44 | 45 | private: 46 | const ::String* ptr; 47 | }; 48 | 49 | NO_ODR ::String ToLower(StringView str) 50 | { 51 | ::String ret(str.data()); 52 | std::transform(ret.begin(), ret.end(), ret.begin(), [](Char c) { return std::tolower(c, std::locale{}); }); 53 | return ret; 54 | } 55 | 56 | [[nodiscard]] NO_ODR bool operator==(const Impl::NameBase& lhs, const Impl::NameBase& rhs) noexcept { return lhs.ptr == rhs.ptr; } 57 | [[nodiscard]] NO_ODR bool operator!=(const Impl::NameBase& lhs, const Impl::NameBase& rhs) noexcept { return !(lhs == rhs); } 58 | 59 | /// @warning That function has the same cost as string. 60 | [[nodiscard]] NO_ODR bool operator<(const Impl::NameBase& lhs, const Impl::NameBase& rhs) { return lhs.ToString() < rhs.ToString(); } 61 | 62 | /// @warning That function has the same cost as string. 63 | [[nodiscard]] NO_ODR bool operator>(const Impl::NameBase& lhs, const Impl::NameBase& rhs) { return rhs < lhs; } 64 | 65 | /// @warning That function has the same cost as string. 66 | [[nodiscard]] NO_ODR bool operator<=(const Impl::NameBase& lhs, const Impl::NameBase& rhs) { return !(lhs > rhs); } 67 | 68 | /// @warning That function has the same cost as string. 69 | [[nodiscard]] NO_ODR bool operator>=(const Impl::NameBase& lhs, const Impl::NameBase& rhs) { return !(lhs < rhs); } 70 | } 71 | 72 | template 73 | class CasedName; 74 | 75 | template <> 76 | class CORE_API CasedName final : public Impl::NameBase 77 | { 78 | public: 79 | CasedName(StringView str) 80 | : NameBase(Impl::ToLower(str)) {} 81 | 82 | CasedName(ReservedName name = ReservedName::None) 83 | : NameBase(name) {} 84 | }; 85 | 86 | template <> 87 | class CORE_API CasedName final : public Impl::NameBase 88 | { 89 | public: 90 | CasedName(StringView str) 91 | : NameBase(str) {} 92 | 93 | CasedName(ReservedName name = ReservedName::None) 94 | : NameBase(name) {} 95 | }; 96 | 97 | using Name = CasedName; 98 | 99 | namespace BSMath 100 | { 101 | template 102 | struct Hash> final 103 | { 104 | [[nodiscard]] size_t operator()(const CasedName& value) const noexcept 105 | { 106 | return reinterpret_cast(value.GetPtr()); 107 | } 108 | }; 109 | } 110 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CharSet.h" 4 | 5 | // The warning is unnecessary because the path variable is used internally. 6 | #pragma warning(disable: 4251) 7 | 8 | #ifndef NDEBUG 9 | namespace Impl 10 | { 11 | [[nodiscard]] CORE_API bool IsDebuggingImpl() noexcept; 12 | } 13 | #endif 14 | 15 | [[nodiscard]] NO_ODR bool IsDebugging() noexcept 16 | { 17 | #ifdef NDEBUG 18 | return false; 19 | #else 20 | return Impl::IsDebuggingImpl(); 21 | #endif 22 | } 23 | 24 | class CORE_API Dll final 25 | { 26 | public: 27 | explicit Dll(StringView inPame); 28 | 29 | Dll(const Dll& other); 30 | Dll(Dll&& other) noexcept; 31 | 32 | Dll& operator=(const Dll& other); 33 | Dll& operator=(Dll&& other) noexcept; 34 | 35 | ~Dll(); 36 | 37 | [[nodiscard]] void* GetSymbol(StringView name) const; 38 | [[nodiscard]] void* FindSymbol(StringView name) const noexcept; 39 | 40 | template 41 | [[nodiscard]] T& GetSymbol(StringView name) const 42 | { 43 | return *reinterpret_cast(GetSymbol(name)); 44 | } 45 | 46 | template 47 | [[nodiscard]] T* FindSymbol(StringView name) const noexcept 48 | { 49 | return reinterpret_cast(FindSymbol(name)); 50 | } 51 | 52 | template 53 | decltype(auto) Call(StringView name, Args&&... args) const 54 | { 55 | return GetSymbol(name)(std::forward(args)...); 56 | } 57 | 58 | private: 59 | void* dll; 60 | String path; 61 | }; 62 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/ReservedName.inl: -------------------------------------------------------------------------------- 1 | REGISTER_NAME(None, 0) 2 | 3 | REGISTER_NAME(Assert, 50) 4 | REGISTER_NAME(Ensure, 51) 5 | 6 | REGISTER_NAME(KeyCode, 95) 7 | REGISTER_NAME(MouseCode, 96) 8 | REGISTER_NAME(MouseAxis, 97) 9 | 10 | REGISTER_NAME(Escape, 100) 11 | REGISTER_NAME(One, 101) 12 | REGISTER_NAME(Two, 102) 13 | REGISTER_NAME(Three, 103) 14 | REGISTER_NAME(Four, 104) 15 | REGISTER_NAME(Five, 105) 16 | REGISTER_NAME(Six, 106) 17 | REGISTER_NAME(Seven, 107) 18 | REGISTER_NAME(Eight, 108) 19 | REGISTER_NAME(Nine, 109) 20 | REGISTER_NAME(Zero, 110) 21 | REGISTER_NAME(Minus, 111) 22 | REGISTER_NAME(Equals, 112) 23 | REGISTER_NAME(Back, 113) 24 | REGISTER_NAME(Tab, 114) 25 | REGISTER_NAME(Q, 115) 26 | REGISTER_NAME(W, 116) 27 | REGISTER_NAME(E, 117) 28 | REGISTER_NAME(R, 118) 29 | REGISTER_NAME(T, 119) 30 | REGISTER_NAME(Y, 120) 31 | REGISTER_NAME(U, 121) 32 | REGISTER_NAME(I, 122) 33 | REGISTER_NAME(O, 123) 34 | REGISTER_NAME(P, 124) 35 | REGISTER_NAME(LBracket, 125) 36 | REGISTER_NAME(RBracket, 126) 37 | REGISTER_NAME(Return, 127) 38 | REGISTER_NAME(LControl, 128) 39 | REGISTER_NAME(A, 129) 40 | REGISTER_NAME(S, 130) 41 | REGISTER_NAME(D, 131) 42 | REGISTER_NAME(F, 132) 43 | REGISTER_NAME(G, 133) 44 | REGISTER_NAME(H, 134) 45 | REGISTER_NAME(J, 135) 46 | REGISTER_NAME(K, 136) 47 | REGISTER_NAME(L, 137) 48 | REGISTER_NAME(Semicolon, 138) 49 | REGISTER_NAME(Apostrophe, 139) 50 | REGISTER_NAME(Grave, 140) 51 | REGISTER_NAME(LShift, 141) 52 | REGISTER_NAME(Backslash, 142) 53 | REGISTER_NAME(Z, 143) 54 | REGISTER_NAME(X, 144) 55 | REGISTER_NAME(C, 145) 56 | REGISTER_NAME(V, 146) 57 | REGISTER_NAME(B, 147) 58 | REGISTER_NAME(N, 148) 59 | REGISTER_NAME(M, 149) 60 | REGISTER_NAME(Comma, 150) 61 | REGISTER_NAME(Reriod, 151) 62 | REGISTER_NAME(Slash, 152) 63 | REGISTER_NAME(RShift, 153) 64 | REGISTER_NAME(Multiply, 154) 65 | REGISTER_NAME(LMenu, 155) 66 | REGISTER_NAME(Space, 156) 67 | REGISTER_NAME(Capital, 157) 68 | REGISTER_NAME(F1, 158) 69 | REGISTER_NAME(F2, 159) 70 | REGISTER_NAME(F3, 160) 71 | REGISTER_NAME(F4, 161) 72 | REGISTER_NAME(F5, 162) 73 | REGISTER_NAME(F6, 163) 74 | REGISTER_NAME(F7, 164) 75 | REGISTER_NAME(F8, 165) 76 | REGISTER_NAME(F9, 166) 77 | REGISTER_NAME(F10, 167) 78 | REGISTER_NAME(Numlock, 168) 79 | REGISTER_NAME(Scroll, 169) 80 | REGISTER_NAME(KP7, 170) 81 | REGISTER_NAME(KP8, 171) 82 | REGISTER_NAME(KP9, 172) 83 | REGISTER_NAME(Subtract, 173) 84 | REGISTER_NAME(KP4, 174) 85 | REGISTER_NAME(KP5, 175) 86 | REGISTER_NAME(KP6, 176) 87 | REGISTER_NAME(Add, 177) 88 | REGISTER_NAME(KP1, 178) 89 | REGISTER_NAME(KP2, 179) 90 | REGISTER_NAME(KP3, 180) 91 | REGISTER_NAME(KP0, 181) 92 | REGISTER_NAME(Decimal, 182) 93 | REGISTER_NAME(F11, 183) 94 | REGISTER_NAME(F12, 184) 95 | REGISTER_NAME(F13, 185) 96 | REGISTER_NAME(F14, 186) 97 | REGISTER_NAME(F15, 187) 98 | REGISTER_NAME(Kana, 188) 99 | REGISTER_NAME(Convert, 189) 100 | REGISTER_NAME(NoConvert, 190) 101 | REGISTER_NAME(Yen, 191) 102 | REGISTER_NAME(NumpadEquals, 191) 103 | REGISTER_NAME(CircumFlex, 192) 104 | REGISTER_NAME(At, 193) 105 | REGISTER_NAME(Colon, 194) 106 | REGISTER_NAME(Underline, 195) 107 | REGISTER_NAME(Kanji, 196) 108 | REGISTER_NAME(Stop, 197) 109 | REGISTER_NAME(Ax, 198) 110 | REGISTER_NAME(UnLabeled, 199) 111 | REGISTER_NAME(NumpadeEnter, 200) 112 | REGISTER_NAME(RControl, 201) 113 | REGISTER_NAME(NumpadComma, 202) 114 | REGISTER_NAME(Divide, 203) 115 | REGISTER_NAME(SysRq, 204) 116 | REGISTER_NAME(RMenu, 205) 117 | REGISTER_NAME(Pause, 206) 118 | REGISTER_NAME(Home, 207) 119 | REGISTER_NAME(Up, 208) 120 | REGISTER_NAME(Prior, 209) 121 | REGISTER_NAME(Left, 210) 122 | REGISTER_NAME(Right, 211) 123 | REGISTER_NAME(End, 212) 124 | REGISTER_NAME(Down, 213) 125 | REGISTER_NAME(Next, 214) 126 | REGISTER_NAME(Insert, 215) 127 | REGISTER_NAME(Delete, 216) 128 | REGISTER_NAME(LWin, 217) 129 | REGISTER_NAME(RWin, 218) 130 | REGISTER_NAME(Apps, 219) 131 | REGISTER_NAME(Power, 220) 132 | REGISTER_NAME(Sleep, 221) 133 | REGISTER_NAME(Wheel, 222) 134 | REGISTER_NAME(X1, 223) 135 | REGISTER_NAME(X2, 224) 136 | REGISTER_NAME(X3, 225) 137 | REGISTER_NAME(X4, 226) 138 | REGISTER_NAME(X5, 227) 139 | REGISTER_NAME(Shift, 250) 140 | REGISTER_NAME(Ctrl, 251) 141 | REGISTER_NAME(Alt, 252) 142 | -------------------------------------------------------------------------------- /Source/Runtime/Core/include/Timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "BSBase/Type.h" 5 | 6 | class CORE_API Timer final 7 | { 8 | public: 9 | Timer() { Reset(); } 10 | 11 | float Tick(); 12 | void Reset(); 13 | 14 | void Pause(); 15 | void Unpause(); 16 | 17 | [[nodiscard]] float GetTimeScale() const noexcept { return timeScale; }; 18 | void SetTimeScale(float scale = 1.0f) noexcept { timeScale = scale; }; 19 | 20 | [[nodiscard]] float GetDeltaTime() const noexcept { return deltaTime.count(); } 21 | 22 | private: 23 | std::chrono::high_resolution_clock::time_point startTime; 24 | std::chrono::high_resolution_clock::time_point pausedTime; 25 | 26 | std::chrono::duration deltaTime; 27 | std::chrono::duration pauseDeltaTime; 28 | 29 | float timeScale; 30 | BSBase::uint8 isPaused : 1; 31 | }; 32 | -------------------------------------------------------------------------------- /Source/Runtime/Core/src/Assertion.cpp: -------------------------------------------------------------------------------- 1 | #include "Assertion.h" 2 | 3 | #ifndef NDEBUG 4 | 5 | #include "Logger.h" 6 | 7 | void Impl::LogToFail(bool isCritical, const Char* expr, const Char* file, BSBase::int32 line, const String& msg) noexcept 8 | { 9 | static Logger assertLogger{ ReservedName::Assert }; 10 | static Logger ensureLogger{ ReservedName::Ensure }; 11 | static bool bInit = false; 12 | 13 | if (!bInit) 14 | { 15 | assertLogger.AddSink(STR("Assert.log")); 16 | ensureLogger.AddSink(STR("Ensure.log")); 17 | 18 | assertLogger.AddSink(); 19 | ensureLogger.AddSink(); 20 | 21 | bInit = true; 22 | } 23 | 24 | if (isCritical) 25 | { 26 | assertLogger.Log(LogVerbosity::Critical, 27 | STR("{} failed: {} {}, file: {}, line: {}"), 28 | isCritical ? STR("Assert") : STR("Ensure"), expr, msg, file, line 29 | ); 30 | } 31 | else 32 | { 33 | ensureLogger.Log(LogVerbosity::Error, 34 | STR("{} failed: {} {}, file: {}, line: {}"), 35 | isCritical ? STR("Assert") : STR("Ensure"), expr, msg, file, line 36 | ); 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Source/Runtime/Core/src/CharSet.cpp: -------------------------------------------------------------------------------- 1 | #include "CharSet.h" 2 | #include "utf8cpp/utf8.h" 3 | 4 | namespace Impl 5 | { 6 | std::string ToString(std::wstring_view from) 7 | { 8 | std::string to; 9 | 10 | if constexpr (sizeof(wchar_t) == 1) 11 | to = std::string(from.cbegin(), from.cend()); 12 | else if (sizeof(wchar_t) == 2) 13 | utf8::utf16to8(from.cbegin(), from.cend(), std::back_inserter(to)); 14 | else if (sizeof(wchar_t) == 4) 15 | utf8::utf32to8(from.cbegin(), from.cend(), std::back_inserter(to)); 16 | 17 | return to; 18 | } 19 | 20 | std::string ToString(std::u16string_view from) 21 | { 22 | std::string to; 23 | utf8::utf16to8(from.cbegin(), from.cend(), std::back_inserter(to)); 24 | return to; 25 | } 26 | 27 | std::string ToString(std::u32string_view from) 28 | { 29 | std::string to; 30 | utf8::utf32to8(from.cbegin(), from.cend(), std::back_inserter(to)); 31 | return to; 32 | } 33 | 34 | std::wstring ToWString(std::string_view from) 35 | { 36 | std::wstring to; 37 | 38 | if constexpr (sizeof(wchar_t) == 1) 39 | to = std::wstring(from.cbegin(), from.cend()); 40 | else if (sizeof(wchar_t) == 2) 41 | utf8::utf8to16(from.cbegin(), from.cend(), std::back_inserter(to)); 42 | else if (sizeof(wchar_t) == 4) 43 | utf8::utf8to32(from.cbegin(), from.cend(), std::back_inserter(to)); 44 | 45 | return to; 46 | } 47 | 48 | std::wstring ToWString(std::u16string_view from) 49 | { 50 | if constexpr (sizeof(wchar_t) == sizeof(char16_t)) 51 | { 52 | return std::wstring(from.cbegin(), from.cend()); 53 | } 54 | else 55 | { 56 | std::wstring to; 57 | utf8::utf16to8(from.cbegin(), from.cend(), std::back_inserter(to)); 58 | return to; 59 | } 60 | } 61 | 62 | std::wstring ToWString(std::u32string_view from) 63 | { 64 | if constexpr (sizeof(wchar_t) == sizeof(char)) 65 | { 66 | std::wstring to; 67 | utf8::utf32to8(from.cbegin(), from.cend(), std::back_inserter(to)); 68 | return to; 69 | } 70 | else 71 | return std::wstring{}; 72 | } 73 | 74 | std::u16string ToU16String(std::string_view from) 75 | { 76 | std::u16string to; 77 | utf8::utf8to16(from.cbegin(), from.cend(), std::back_inserter(to)); 78 | return to; 79 | } 80 | 81 | std::u16string ToU16String(std::wstring_view from) 82 | { 83 | if constexpr (sizeof(wchar_t) == sizeof(char16_t)) 84 | { 85 | return std::u16string(from.cbegin(), from.cend()); 86 | } 87 | else 88 | { 89 | std::u16string to; 90 | utf8::utf8to16(from.cbegin(), from.cend(), std::back_inserter(to)); 91 | return to; 92 | } 93 | } 94 | 95 | std::u32string ToU32String(std::string_view from) 96 | { 97 | std::u32string to; 98 | utf8::utf8to32(from.cbegin(), from.cend(), std::back_inserter(to)); 99 | return to; 100 | } 101 | 102 | std::u32string ToU32String(std::wstring_view from) 103 | { 104 | if constexpr (sizeof(wchar_t) == sizeof(char)) 105 | { 106 | std::u32string to; 107 | utf8::utf8to32(from.cbegin(), from.cend(), std::back_inserter(to)); 108 | return to; 109 | } 110 | else 111 | return std::u32string{}; 112 | } 113 | } -------------------------------------------------------------------------------- /Source/Runtime/Core/src/ConfigFile.cpp: -------------------------------------------------------------------------------- 1 | #include "ConfigFile.h" 2 | #include 3 | #include "Logger.h" 4 | 5 | const String* ConfigFile::ConfigSection::operator[](const String& key) const noexcept 6 | { 7 | const auto iter = data.find(key); 8 | return iter != data.cend() ? &(iter->second) : nullptr; 9 | } 10 | 11 | namespace 12 | { 13 | String TrimInline(const String& s) noexcept 14 | { 15 | auto begin = s.find_first_not_of(u" \t"); 16 | auto end = s.find_last_not_of(u" \t"); 17 | 18 | if (begin == String::npos || end == String::npos || begin > end) 19 | return u""; 20 | 21 | return s.substr(begin, end - begin + 1); 22 | } 23 | } 24 | 25 | bool ConfigFile::LoadFromFile(const String& fileName) noexcept 26 | { 27 | Clear(); 28 | 29 | const auto path = STR("Config\\") + fileName + STR(".ini"); 30 | IfStream fin{ reinterpret_cast(path.c_str()) }; 31 | if (!fin) return false; 32 | 33 | String section{ u"" }; 34 | std::unordered_map sectionMap; 35 | String line; 36 | 37 | while (std::getline(fin, line)) 38 | { 39 | if ((line = TrimInline(line)).empty() || line[0] == STR('#')) 40 | continue; 41 | 42 | if (line[0] == STR('[')) 43 | { 44 | const auto end = line.rfind(STR(']')); 45 | if (end == String::npos || end == 1) 46 | { 47 | Clear(); 48 | return false; 49 | } 50 | 51 | data.emplace(std::make_pair(section, ConfigSection{ sectionMap })); 52 | section = TrimInline(line.substr(1, end - 1)); 53 | } 54 | else 55 | { 56 | const auto eq = line.find(STR('=')); 57 | if (eq == String::npos || eq == 0 || eq == line.length() - 1) 58 | { 59 | Clear(); 60 | return false; 61 | } 62 | 63 | const auto left = TrimInline(line.substr(0, eq)); 64 | const auto right = TrimInline(line.substr(eq + 1)); 65 | 66 | if (left.empty() || right.empty()) 67 | { 68 | Clear(); 69 | return false; 70 | } 71 | 72 | sectionMap.emplace(std::make_pair(left, right)); 73 | } 74 | } 75 | 76 | data.emplace(std::make_pair(section, ConfigSection{ sectionMap })); 77 | isAvailable = true; 78 | return true; 79 | } 80 | 81 | void ConfigFile::Clear() noexcept 82 | { 83 | data.clear(); 84 | isAvailable = false; 85 | } 86 | 87 | const String* ConfigFile::operator()(const String& sectionName, const String& keyName) const noexcept 88 | { 89 | const auto iter = data.find(sectionName); 90 | if (iter == data.cend()) return nullptr; 91 | return (iter->second)[keyName]; 92 | } 93 | -------------------------------------------------------------------------------- /Source/Runtime/Core/src/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | #include "spdlog/spdlog.h" 3 | 4 | namespace 5 | { 6 | [[nodiscard]] constexpr spdlog::level::level_enum ToSpdLogLevel(LogVerbosity verbosity) noexcept 7 | { 8 | constexpr spdlog::level::level_enum mapper[]{ spdlog::level::debug, spdlog::level::info, 9 | spdlog::level::info, spdlog::level::warn, spdlog::level::err, spdlog::level::critical }; 10 | 11 | return mapper[static_cast(verbosity)]; 12 | } 13 | } 14 | 15 | Logger::Logger(Name name) 16 | { 17 | auto str = CastCharSet(StringView{ name.ToString() }); 18 | if (const auto logger = spdlog::get(str)) 19 | { 20 | impl = logger; 21 | return; 22 | } 23 | 24 | impl = std::make_shared(std::move(str)); 25 | spdlog::details::registry::instance().initialize_logger(impl); 26 | impl->set_level(spdlog::level::debug); 27 | impl->flush_on(spdlog::level::critical); 28 | } 29 | 30 | Logger::~Logger() 31 | { 32 | impl->flush(); 33 | } 34 | 35 | void Logger::RemoveSink(size_t index) 36 | { 37 | impl->sinks().erase(impl->sinks().cbegin() + index); 38 | } 39 | 40 | void Logger::LogImpl(LogVerbosity verbosity, String message) 41 | { 42 | impl->log(ToSpdLogLevel(verbosity), 43 | CastCharSet(StringView{ std::move(message) })); 44 | } 45 | 46 | size_t Logger::AddSinkImpl(std::shared_ptr sink) 47 | { 48 | impl->sinks().emplace_back(std::move(sink)); 49 | return impl->sinks().size() - 1; 50 | } 51 | -------------------------------------------------------------------------------- /Source/Runtime/Core/src/Name.cpp: -------------------------------------------------------------------------------- 1 | #include "Name.h" 2 | #include 3 | #include 4 | #include 5 | #include "BSMath/Hash.h" 6 | #include "Assertion.h" 7 | 8 | namespace 9 | { 10 | class NamePool final 11 | { 12 | public: 13 | static NamePool& Get() 14 | { 15 | return *inst; 16 | } 17 | 18 | NamePool() 19 | { 20 | reserveMapper.resize(static_cast(ReservedName::ReserveNum), nullptr); 21 | 22 | #define REGISTER_NAME(name, num) reserveMapper[num] = &*nameSet.insert(Impl::ToLower(STR(#name))).first; 23 | #include "ReservedName.inl" 24 | #undef REGISTER_NAME 25 | } 26 | 27 | const String* Insert(StringView str) 28 | { 29 | if (const auto ptr = Find(str)) 30 | return ptr; 31 | 32 | std::unique_lock lock{ mutex }; 33 | return &*nameSet.insert(String(str.data())).first; 34 | } 35 | 36 | const String* Find(StringView str) const 37 | { 38 | std::shared_lock lock{ mutex }; 39 | 40 | const auto iter = nameSet.find(String(str.data())); 41 | return iter != nameSet.end() ? &*iter : nullptr; 42 | } 43 | 44 | const String* Find(ReservedName name) const 45 | { 46 | Assert(name < ReservedName::ReserveNum); 47 | return reserveMapper[static_cast(name)]; 48 | } 49 | 50 | private: 51 | static std::unique_ptr inst; 52 | 53 | std::unordered_set> nameSet; 54 | std::vector reserveMapper; 55 | mutable std::shared_mutex mutex; 56 | }; 57 | 58 | std::unique_ptr NamePool::inst = std::make_unique(); 59 | } 60 | 61 | namespace Impl 62 | { 63 | NameBase::NameBase(StringView str) 64 | : ptr(NamePool::Get().Insert(str)) {} 65 | 66 | NameBase::NameBase(ReservedName name) 67 | : ptr(NamePool::Get().Find(name)) {} 68 | } 69 | -------------------------------------------------------------------------------- /Source/Runtime/Core/src/PlatformWindow.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WINDOWS 2 | 3 | #define NOMINMAX 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include "Platform.h" 7 | #include 8 | #include "Assertion.h" 9 | 10 | #ifndef NDEBUG 11 | 12 | bool Impl::IsDebuggingImpl() noexcept 13 | { 14 | return IsDebuggerPresent(); 15 | } 16 | 17 | #endif 18 | 19 | namespace 20 | { 21 | String GetLastErrorMsg() 22 | { 23 | constexpr static auto MaxSize = 512; 24 | static wchar_t buffer[MaxSize]; 25 | 26 | FormatMessageW( 27 | FORMAT_MESSAGE_FROM_SYSTEM, 28 | nullptr, 29 | GetLastError(), 30 | 0, 31 | buffer, 32 | MaxSize, 33 | nullptr 34 | ); 35 | 36 | const std::wstring msg(buffer); 37 | return String(msg.begin(), msg.end()); 38 | } 39 | } 40 | 41 | Dll::Dll(StringView inPath) 42 | : path(inPath.data()) 43 | { 44 | const std::wstring wPath(path.cbegin(), path.cend()); 45 | dll = LoadLibraryW(wPath.c_str()); 46 | AssertMsg(dll, STR("{}: cannot load module, {}"), path, GetLastErrorMsg()); 47 | } 48 | 49 | Dll::Dll(const Dll& other) 50 | : path(other.path) 51 | { 52 | if (dll) FreeLibrary(reinterpret_cast(dll)); 53 | dll = other.dll; 54 | } 55 | 56 | Dll::Dll(Dll&& other) noexcept 57 | : path(std::move(other.path)) 58 | { 59 | if (dll) FreeLibrary(reinterpret_cast(dll)); 60 | dll = std::move(other.dll); 61 | } 62 | 63 | Dll& Dll::operator=(const Dll& other) 64 | { 65 | if (dll) 66 | FreeLibrary(reinterpret_cast(dll)); 67 | 68 | dll = other.dll; 69 | path = other.path; 70 | return *this; 71 | } 72 | 73 | Dll& Dll::operator=(Dll&& other) noexcept 74 | { 75 | if (dll) 76 | FreeLibrary(reinterpret_cast(dll)); 77 | 78 | dll = std::move(other.dll); 79 | path = std::move(other.path); 80 | return *this; 81 | } 82 | 83 | Dll::~Dll() 84 | { 85 | FreeLibrary(reinterpret_cast(dll)); 86 | } 87 | 88 | void* Dll::GetSymbol(StringView name) const 89 | { 90 | const auto symbol = FindSymbol(name); 91 | if (EnsureMsg(symbol, STR("Path: {}, Name: {}, {}"), path, name, GetLastErrorMsg())) 92 | return symbol; 93 | 94 | return nullptr; 95 | } 96 | 97 | void* Dll::FindSymbol(StringView name) const noexcept 98 | { 99 | const auto buf = CastCharSet(StringView{ name }); 100 | return reinterpret_cast(GetProcAddress(reinterpret_cast(dll), buf.c_str())); 101 | } 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /Source/Runtime/Core/src/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | float Timer::Tick() 4 | { 5 | auto now = isPaused ? pausedTime : std::chrono::high_resolution_clock::now(); 6 | deltaTime = (now - startTime - pauseDeltaTime) * timeScale; 7 | 8 | pauseDeltaTime = std::chrono::duration(0.0f); 9 | startTime = std::move(now); 10 | return GetDeltaTime(); 11 | } 12 | 13 | void Timer::Reset() 14 | { 15 | startTime = pausedTime = std::chrono::high_resolution_clock::now(); 16 | deltaTime = pauseDeltaTime = std::chrono::duration(0.0f); 17 | timeScale = 1.0f; 18 | } 19 | 20 | void Timer::Pause() 21 | { 22 | if (isPaused) return; 23 | 24 | pausedTime = std::chrono::high_resolution_clock::now(); 25 | isPaused = true; 26 | } 27 | 28 | void Timer::Unpause() 29 | { 30 | if (!isPaused) return; 31 | 32 | pauseDeltaTime = std::chrono::high_resolution_clock::now() - pausedTime; 33 | isPaused = false; 34 | } 35 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Engine 2 | 3 | register_library () 4 | 5 | target_link_libraries (Engine PUBLIC Framework PRIVATE Input Thread Window) -------------------------------------------------------------------------------- /Source/Runtime/Engine/include/Engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | class ENGINE_API Engine final 6 | { 7 | public: 8 | Engine() noexcept 9 | : timer(), window(nullptr), thread(nullptr), input(nullptr), scene(nullptr) {} 10 | 11 | Engine(const Engine&) = delete; 12 | Engine(Engine&&) noexcept = delete; 13 | 14 | Engine& operator=(const Engine&) = delete; 15 | Engine& operator=(Engine&&) noexcept = delete; 16 | 17 | ~Engine() = default; 18 | 19 | [[nodiscard]] int32 Init() noexcept; 20 | [[nodiscard]] int32 Run() noexcept; 21 | void Release() noexcept; 22 | 23 | private: 24 | Timer timer; 25 | 26 | class WindowManager* window; 27 | class ThreadManager* thread; 28 | class PluginManager* plugin; 29 | 30 | class InputManager* input; 31 | class SceneManager* scene; 32 | class RenderManager* render; 33 | }; 34 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/include/Entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | #include 5 | #include 6 | #include "Component.h" 7 | 8 | #if WINDOWS 9 | # define FUNC_SIG __FUNCSIG__ 10 | #else 11 | # define FUNC_SIG __PRETTY_FUNCTION__ 12 | #endif 13 | 14 | class ENGINE_API Entity final 15 | { 16 | public: 17 | Entity() : components(), name() {} 18 | 19 | Entity(const Entity& other) 20 | : components(other.components) 21 | , name(other.name) 22 | { 23 | // Todo: Create name appender 24 | } 25 | 26 | Entity(Entity&& other) noexcept 27 | : components(std::move(other.components)) 28 | , name(std::move(other.name)) 29 | { 30 | // Todo: Create name appender 31 | other.components.clear(); 32 | } 33 | 34 | Entity& operator=(const Entity& other) 35 | { 36 | components = other.components; 37 | return *this; 38 | } 39 | 40 | Entity& operator=(Entity&& other) noexcept 41 | { 42 | components = std::move(other.components); 43 | other.components.clear(); 44 | return *this; 45 | } 46 | 47 | ~Entity(); 48 | 49 | template 50 | T* AddComponent() 51 | { 52 | static_assert(std::is_base_of_v); 53 | 54 | const Name name = GetComponentName(STR(FUNC_SIG)); 55 | const auto ret = CreateComponent(name, this); 56 | components.insert(std::make_pair(name, ret)); 57 | return ret; 58 | } 59 | 60 | template 61 | [[nodiscard]] T* GetComponent() 62 | { 63 | return const_cast(static_cast(this)->GetComponent()); 64 | } 65 | 66 | template 67 | [[nodiscard]] const T* GetComponent() const 68 | { 69 | const auto& comps = GetComponents(); 70 | return comps.empty() ? nullptr : comps[0]; 71 | } 72 | 73 | template 74 | [[nodiscard]] std::vector GetComponents() 75 | { 76 | return reinterpret_cast&> 77 | (const_cast(this)->GetComponents()); 78 | } 79 | 80 | template 81 | [[nodiscard]] std::vector GetComponents() const 82 | { 83 | static_assert(std::is_base_of_v); 84 | 85 | const auto iter = components.find(GetComponentName(STR(FUNC_SIG))); 86 | if (iter == components.cend()) 87 | return std::vector{}; 88 | 89 | const auto& comps = iter->second; 90 | const size_t size = comps.size(); 91 | std::vector ret(size, nullptr); 92 | 93 | for (size_t i = 0; i < size; ++i) 94 | ret[i] = reinterpret_cast(comps[i]); 95 | 96 | return ret; 97 | } 98 | 99 | [[nodiscard]] Json Serialize() const; 100 | void Deserialize(const Json& json); 101 | 102 | [[nodiscard]] const String& GetName() const noexcept { return name; } 103 | void SetName(StringView inName) noexcept; 104 | 105 | private: 106 | [[nodiscard]] static Name GetComponentName(StringView functionName); 107 | 108 | public: 109 | Event onChangedName; 110 | 111 | private: 112 | std::unordered_map, Hash> components; 113 | String name; 114 | }; 115 | 116 | #undef FUNC_SIG 117 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/include/Scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Entity.h" 4 | #include 5 | 6 | class ENGINE_API Scene final 7 | { 8 | public: 9 | Scene() = default; 10 | 11 | Scene(const Scene&) = delete; 12 | Scene(Scene&&) noexcept = default; 13 | 14 | Scene& operator=(const Scene&) = delete; 15 | Scene& operator=(Scene&&) noexcept = default; 16 | 17 | ~Scene() = default; 18 | 19 | bool Load(Name inName) noexcept; 20 | bool Save(Name inName) const noexcept; 21 | bool Save() const noexcept { return Save(name); } 22 | 23 | Entity* AddEntity(const String& name); 24 | Entity* AddEntity(const String& name, Entity* prefab); 25 | 26 | bool RemoveEntity(const String& name); 27 | bool RemoveEntity(Entity* entity) { return RemoveEntity(entity->GetName()); } 28 | 29 | [[nodiscard]] Entity* GetEntity(const String& name) noexcept { return &entities.at(name); } 30 | [[nodiscard]] const Entity* GetEntity(const String& name) const noexcept { return &entities.at(name); } 31 | 32 | [[nodiscard]] Name GetName() const noexcept { return name; } 33 | 34 | private: 35 | Entity* AddEntityImpl(Entity& entity); 36 | void RemoveEntityImpl(Entity& entity); 37 | 38 | void OnChangedName(Entity& entity, const String& after, const String& before); 39 | 40 | private: 41 | std::unordered_map entities; 42 | Name name; 43 | }; 44 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/include/SceneManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Manager.h" 4 | #include 5 | #include 6 | #include "Accessor.h" 7 | #include "Event.h" 8 | #include "Scene.h" 9 | 10 | class ENGINE_API SceneManager final : public Manager, private Accessor 11 | { 12 | public: 13 | [[nodiscard]] bool Update(float deltaTime) noexcept override; 14 | 15 | std::future Load(Name name) noexcept; 16 | bool Save(Name name) const noexcept { return scenes[isFrontScene].Save(name); } 17 | bool Save() const noexcept { return scenes[isFrontScene].Save(); } 18 | 19 | void RegisterUpdate(const Delegate& callback); 20 | void RegisterUpdate(Delegate&& callback); 21 | 22 | [[nodiscard]] Scene& GetScene() noexcept { return scenes[isFrontScene]; } 23 | [[nodiscard]] const Scene& GetScene() const noexcept { return scenes[isFrontScene]; } 24 | 25 | void Exit() noexcept { isEnd = true; } 26 | 27 | private: 28 | bool LoadImpl(Name name) noexcept; 29 | 30 | private: 31 | Event onUpdates[2]; 32 | Scene scenes[2]; 33 | 34 | std::shared_mutex mutex; 35 | 36 | uint8 isFrontScene : 1; 37 | uint8 isLoadScene : 1; 38 | uint8 isSwapScene : 1; 39 | uint8 isEnd : 1; 40 | }; 41 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/include/Transform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Accessor.h" 4 | #include "Component.h" 5 | #include 6 | #include "BSMath.h" 7 | 8 | class ENGINE_API Transform final : public Component, public Accessor 9 | { 10 | public: 11 | using Component::Component; 12 | 13 | [[nodiscard]] Matrix4 GetWorldMatrix(); 14 | 15 | [[nodiscard]] Json Serialize() const override; 16 | void Deserialize(const Json& json) override; 17 | 18 | [[nodiscard]] Transform* GetParent() noexcept { return parent; } 19 | [[nodiscard]] const Transform* GetParent() const noexcept { return parent; } 20 | 21 | void SetParent(Transform* inParent) noexcept { parent = inParent; } 22 | 23 | [[nodiscard]] const Vector3& GetPosition() const noexcept { return position; } 24 | [[nodiscard]] const Rotator& GetRotation() const noexcept { return rotation; } 25 | [[nodiscard]] const Vector3& GetScale() const noexcept { return scale; } 26 | 27 | void SetPosition(const Vector3& inPos) noexcept { position = inPos; isUpdated = false; } 28 | void SetRotation(const Rotator& inRot) noexcept { rotation = inRot; isUpdated = false; } 29 | void SetScale(const Vector3& inScale) noexcept { scale = inScale; isUpdated = false; } 30 | 31 | private: 32 | void SetParentTransform(); 33 | 34 | private: 35 | Matrix4 mat; 36 | 37 | Vector3 position; 38 | Rotator rotation; 39 | Vector3 scale; 40 | 41 | Transform* parent; 42 | String parentName; 43 | 44 | uint8 isUpdated : 1; 45 | }; 46 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/src/Engine.cpp: -------------------------------------------------------------------------------- 1 | #include "Engine.h" 2 | #include "Accessor.h" 3 | #include "SceneManager.h" 4 | #include "InputManager.h" 5 | #include "ThreadManager.h" 6 | #include "WindowManager.h" 7 | 8 | namespace 9 | { 10 | constexpr static int32 Success = 0; 11 | constexpr static int32 FailCreate = 1; 12 | constexpr static int32 FailInit = 2; 13 | 14 | template 15 | int32 CreateManager(T*& manager, Args&&... args) noexcept 16 | { 17 | manager = new T{ std::forward(args)... }; 18 | if (!manager) return FailCreate; 19 | 20 | Accessor::SetManager(manager); 21 | return manager->Init() ? Success : FailInit; 22 | } 23 | 24 | template 25 | void RemoveManager(T*& manager) noexcept 26 | { 27 | Accessor::SetManager(nullptr); 28 | manager->Release(); 29 | delete manager; 30 | manager = nullptr; 31 | } 32 | } 33 | 34 | int32 Engine::Init() noexcept 35 | { 36 | int32 error = CreateManager(window, Name{ STR(STRINGIFY(GAME_NAME)) }); 37 | if (error) return error; 38 | 39 | error = CreateManager(thread); 40 | if (error) return error; 41 | 42 | error = CreateManager(plugin); 43 | if (error) return error; 44 | 45 | error = CreateManager(input); 46 | if (error) return error; 47 | 48 | error = CreateManager(scene); 49 | if (error) return error; 50 | 51 | error = CreateManager(render); 52 | if (error) return error; 53 | 54 | timer.Reset(); 55 | return 0; 56 | } 57 | 58 | int32 Engine::Run() noexcept 59 | { 60 | bool isEnd = false; 61 | 62 | while (!isEnd) 63 | { 64 | const float deltaTime = timer.Tick(); 65 | 66 | if (!window->Update(deltaTime)) 67 | isEnd = true; 68 | 69 | if (!input->Update(deltaTime)) 70 | isEnd = true; 71 | 72 | if (!scene->Update(deltaTime)) 73 | isEnd = true; 74 | 75 | if (!render->Update(deltaTime)) 76 | isEnd = true; 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | void Engine::Release() noexcept 83 | { 84 | RemoveManager(render); 85 | RemoveManager(scene); 86 | RemoveManager(input); 87 | RemoveManager(plugin); 88 | RemoveManager(thread); 89 | RemoveManager(window); 90 | } 91 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/src/Entity.cpp: -------------------------------------------------------------------------------- 1 | #include "Entity.h" 2 | 3 | Entity::~Entity() 4 | { 5 | for (const auto& compList : components) 6 | for (const auto comp : compList.second) 7 | delete comp; 8 | 9 | components.clear(); 10 | } 11 | 12 | Json Entity::Serialize() const 13 | { 14 | Json json = Json::object(); 15 | json["name"] = name; 16 | Json& comps = json["components"] = Json::array(); 17 | 18 | for (const auto& compList : components) 19 | for (const auto* comp : compList.second) 20 | comps.push_back(comp->Serialize()); 21 | 22 | return json; 23 | } 24 | 25 | void Entity::Deserialize(const Json& json) 26 | { 27 | name = json["name"].get(); 28 | 29 | for (const auto& comp : json["components"]) 30 | { 31 | const auto strName = comp["name"].get(); 32 | const Name name = strName.c_str(); 33 | components[name].emplace_back(CreateComponent(name, this))->Deserialize(comp); 34 | } 35 | } 36 | 37 | void Entity::SetName(StringView inName) noexcept 38 | { 39 | const String beforeName = name; 40 | name = std::move(inName); 41 | 42 | onChangedName(*this, name, beforeName); 43 | } 44 | 45 | Name Entity::GetComponentName(StringView functionName) 46 | { 47 | const size_t begin = functionName.find(STR('<')); 48 | const size_t end = functionName.find(STR('>')); 49 | return functionName.substr(begin + 1, end - begin - 1); 50 | } 51 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/src/Scene.cpp: -------------------------------------------------------------------------------- 1 | #include "Scene.h" 2 | #include 3 | #include "Core.h" 4 | 5 | bool Scene::Load(Name inName) noexcept 6 | { 7 | name = inName; 8 | entities.clear(); 9 | 10 | std::filesystem::path path{ STR("Assets") }; 11 | path /= name.ToString(); 12 | 13 | std::ifstream stream{ path.string() }; 14 | Json json; 15 | stream >> json; 16 | 17 | const auto jsonStr = json["name"].get(); 18 | const Name jsonName{ jsonStr.c_str() }; 19 | if (Ensure(jsonName == name)) 20 | return false; 21 | 22 | for (const auto& entity : json["entities"]) 23 | { 24 | const auto name = entity["name"].get(); 25 | entities[name].Deserialize(entity); 26 | } 27 | 28 | return true; 29 | } 30 | 31 | bool Scene::Save(Name inName) const noexcept 32 | { 33 | std::filesystem::path path{ STR("Assets") }; 34 | path /= inName.ToString(); 35 | 36 | Json json = Json::object(); 37 | json["name"] = inName.ToString(); 38 | 39 | Json& entityJson = json["entities"] = Json::array(); 40 | 41 | for (const auto& entity : entities) 42 | entityJson.push_back(entity.second.Serialize()); 43 | 44 | std::ofstream stream{ path.string() }; 45 | stream << json; 46 | return true; 47 | } 48 | 49 | Entity* Scene::AddEntity(const String& name) 50 | { 51 | Entity& entity = entities.insert(std::make_pair(name, Entity{})).first->second; 52 | return AddEntityImpl(entity); 53 | } 54 | 55 | Entity* Scene::AddEntity(const String& name, Entity* prefab) 56 | { 57 | Entity& entity = entities.insert(std::make_pair(name, Entity{ *prefab })).first->second; 58 | return AddEntityImpl(entity); 59 | } 60 | 61 | bool Scene::RemoveEntity(const String& name) 62 | { 63 | auto iter = entities.find(name); 64 | if (iter == entities.end()) 65 | return false; 66 | 67 | RemoveEntityImpl(iter->second); 68 | entities.erase(iter); 69 | return true; 70 | } 71 | 72 | Entity* Scene::AddEntityImpl(Entity& entity) 73 | { 74 | entity.onChangedName += Delegate{ this, &Scene::OnChangedName }; 75 | return &entity; 76 | } 77 | 78 | void Scene::RemoveEntityImpl(Entity& entity) 79 | { 80 | entity.onChangedName -= Delegate{ this, &Scene::OnChangedName }; 81 | } 82 | 83 | void Scene::OnChangedName(Entity& entity, const String& after, const String& before) 84 | { 85 | auto node = entities.extract(before); 86 | Assert(!node.empty()); 87 | 88 | node.key() = after; 89 | entities.insert(std::move(node)); 90 | } 91 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/src/SceneManager.cpp: -------------------------------------------------------------------------------- 1 | #include "SceneManager.h" 2 | #include "ThreadManager.h" 3 | 4 | bool SceneManager::Update(float deltaTime) noexcept 5 | { 6 | if (isSwapScene) 7 | { 8 | isFrontScene = !isFrontScene; 9 | isSwapScene = false; 10 | } 11 | 12 | onUpdates[isFrontScene](deltaTime); 13 | return !isEnd; 14 | } 15 | 16 | std::future SceneManager::Load(Name name) noexcept 17 | { 18 | Delegate load{ [this, name] { return LoadImpl(name); } }; 19 | return Accessor::GetManager()->AddTask(std::move(load)); 20 | } 21 | 22 | void SceneManager::RegisterUpdate(const Delegate& callback) 23 | { 24 | std::shared_lock lock{ mutex }; 25 | onUpdates[isFrontScene != isLoadScene] += callback; 26 | } 27 | 28 | void SceneManager::RegisterUpdate(Delegate&& callback) 29 | { 30 | std::shared_lock lock{ mutex }; 31 | onUpdates[isFrontScene != isLoadScene] += std::move(callback); 32 | } 33 | 34 | bool SceneManager::LoadImpl(Name name) noexcept 35 | { 36 | mutex.lock(); 37 | isLoadScene = true; 38 | mutex.unlock(); 39 | 40 | const bool isSuccess = scenes[!isFrontScene].Load(name); 41 | 42 | mutex.lock(); 43 | isLoadScene = false; 44 | isSwapScene = true; 45 | mutex.unlock(); 46 | 47 | return isSuccess; 48 | } 49 | -------------------------------------------------------------------------------- /Source/Runtime/Engine/src/Transform.cpp: -------------------------------------------------------------------------------- 1 | #include "Transform.h" 2 | #include "Scene.h" 3 | #include "SceneManager.h" 4 | 5 | REGISTER_COMPONENT(Transform) 6 | 7 | Matrix4 Transform::GetWorldMatrix() 8 | { 9 | if (!isUpdated) 10 | { 11 | mat = Creator::Matrix::FromTRS(position, rotation, scale); 12 | isUpdated = true; 13 | } 14 | 15 | if (!parentName.empty() && !parent) 16 | SetParentTransform(); 17 | 18 | const Matrix4 parentMat = parent ? 19 | parent->GetWorldMatrix() : Matrix4::Identity; 20 | 21 | return parentMat * mat; 22 | } 23 | 24 | Json Transform::Serialize() const 25 | { 26 | Json json = Json::object(); 27 | 28 | json["position"] = { position.x, position.y, position.z }; 29 | json["rotation"] = { rotation.roll, rotation.pitch, rotation.yaw }; 30 | json["scale"] = { scale.x, scale.y, scale.z }; 31 | 32 | if (parent) 33 | json["parent"] = parent->GetEntity()->GetName(); 34 | 35 | return json; 36 | } 37 | 38 | void Transform::Deserialize(const Json& json) 39 | { 40 | const auto inPos = json["position"].get>(); 41 | SetPosition(Vector3{ inPos.data() }); 42 | 43 | const auto inRot = json["rotation"].get>(); 44 | SetRotation(Rotator{ inRot.data() }); 45 | 46 | const auto inScale = json["scale"].get>(); 47 | SetScale(Vector3{ inScale.data() }); 48 | 49 | if (json.contains("parent")) 50 | parentName = json["parent"].get(); 51 | } 52 | 53 | void Transform::SetParentTransform() 54 | { 55 | const auto entity = Accessor::GetManager()->GetScene().GetEntity(parentName); 56 | if (entity) 57 | parent = entity->GetComponent(); 58 | } 59 | -------------------------------------------------------------------------------- /Source/Runtime/Framework/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Framework 2 | 3 | register_library () 4 | 5 | target_link_libraries (Framework PUBLIC Core) -------------------------------------------------------------------------------- /Source/Runtime/Framework/include/Accessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class Accessor 5 | { 6 | public: 7 | static void SetManager(T* inManager) noexcept { manager = inManager; } 8 | 9 | protected: 10 | [[nodiscard]] static T* GetManager() noexcept { return manager; } 11 | 12 | private: 13 | inline static T* manager = nullptr; 14 | }; 15 | -------------------------------------------------------------------------------- /Source/Runtime/Framework/include/Component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Name.h" 4 | #include "Json.h" 5 | 6 | class Entity; 7 | 8 | class FRAMEWORK_API Component 9 | { 10 | public: 11 | Component(Entity* inEntity) : entity(inEntity) {} 12 | virtual ~Component() {}; 13 | 14 | [[nodiscard]] virtual Json Serialize() const { return Json{}; } 15 | virtual void Deserialize(const Json& json) {} 16 | 17 | [[nodiscard]] Entity* GetEntity() noexcept { return entity; } 18 | [[nodiscard]] const Entity* GetEntity() const noexcept { return entity; } 19 | 20 | private: 21 | Entity* entity; 22 | }; 23 | 24 | template 25 | [[nodiscard]] T* CreateComponent(Name name, Entity* entity) 26 | { 27 | return reinterpret_cast(Impl::CreateComponent(name, entity)); 28 | } 29 | 30 | namespace Impl 31 | { 32 | template 33 | [[nodiscard]] Component* Create(Entity* entity) 34 | { 35 | return new T{ entity }; 36 | } 37 | 38 | [[nodiscard]] FRAMEWORK_API Component* CreateComponent(Name name, Entity* entity); 39 | FRAMEWORK_API void RegisterComponent(Name name, Component*(*ptr)(Entity*)); 40 | } 41 | 42 | #define REGISTER_COMPONENT(Class) \ 43 | static void _RegisterReflectionComponent(); \ 44 | namespace \ 45 | { \ 46 | struct _ComponentRegister \ 47 | { \ 48 | _ComponentRegister() \ 49 | { \ 50 | _RegisterReflectionComponent(); \ 51 | } \ 52 | }; \ 53 | } \ 54 | static const _ComponentRegister ADD_PREFIX(ComponentRegister_, __LINE__); \ 55 | static void _RegisterReflectionComponent() \ 56 | { \ 57 | Impl::RegisterComponent(Name{ STR(#Class) }, &Impl::Create); \ 58 | } 59 | -------------------------------------------------------------------------------- /Source/Runtime/Framework/include/Manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class FRAMEWORK_API Manager 4 | { 5 | public: 6 | Manager() = default; 7 | 8 | Manager(const Manager&) = delete; 9 | Manager(Manager&&) noexcept = delete; 10 | 11 | Manager& operator=(const Manager&) = delete; 12 | Manager& operator=(Manager&&) noexcept = delete; 13 | 14 | virtual ~Manager() = default; 15 | 16 | [[nodiscard]] virtual bool Init() noexcept; 17 | [[nodiscard]] virtual bool Update(float deltaTime) noexcept; 18 | virtual void Release() noexcept; 19 | }; 20 | -------------------------------------------------------------------------------- /Source/Runtime/Framework/src/Component.cpp: -------------------------------------------------------------------------------- 1 | #include "Component.h" 2 | #include 3 | #include "BSMath.h" 4 | 5 | namespace 6 | { 7 | class ComponentRegistry final 8 | { 9 | public: 10 | static ComponentRegistry& Get() 11 | { 12 | static std::unique_ptr inst = std::make_unique(); 13 | return *inst; 14 | } 15 | 16 | Component* CreateComponent(Name name, Entity* entity) const 17 | { 18 | return registry.find(name)->second(entity); 19 | } 20 | 21 | void RegisterComponent(Name name, Component*(*ptr)(Entity*)) 22 | { 23 | registry.insert(std::make_pair(name, ptr)); 24 | } 25 | 26 | private: 27 | std::unordered_map> registry; 28 | }; 29 | } 30 | 31 | Component* Impl::CreateComponent(Name name, Entity* entity) 32 | { 33 | return ComponentRegistry::Get().CreateComponent(name, entity); 34 | } 35 | 36 | void Impl::RegisterComponent(Name name, Component*(*ptr)(Entity*)) 37 | { 38 | ComponentRegistry::Get().RegisterComponent(name, ptr); 39 | } 40 | -------------------------------------------------------------------------------- /Source/Runtime/Framework/src/Manager.cpp: -------------------------------------------------------------------------------- 1 | #include "Manager.h" 2 | 3 | bool Manager::Init() noexcept { return true; } 4 | bool Manager::Update(float deltaTime) noexcept { return true; } 5 | void Manager::Release() noexcept {} 6 | -------------------------------------------------------------------------------- /Source/Runtime/Input/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Input 2 | 3 | register_library () 4 | 5 | target_link_libraries (Input PUBLIC Framework PRIVATE Window) -------------------------------------------------------------------------------- /Source/Runtime/Input/include/InputCode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "BSBase/Type.h" 6 | #include "Name.h" 7 | 8 | using BSBase::uint8; 9 | 10 | enum class KeyCode : uint8 11 | { 12 | Escape = 1, 13 | One, 14 | Two, 15 | Three, 16 | Four, 17 | Five, 18 | Six, 19 | Seven, 20 | Eight, 21 | Nine, 22 | Zero, 23 | Minus, 24 | Equals, 25 | Back, 26 | Tab, 27 | Q, 28 | W, 29 | E, 30 | R, 31 | T, 32 | Y, 33 | U, 34 | I, 35 | O, 36 | P, 37 | LBracket, 38 | RBracket, 39 | Return, 40 | LControl, 41 | A, 42 | S, 43 | D, 44 | F, 45 | G, 46 | H, 47 | J, 48 | K, 49 | L, 50 | Semicolon, 51 | Apostrophe, 52 | Grave, 53 | LShift, 54 | Backslash, 55 | Z, 56 | X, 57 | C, 58 | V, 59 | B, 60 | N, 61 | M, 62 | Comma, 63 | Reriod, 64 | Slash, 65 | RShift, 66 | Multiply, 67 | LMenu, 68 | Space, 69 | Capital, 70 | F1, 71 | F2, 72 | F3, 73 | F4, 74 | F5, 75 | F6, 76 | F7, 77 | F8, 78 | F9, 79 | F10, 80 | Numlock, 81 | Scroll, 82 | KP7, 83 | KP8, 84 | KP9, 85 | Subtract, 86 | KP4, 87 | KP5, 88 | KP6, 89 | Add, 90 | KP1, 91 | KP2, 92 | KP3, 93 | KP0, 94 | Decimal, 95 | F11, 96 | F12, 97 | F13, 98 | F14, 99 | F15, 100 | Kana, 101 | Convert, 102 | NoConvert, 103 | Yen, 104 | NumpadEquals, 105 | CircumFlex, 106 | At, 107 | Colon, 108 | Underline, 109 | Kanji, 110 | Stop, 111 | Ax, 112 | UnLabeled, 113 | NumpadeEnter, 114 | RControl, 115 | NumpadComma, 116 | Divide, 117 | SysRq, 118 | RMenu, 119 | Pause, 120 | Home, 121 | Up, 122 | Prior, 123 | Left, 124 | Right, 125 | End, 126 | Down, 127 | Next, 128 | Insert, 129 | Delete, 130 | LWin, 131 | RWin, 132 | Apps, 133 | Power, 134 | Sleep 135 | }; 136 | 137 | enum class MouseCode : uint8 { L, R, M, X1, X2, X3, X4, X5 }; 138 | enum class MouseAxis : uint8 { X, Y, Wheel }; 139 | 140 | enum class KeyMode : uint8 141 | { 142 | None = 0x00, 143 | Shift = 0x01, 144 | Ctrl = 0x02, 145 | Alt = 0x04, 146 | }; 147 | 148 | constexpr static uint8 ModeNum = 3; 149 | 150 | [[nodiscard]] constexpr KeyMode operator&(KeyMode a, KeyMode b) noexcept 151 | { 152 | return static_cast(static_cast(a) & static_cast(b)); 153 | } 154 | 155 | [[nodiscard]] constexpr KeyMode operator|(KeyMode a, KeyMode b) noexcept 156 | { 157 | return static_cast(static_cast(a) | static_cast(b)); 158 | } 159 | 160 | constexpr KeyMode& operator&=(KeyMode& lhs, KeyMode rhs) noexcept 161 | { 162 | return lhs = lhs & rhs; 163 | } 164 | 165 | constexpr KeyMode& operator|=(KeyMode& lhs, KeyMode rhs) noexcept 166 | { 167 | return lhs = lhs | rhs; 168 | } 169 | 170 | [[nodiscard]] NO_ODR Name FromKeyCode(KeyCode code) noexcept 171 | { 172 | return static_cast(static_cast 173 | (ReservedName::Escape) + static_cast(code) - 1); 174 | } 175 | 176 | [[nodiscard]] INPUT_API std::vector FromKeyMode(KeyMode mode) noexcept; 177 | [[nodiscard]] INPUT_API Name FromMouseCode(MouseCode code) noexcept; 178 | [[nodiscard]] INPUT_API Name FromMouseAxis(MouseAxis axis) noexcept; 179 | 180 | [[nodiscard]] INPUT_API std::optional ToKeyCode(Name name) noexcept; 181 | [[nodiscard]] INPUT_API std::optional ToKeyMode(Name name) noexcept; 182 | [[nodiscard]] INPUT_API std::optional ToMouseCode(Name name) noexcept; 183 | [[nodiscard]] INPUT_API std::optional ToMouseAxis(Name name) noexcept; 184 | -------------------------------------------------------------------------------- /Source/Runtime/Input/include/InputManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | #include "Accessor.h" 5 | #include "Manager.h" 6 | #include 7 | #include "InputCode.h" 8 | 9 | using InputCode = std::variant; 10 | 11 | struct INPUT_API InputAction final 12 | { 13 | constexpr InputAction(KeyCode inCode, KeyMode inMode = KeyMode::None) noexcept 14 | : code(inCode), mode(inMode) {} 15 | 16 | constexpr InputAction(MouseCode inCode, KeyMode inMode = KeyMode::None) noexcept 17 | : code(inCode), mode(inMode) {} 18 | 19 | constexpr InputAction(MouseAxis inAxis, KeyMode inMode = KeyMode::None) noexcept 20 | : code(inAxis), mode(inMode) {} 21 | 22 | 23 | Json Serialize() const; 24 | void Deserialize(const Json& json); 25 | 26 | InputCode code; 27 | KeyMode mode; 28 | }; 29 | 30 | struct INPUT_API InputAxis final 31 | { 32 | constexpr InputAxis(KeyCode inCode, float inScale = 1.0f) noexcept 33 | : code(inCode), scale(inScale) {} 34 | 35 | constexpr InputAxis(MouseCode inCode, float inScale = 1.0f) noexcept 36 | : code(inCode), scale(inScale) {} 37 | 38 | constexpr InputAxis(MouseAxis inAxis, float inScale = 1.0f) noexcept 39 | : code(inAxis), scale(inScale) {} 40 | 41 | Json Serialize() const; 42 | void Deserialize(const Json& json); 43 | 44 | InputCode code; 45 | float scale; 46 | }; 47 | 48 | struct AxisConfig final 49 | { 50 | float deadZone = 0.0f; 51 | float sensitivity = 1.0f; 52 | 53 | Json Serialize() const; 54 | void Deserialize(const Json& json); 55 | }; 56 | 57 | class INPUT_API InputManager : public Manager, private Accessor 58 | { 59 | public: 60 | [[nodiscard]] bool Init() noexcept override; 61 | [[nodiscard]] bool Update(float deltaTime) noexcept override; 62 | void Release() noexcept override; 63 | 64 | [[nodiscard]] float GetAxisValue(Name name) const noexcept; 65 | [[nodiscard]] float GetAxisValue(InputAxis axis) const noexcept; 66 | [[nodiscard]] float GetAxisValue(MouseAxis axis) const noexcept; 67 | 68 | [[nodiscard]] bool GetValue(Name name) const noexcept; 69 | [[nodiscard]] bool GetValue(InputAction action) const noexcept; 70 | 71 | [[nodiscard]] bool GetValue(KeyCode code) const noexcept; 72 | [[nodiscard]] bool GetValue(MouseCode code) const noexcept; 73 | 74 | private: 75 | [[nodiscard]] bool ReadKeyboard() noexcept; 76 | [[nodiscard]] bool ReadMouse() noexcept; 77 | 78 | [[nodiscard]] float FilterValue(InputCode code, float value) const noexcept; 79 | [[nodiscard]] bool GetModeValue(KeyMode mode) const noexcept; 80 | [[nodiscard]] bool GetSimpleModeValue(uint8 mode) const noexcept; 81 | 82 | private: 83 | struct InputImpl* impl; 84 | 85 | std::unordered_map, Hash> axises; 86 | std::unordered_map, Hash> actions; 87 | std::unordered_map axisConfigs; 88 | 89 | uint8 keyState[256]; 90 | uint8 mouseState[8]; 91 | IntVector3 mouseAxis; 92 | }; 93 | -------------------------------------------------------------------------------- /Source/Runtime/Input/src/InputCode.cpp: -------------------------------------------------------------------------------- 1 | #include "InputCode.h" 2 | #include 3 | 4 | std::vector FromKeyMode(KeyMode mode) noexcept 5 | { 6 | std::vector ret; 7 | 8 | for (uint8 i = 1; i <= ModeNum; ++i) 9 | if ((mode & static_cast(1 << i)) != KeyMode::None) 10 | ret.push_back(static_cast(static_cast(ReservedName::Shift) + i)); 11 | 12 | return ret; 13 | } 14 | 15 | Name FromMouseCode(MouseCode code) noexcept 16 | { 17 | constexpr static ReservedName names[] 18 | { 19 | ReservedName::L, ReservedName::R, ReservedName::M, ReservedName::X1, 20 | ReservedName::X2, ReservedName::X3, ReservedName::X4, ReservedName::X5 21 | }; 22 | 23 | if (static_cast(code) > static_cast(MouseCode::X5)) 24 | return ReservedName::None; 25 | 26 | return names[static_cast(code)]; 27 | } 28 | 29 | Name FromMouseAxis(MouseAxis axis) noexcept 30 | { 31 | constexpr static ReservedName names[] 32 | { 33 | ReservedName::X, ReservedName::Y, ReservedName::Wheel 34 | }; 35 | 36 | if (static_cast(axis) > static_cast(MouseAxis::Wheel)) 37 | return ReservedName::None; 38 | 39 | return names[static_cast(axis)]; 40 | } 41 | 42 | std::optional ToKeyCode(Name name) noexcept 43 | { 44 | constexpr static auto Begin = static_cast(ReservedName::Escape); 45 | constexpr static auto End = static_cast(ReservedName::Sleep); 46 | 47 | for (BSBase::uint32 i = Begin; i <= End; ++i) 48 | if (name == static_cast(i)) 49 | return static_cast(i - Begin + 1); 50 | 51 | return std::nullopt; 52 | } 53 | 54 | std::optional ToKeyMode(Name name) noexcept 55 | { 56 | static std::unordered_map> modes 57 | { 58 | std::make_pair(Name{ ReservedName::None }, KeyMode::None), 59 | std::make_pair(Name{ ReservedName::Shift }, KeyMode::Shift), 60 | std::make_pair(Name{ ReservedName::Ctrl }, KeyMode::Ctrl), 61 | std::make_pair(Name{ ReservedName::Alt }, KeyMode::Alt) 62 | }; 63 | 64 | const auto iter = modes.find(name); 65 | if (iter != modes.cend()) 66 | return iter->second; 67 | 68 | return std::nullopt; 69 | } 70 | 71 | std::optional ToMouseCode(Name name) noexcept 72 | { 73 | static std::unordered_map> codes 74 | { 75 | std::make_pair(Name{ ReservedName::L }, MouseCode::L), 76 | std::make_pair(Name{ ReservedName::R }, MouseCode::R), 77 | std::make_pair(Name{ ReservedName::M }, MouseCode::M), 78 | std::make_pair(Name{ ReservedName::X1 }, MouseCode::X1), 79 | std::make_pair(Name{ ReservedName::X2 }, MouseCode::X2), 80 | std::make_pair(Name{ ReservedName::X3 }, MouseCode::X3), 81 | std::make_pair(Name{ ReservedName::X4 }, MouseCode::X4), 82 | std::make_pair(Name{ ReservedName::X5 }, MouseCode::X5) 83 | }; 84 | 85 | const auto iter = codes.find(name); 86 | if (iter != codes.cend()) 87 | return iter->second; 88 | 89 | return std::nullopt; 90 | } 91 | 92 | std::optional ToMouseAxis(Name name) noexcept 93 | { 94 | if (name == ReservedName::X) 95 | return MouseAxis::X; 96 | if (name == ReservedName::Y) 97 | return MouseAxis::Y; 98 | if (name == ReservedName::Wheel) 99 | return MouseAxis::Wheel; 100 | 101 | return std::nullopt; 102 | } 103 | -------------------------------------------------------------------------------- /Source/Runtime/Input/src/InputManager.cpp: -------------------------------------------------------------------------------- 1 | #include "InputManager.h" 2 | #include 3 | #include "BSMath.h" 4 | #include "WindowManager.h" 5 | 6 | #pragma comment(lib, "dinput8.lib") 7 | #pragma comment(lib, "dxguid.lib") 8 | 9 | struct InputImpl final 10 | { 11 | IDirectInput8* directInput; 12 | IDirectInputDevice8* keyboard; 13 | IDirectInputDevice8* mouse; 14 | DIMOUSESTATE2 mouseState; 15 | }; 16 | 17 | namespace 18 | { 19 | template 20 | struct Overload : Ts... 21 | { 22 | using Ts::operator()...; 23 | }; 24 | 25 | template 26 | Overload(Ts ...)->Overload; 27 | 28 | String ToString(InputCode code) 29 | { 30 | const auto type = std::visit( 31 | Overload{ 32 | [] (KeyCode) { return ReservedName::KeyCode; }, 33 | [] (MouseCode) { return ReservedName::MouseCode; }, 34 | [] (MouseAxis) { return ReservedName::MouseAxis; } 35 | }, code); 36 | 37 | Name str = std::visit( 38 | Overload{ 39 | [] (KeyCode code) { return FromKeyCode(code); }, 40 | [] (MouseCode code) { return FromMouseCode(code); }, 41 | [] (MouseAxis axis) { return FromMouseAxis(axis); } 42 | }, code); 43 | 44 | return Name{ type }.ToString() + str.ToString(); 45 | } 46 | 47 | std::optional FromString(String str) 48 | { 49 | const static std::unordered_map(Name)>, Hash> parser 50 | { 51 | std::make_pair(ReservedName::KeyCode, ToKeyCode), 52 | std::make_pair(ReservedName::MouseCode, ToMouseCode), 53 | std::make_pair(ReservedName::MouseAxis, ToMouseAxis) 54 | }; 55 | 56 | const auto del = str.find(STR('.')); 57 | if (del == String::npos) 58 | return std::nullopt; 59 | 60 | const auto type = str.substr(0, del); 61 | const auto code = str.substr(del + 1); 62 | 63 | auto iter = parser.find(Name{ type.c_str() }); 64 | if (iter == parser.cend()) 65 | return std::nullopt; 66 | 67 | return iter->second(Name{ code.c_str() }); 68 | } 69 | 70 | void DeserializeCode(const Json& json, InputCode& code) 71 | { 72 | const auto str = json["code"].get(); 73 | const auto codeValue = FromString(CastCharSet(std::string_view{ str.c_str() })); 74 | if (codeValue) 75 | code = codeValue.value(); 76 | } 77 | } 78 | 79 | Json InputAction::Serialize() const 80 | { 81 | Json json = Json::object(); 82 | json["code"] = CastCharSet(StringView{ ToString(code).c_str() }); 83 | Json modes = json["mode"] = Json::array(); 84 | 85 | for (const auto mod : FromKeyMode(mode)) 86 | modes.emplace_back(CastCharSet(StringView{ mod.ToString().c_str() })); 87 | 88 | return json; 89 | } 90 | 91 | void InputAction::Deserialize(const Json& json) 92 | { 93 | DeserializeCode(json, code); 94 | mode = KeyMode::None; 95 | 96 | for (const auto mod : json["mode"]) 97 | { 98 | const auto modeAnsiStr = mod.get(); 99 | const auto modeStr = CastCharSet(std::string_view{ modeAnsiStr.c_str() }); 100 | mode |= ToKeyMode(Name{ modeStr.c_str() }).value(); 101 | } 102 | } 103 | 104 | Json InputAxis::Serialize() const 105 | { 106 | Json json = Json::object(); 107 | json["code"] = CastCharSet(StringView{ ToString(code).c_str() }); 108 | json["scale"] = scale; 109 | return json; 110 | } 111 | 112 | void InputAxis::Deserialize(const Json& json) 113 | { 114 | DeserializeCode(json, code); 115 | scale = json["scale"].get(); 116 | } 117 | 118 | Json AxisConfig::Serialize() const 119 | { 120 | Json json = Json::object(); 121 | json["deadZone"] = deadZone; 122 | json["sensitivity"] = sensitivity; 123 | return json; 124 | } 125 | 126 | void AxisConfig::Deserialize(const Json& json) 127 | { 128 | deadZone = json["deadZone"].get(); 129 | sensitivity = json["sensitivity"].get(); 130 | } 131 | 132 | bool InputManager::Init() noexcept 133 | { 134 | impl = new InputImpl; 135 | 136 | const auto hInst = reinterpret_cast 137 | (Accessor::GetManager()->GetHandle().hInstance); 138 | 139 | HRESULT result = DirectInput8Create(hInst, DIRECTINPUT_VERSION, 140 | IID_IDirectInput8, reinterpret_cast(&impl->directInput), nullptr); 141 | 142 | if (FAILED(result)) return false; 143 | 144 | result = impl->directInput->CreateDevice(GUID_SysKeyboard, &impl->keyboard, nullptr); 145 | if (FAILED(result)) return false; 146 | 147 | result = impl->keyboard->SetDataFormat(&c_dfDIKeyboard); 148 | if (FAILED(result)) return false; 149 | 150 | const auto hWnd = reinterpret_cast 151 | (Accessor::GetManager()->GetHandle().hInstance); 152 | 153 | result = impl->keyboard->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE); 154 | if (FAILED(result)) return false; 155 | 156 | result = impl->keyboard->Acquire(); 157 | if (FAILED(result)) return false; 158 | 159 | result = impl->directInput->CreateDevice(GUID_SysMouse, &impl->mouse, nullptr); 160 | if (FAILED(result)) return false; 161 | 162 | result = impl->mouse->SetDataFormat(&c_dfDIMouse2); 163 | if (FAILED(result)) return false; 164 | 165 | result = impl->mouse->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 166 | if (FAILED(result)) return false; 167 | 168 | result = impl->mouse->Acquire(); 169 | if (FAILED(result)) return false; 170 | 171 | return true; 172 | } 173 | 174 | bool InputManager::Update(float deltaTime) noexcept 175 | { 176 | bool result = ReadKeyboard(); 177 | if(!result) return false; 178 | 179 | result = ReadMouse(); 180 | if(!result) return false; 181 | 182 | return true; 183 | } 184 | 185 | void InputManager::Release() noexcept 186 | { 187 | if (impl->mouse) 188 | { 189 | impl->mouse->Unacquire(); 190 | impl->mouse->Release(); 191 | impl->mouse = nullptr; 192 | } 193 | 194 | if (impl->keyboard) 195 | { 196 | impl->keyboard->Unacquire(); 197 | impl->keyboard->Release(); 198 | impl->keyboard = nullptr; 199 | } 200 | 201 | if (impl->directInput) 202 | { 203 | impl->directInput->Release(); 204 | impl->directInput = nullptr; 205 | } 206 | 207 | delete impl; 208 | } 209 | 210 | float InputManager::GetAxisValue(Name name) const noexcept 211 | { 212 | const auto iter = axises.find(name); 213 | if (iter != axises.cend()) return 0.0f; 214 | 215 | float value = 0.0f; 216 | 217 | for (auto axis : iter->second) 218 | value += GetAxisValue(axis); 219 | 220 | return value; 221 | } 222 | 223 | float InputManager::GetAxisValue(InputAxis axis) const noexcept 224 | { 225 | const auto get = Overload{ 226 | [this] (KeyCode code) { return GetValue(code) ? 1.0f : 0.0f; }, 227 | [this] (MouseCode code) { return GetValue(code) ? 1.0f : 0.0f; }, 228 | [this] (MouseAxis axis) { return GetAxisValue(axis); } 229 | }; 230 | 231 | return std::visit(get, axis.code) * axis.scale; 232 | } 233 | 234 | float InputManager::GetAxisValue(MouseAxis axis) const noexcept 235 | { 236 | if (static_cast(axis) > static_cast(MouseAxis::Wheel)) 237 | return 0.0f; 238 | 239 | return FilterValue(axis, static_cast(mouseAxis[static_cast(axis)])); 240 | } 241 | 242 | bool InputManager::GetValue(Name name) const noexcept 243 | { 244 | const auto iter = actions.find(name); 245 | if (iter != actions.cend()) return false; 246 | 247 | for (auto action : iter->second) 248 | if (GetValue(action)) 249 | return true; 250 | 251 | return false; 252 | } 253 | 254 | bool InputManager::GetValue(InputAction action) const noexcept 255 | { 256 | const auto get = Overload{ 257 | [this](KeyCode code) { return GetValue(code); }, 258 | [this](MouseCode code) { return GetValue(code); }, 259 | [this](MouseAxis axis) { return GetAxisValue(axis) != 0.0f; } 260 | }; 261 | 262 | return std::visit(get, action.code) && GetModeValue(action.mode); 263 | } 264 | 265 | bool InputManager::GetValue(KeyCode code) const noexcept 266 | { 267 | if (static_cast(code) > static_cast(KeyCode::Sleep)) 268 | return false; 269 | 270 | return keyState[static_cast(code)] & 0x80; 271 | } 272 | 273 | bool InputManager::GetValue(MouseCode code) const noexcept 274 | { 275 | if (static_cast(code) > static_cast(MouseCode::X5)) 276 | return false; 277 | 278 | return mouseState[static_cast(code)] & 0x80; 279 | } 280 | 281 | bool InputManager::ReadKeyboard() noexcept 282 | { 283 | const HRESULT result = impl->keyboard->GetDeviceState( 284 | sizeof(keyState), reinterpret_cast(&keyState)); 285 | 286 | if (FAILED(result)) 287 | { 288 | if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED)) 289 | impl->keyboard->Acquire(); 290 | else 291 | return false; 292 | } 293 | 294 | return true; 295 | } 296 | 297 | bool InputManager::ReadMouse() noexcept 298 | { 299 | const HRESULT result = impl->mouse->GetDeviceState( 300 | sizeof(DIMOUSESTATE2), reinterpret_cast(&impl->mouseState)); 301 | 302 | if (FAILED(result)) 303 | { 304 | if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED)) 305 | impl->mouse->Acquire(); 306 | else 307 | return false; 308 | } 309 | 310 | mouseAxis = IntVector3{ impl->mouseState.lX, impl->mouseState.lY, -impl->mouseState.lZ }; 311 | memcpy(mouseState, impl->mouseState.rgbButtons, 8); 312 | return true; 313 | } 314 | 315 | float InputManager::FilterValue(InputCode code, float value) const noexcept 316 | { 317 | const auto config = axisConfigs.find(code); 318 | 319 | if (config != axisConfigs.cend()) 320 | { 321 | const auto cfg = config->second; 322 | 323 | value = value >= 0.0f 324 | ? Max(0, Lerp(0.0f, 1.0f, GetRangePct(value, cfg.deadZone, 1.0f))) 325 | : Min(0, Lerp(-1.0f, 0.0f, GetRangePct(value, -1.0f, cfg.deadZone))); 326 | 327 | value *= cfg.sensitivity; 328 | } 329 | 330 | return value; 331 | } 332 | 333 | bool InputManager::GetModeValue(KeyMode mode) const noexcept 334 | { 335 | bool ret = true; 336 | 337 | for (uint8 i = 1; i <= ModeNum; ++i) 338 | if (static_cast(mode) & 1 << i) 339 | ret = ret && GetSimpleModeValue(i - 1); 340 | 341 | return ret; 342 | } 343 | 344 | bool InputManager::GetSimpleModeValue(uint8 mode) const noexcept 345 | { 346 | const static std::vector mapper[3] 347 | { 348 | { KeyCode::LShift, KeyCode::RShift }, 349 | { KeyCode::LControl, KeyCode::RControl }, 350 | { KeyCode::LMenu, KeyCode::RMenu } 351 | }; 352 | 353 | for (const auto code : mapper[mode]) 354 | if (GetValue(code)) 355 | return true; 356 | 357 | return false; 358 | } 359 | -------------------------------------------------------------------------------- /Source/Runtime/Launch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Launch 2 | 3 | set (SRCS "Launch.cpp") 4 | 5 | if (${PLATFORM_WINDOWS}) 6 | add_executable (${CMAKE_PROJECT_NAME} WIN32 "LaunchWindows.cpp" ${SRCS}) 7 | else () 8 | message (FATAL_ERROR "Unsupported OS!") 9 | endif () 10 | 11 | target_link_libraries (${CMAKE_PROJECT_NAME} PRIVATE Engine) -------------------------------------------------------------------------------- /Source/Runtime/Launch/Launch.cpp: -------------------------------------------------------------------------------- 1 | #include "Engine.h" 2 | 3 | int32 Main(StringView cmdLine) 4 | { 5 | Engine engine; 6 | 7 | int32 error = engine.Init(); 8 | if (error) return error; 9 | 10 | error = engine.Run(); 11 | engine.Release(); 12 | return error; 13 | } 14 | -------------------------------------------------------------------------------- /Source/Runtime/Launch/LaunchWindows.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WINDOWS 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define NOMINMAX 5 | 6 | #include "Core.h" 7 | 8 | // @note: Error if you include windows.h first 9 | #include 10 | 11 | extern int32 Main(StringView cmdLine); 12 | 13 | String ProcessCommandLine() 14 | { 15 | const auto cmd = std::wstring{ GetCommandLineW() }; 16 | return CastCharSet(std::wstring_view{ cmd.c_str() }); 17 | } 18 | 19 | int32 WINAPI WinMain(_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE, _In_ char*, _In_ int32) 20 | { 21 | const auto cmdLine = ProcessCommandLine(); 22 | return Main(StringView{ cmdLine.c_str() }); 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /Source/Runtime/Plugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Plugin 2 | 3 | register_library () 4 | 5 | target_link_libraries (Plugin PUBLIC Framework) -------------------------------------------------------------------------------- /Source/Runtime/Plugin/include/PluginManager.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blas1n/BSEngine/2e16a5b136c3065e185c7376f60cff3ab8f393c2/Source/Runtime/Plugin/include/PluginManager.h -------------------------------------------------------------------------------- /Source/Runtime/Plugin/src/PluginManager.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blas1n/BSEngine/2e16a5b136c3065e185c7376f60cff3ab8f393c2/Source/Runtime/Plugin/src/PluginManager.cpp -------------------------------------------------------------------------------- /Source/Runtime/RHI/include/Mesh.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blas1n/BSEngine/2e16a5b136c3065e185c7376f60cff3ab8f393c2/Source/Runtime/RHI/include/Mesh.h -------------------------------------------------------------------------------- /Source/Runtime/RHI/include/RHI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | class RHIMesh; 6 | class RHIShader; 7 | class RHITexture; 8 | 9 | class RHI_API RHI 10 | { 11 | public: 12 | virtual ~RHI() = default; 13 | 14 | virtual RHIMesh* CreateMesh() { return nullptr; } 15 | virtual RHIMesh* CreateShader() { return nullptr; } 16 | virtual RHIMesh* CreateTexture() { return nullptr; } 17 | }; 18 | -------------------------------------------------------------------------------- /Source/Runtime/RHI/include/RHIDef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "BSMath/Color.h" 5 | #include "Assertion.h" 6 | 7 | struct DepthStencil 8 | { 9 | DepthStencil(float inDepth, BSBase::uint32 inStencil) 10 | : depth(inDepth), stencil(inStencil) {} 11 | 12 | float depth; 13 | BSBase::uint32 stencil; 14 | }; 15 | 16 | bool operator==(const DepthStencil& lhs, const DepthStencil& rhs) 17 | { 18 | return (lhs.depth == rhs.depth) && (lhs.stencil == rhs.stencil); 19 | } 20 | 21 | bool operator!=(const DepthStencil& lhs, const DepthStencil& rhs) 22 | { 23 | return !(lhs == rhs); 24 | } 25 | 26 | class RHI_API ClearValue final 27 | { 28 | public: 29 | ClearValue() noexcept : value() {} 30 | 31 | explicit ClearValue(BSMath::Color inColor) 32 | : value(std::move(inColor)) {} 33 | 34 | explicit ClearValue(float inDepth, uint32 inStencil = 0) 35 | : value(std::in_place_type, inDepth, inStencil) {} 36 | 37 | BSMath::Color GetColor() const noexcept 38 | { 39 | Assert(std::holds_alternative(value)); 40 | return std::get(value); 41 | } 42 | 43 | DepthStencil GetDepthStencil() const noexcept 44 | { 45 | Assert(std::holds_alternative(value)); 46 | return std::get(value); 47 | } 48 | 49 | friend bool operator==(const ClearValue& lhs, const ClearValue& rhs); 50 | 51 | private: 52 | std::variant value; 53 | }; 54 | 55 | bool operator==(const ClearValue& lhs, const ClearValue& rhs) 56 | { 57 | return lhs.value == rhs.value; 58 | } 59 | 60 | bool operator!=(const ClearValue& lhs, const ClearValue& rhs) 61 | { 62 | return !(lhs == rhs); 63 | } 64 | 65 | enum class PixelFormat : BSBase::uint8 66 | { 67 | Unknown, 68 | A32B32G32R32F, 69 | B8G8R8A8, 70 | G8, 71 | G16, 72 | DXT1, 73 | DXT3, 74 | DXT5, 75 | UYVY, 76 | FloatRGB, 77 | FloatRGBA, 78 | DepthStencil, 79 | ShadowDepth, 80 | R32_FLOAT, 81 | G16R16, 82 | G16R16F, 83 | G16R16F_FILTER, 84 | G32R32F, 85 | A2B10G10R10, 86 | A16B16G16R16, 87 | D24, 88 | R16F, 89 | R16F_FILTER, 90 | BC5, 91 | V8U8, 92 | A1, 93 | FloatR11G11B10, 94 | A8, 95 | R32_UINT, 96 | R32_SINT, 97 | PVRTC2, 98 | PVRTC4, 99 | R16_UINT, 100 | R16_SINT, 101 | R16G16B16A16_UINT, 102 | R16G16B16A16_SINT, 103 | R5G6B5_UNORM, 104 | R8G8B8A8, 105 | A8R8G8B8, 106 | BC4, 107 | R8G8, 108 | ATC_RGB, 109 | ATC_RGBA_E, 110 | ATC_RGBA_I, 111 | X24_G8, 112 | ETC1, 113 | ETC2_RGB, 114 | ETC2_RGBA, 115 | R32G32B32A32_UINT, 116 | R16G16_UINT, 117 | ASTC_4x4, 118 | ASTC_6x6, 119 | ASTC_8x8, 120 | ASTC_10x10, 121 | ASTC_12x12, 122 | BC6H, 123 | BC7, 124 | R8_UINT, 125 | L8, 126 | XGXR8, 127 | R8G8B8A8_UINT, 128 | R8G8B8A8_SNORM, 129 | R16G16B16A16_UNORM, 130 | R16G16B16A16_SNORM, 131 | PLATFORM_HDR_0, 132 | PLATFORM_HDR_1, 133 | PLATFORM_HDR_2, 134 | NV12, 135 | R32G32_UINT, 136 | ETC2_R11_EAC, 137 | ETC2_RG11_EAC, 138 | R8, 139 | MAX 140 | }; 141 | -------------------------------------------------------------------------------- /Source/Runtime/RHI/include/Shader.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blas1n/BSEngine/2e16a5b136c3065e185c7376f60cff3ab8f393c2/Source/Runtime/RHI/include/Shader.h -------------------------------------------------------------------------------- /Source/Runtime/RHI/include/Texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BSMath/Vector.h" 4 | #include "RHIDef.h" 5 | 6 | using BSBase::uint32; 7 | 8 | enum class TextureCreateFlags : uint32 9 | { 10 | None = 0, 11 | RenderTargetable = 1 << 0, 12 | ResolveTargetable = 1 << 1, 13 | DepthStencilTargetable = 1 << 2, 14 | ShaderResource = 1 << 3, 15 | SRGB = 1 << 4, 16 | CPUWritable = 1 << 5, 17 | NoTiling = 1 << 6, 18 | VideoDecode = 1 << 7, 19 | Dynamic = 1 << 8, 20 | InputAttachmentRead = 1 << 9, 21 | Foveation = 1 << 10, 22 | Memoryless = 1 << 11, 23 | GenerateMipCapable = 1 << 12, 24 | FastVRAMPartialAlloc = 1 << 13, 25 | DisableSRVCreation = 1 << 14, 26 | DisableDCC = 1 << 15, 27 | UAV = 1 << 16, 28 | Presentable = 1 << 17, 29 | CPUReadback = 1 << 18, 30 | OfflineProcessed = 1 << 19, 31 | FastVRAM = 1 << 20, 32 | HideInVisualizeTexture = 1 << 21, 33 | Virtual = 1 << 22, 34 | TargetArraySlicesIndependently = 1 << 23, 35 | Shared = 1 << 24, 36 | NoFastClear = 1 << 25, 37 | DepthStencilResolveTarget = 1 << 26, 38 | Streamable = 1 << 27, 39 | NoFastClearFinalize = 1 << 28, 40 | AFRManual = 1 << 29, 41 | ReduceMemoryWithTilingMode = 1 << 30, 42 | Transient = 1 << 31, 43 | } 44 | 45 | class RHI_API RHITexture 46 | { 47 | RHITexture(uint32 inNumMips, uint32 inNumSamples, iPixelFormat inFormat, TextureCreateFlags inFlags, const ClearValue& inClearValue) 48 | : clearValue(inClearValue) 49 | , numMips(inNumMips) 50 | , numSamples(inNumSamples) 51 | , flags(inFlags) 52 | , format(inFormat) {} 53 | 54 | virtual void* GetNativeResource() const noexcept { return nullptr; } 55 | virtual void* GetShaderResourceView() const noexcept { return nullptr; } 56 | 57 | virtual IntVector GetSize() const noexcept = 0; 58 | 59 | ClearValue GetClearValue() const noexcept { return clearValue; } 60 | 61 | uint32 GetNumMipmaps() const noexcept { return numMips; } 62 | uint32 GetNumSamples() const noexcept { return numSamples; } 63 | 64 | TextureCreateFlags GetFlags() const noexcept { return flags; } 65 | PixelFormat GetFormat() const noexcept { return format; } 66 | 67 | private: 68 | ClearValue clearValue; 69 | 70 | uint32 numMips; 71 | uint32 numSamples; 72 | 73 | TextureCreateFlags flags; 74 | PixelFormat format; 75 | }; 76 | -------------------------------------------------------------------------------- /Source/Runtime/RHI/src/RHI.cpp: -------------------------------------------------------------------------------- 1 | #include "RHI.h" 2 | #include "RHIDef.h" 3 | -------------------------------------------------------------------------------- /Source/Runtime/Render/include/RenderManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Assertion.h" 4 | #include "Manager.h" 5 | 6 | class RENDER_API RenderManager final : public Manager 7 | { 8 | public: 9 | [[nodiscard]] bool Init() noexcept override; 10 | [[nodiscard]] bool Update(float deltaTime) noexcept override; 11 | void Release() noexcept override; 12 | 13 | std::shared_ptr GetRHI() noexcept { return rhi; } 14 | std::shared_ptr GetRHI() const noexcept { return rhi; } 15 | 16 | void SetRHI(std::shared_ptr inRhi) noexcept 17 | { 18 | if (Ensure(!rhi, "Set RHI must call once.")) 19 | rhi = std::move(inRhi); 20 | } 21 | 22 | private: 23 | std::shared_ptr rhi; 24 | }; 25 | -------------------------------------------------------------------------------- /Source/Runtime/Render/src/RenderManager.cpp: -------------------------------------------------------------------------------- 1 | #include "RenderManager.h" 2 | 3 | bool RenderManager::Init() noexcept 4 | { 5 | if (Ensure(rhi, "RHI must set before initialize")) 6 | return false; 7 | 8 | return true; 9 | } 10 | 11 | bool RenderManager::Update(float deltaTime) noexcept 12 | { 13 | return true; 14 | } 15 | 16 | void RenderManager::Release() noexcept 17 | { 18 | 19 | } -------------------------------------------------------------------------------- /Source/Runtime/Thread/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Thread 2 | 3 | register_library () 4 | 5 | target_link_libraries (Thread PUBLIC Framework) -------------------------------------------------------------------------------- /Source/Runtime/Thread/include/ThreadManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | #include "Manager.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Delegate.h" 11 | 12 | class THREAD_API ThreadManager final : public Manager 13 | { 14 | public: 15 | [[nodiscard]] bool Init() noexcept override; 16 | void Release() noexcept override; 17 | 18 | template 19 | decltype(auto) AddTask(const Delegate& task, Args&&... args) noexcept; 20 | 21 | template 22 | decltype(auto) AddTask(Delegate&& task, Args&&... args) noexcept; 23 | 24 | [[nodiscard]] bool IsMainThread() const noexcept { return mainThreadId == std::this_thread::get_id(); } 25 | 26 | private: 27 | void ThreadWork() noexcept; 28 | 29 | template 30 | decltype(auto) AddTaskImpl(std::shared_ptr> task); 31 | 32 | private: 33 | std::queue> tasks; 34 | std::vector threads; 35 | std::thread::id mainThreadId; 36 | std::condition_variable cv; 37 | std::mutex taskMutex; 38 | bool isEnd = false; 39 | }; 40 | 41 | template 42 | decltype(auto) ThreadManager::AddTask(const Delegate& task, Args&&... args) noexcept 43 | { 44 | auto package = std::make_shared>(std::bind(task, std::forward(args)...)); 46 | 47 | return AddTaskImpl(std::move(package)); 48 | } 49 | 50 | template 51 | decltype(auto) ThreadManager::AddTask(Delegate&& task, Args&&... args) noexcept 52 | { 53 | auto package = std::make_shared>(std::bind(std::move(task), std::forward(args)...)); 55 | 56 | return AddTaskImpl(std::move(package)); 57 | } 58 | 59 | template 60 | decltype(auto) ThreadManager::AddTaskImpl(std::shared_ptr> task) 61 | { 62 | taskMutex.lock(); 63 | tasks.push([task] { (*task)(); }); 64 | taskMutex.unlock(); 65 | 66 | cv.notify_one(); 67 | return task->get_future(); 68 | } 69 | -------------------------------------------------------------------------------- /Source/Runtime/Thread/src/ThreadManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadManager.h" 2 | #include "Assertion.h" 3 | 4 | bool ThreadManager::Init() noexcept 5 | { 6 | uint32 threadNum = std::thread::hardware_concurrency(); 7 | if (!Ensure(threadNum > 0)) 8 | return false; 9 | 10 | mainThreadId = std::this_thread::get_id(); 11 | threads.reserve(threadNum); 12 | 13 | while (threadNum--) 14 | threads.emplace_back([this]() { ThreadWork(); }); 15 | 16 | return true; 17 | } 18 | 19 | void ThreadManager::Release() noexcept 20 | { 21 | isEnd = true; 22 | cv.notify_all(); 23 | 24 | for (auto& t : threads) 25 | t.join(); 26 | } 27 | 28 | void ThreadManager::ThreadWork() noexcept 29 | { 30 | while (true) 31 | { 32 | std::unique_lock lock{ taskMutex }; 33 | cv.wait(lock, [this]() { return !tasks.empty() || isEnd; }); 34 | if (isEnd && tasks.empty()) return; 35 | 36 | auto task = std::move(tasks.front()); 37 | tasks.pop(); 38 | lock.unlock(); 39 | task(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Runtime/Window/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Source/Runtime/Window 2 | 3 | register_library () 4 | 5 | target_link_libraries (Window PUBLIC Framework) -------------------------------------------------------------------------------- /Source/Runtime/Window/include/WindowManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Manager.h" 4 | #include "BSMath.h" 5 | #include "Name.h" 6 | 7 | class WINDOW_API WindowManager final : public Manager 8 | { 9 | public: 10 | WindowManager(Name inGameName) : gameName(std::move(inGameName)) {} 11 | 12 | [[nodiscard]] bool Init() noexcept override; 13 | [[nodiscard]] bool Update(float deltaTime) noexcept override; 14 | void Release() noexcept override; 15 | 16 | const struct Handle& GetHandle() const noexcept { return *handle; } 17 | const IntVector2& GetSize() const noexcept { return size; } 18 | 19 | private: 20 | Handle* handle; 21 | 22 | Name gameName; 23 | IntVector2 size; 24 | }; 25 | -------------------------------------------------------------------------------- /Source/Runtime/Window/src/WindowManager.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WINDOWS 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define NOMINMAX 5 | 6 | #include "WindowManager.h" 7 | #include 8 | 9 | namespace 10 | { 11 | constexpr char* ClassName = "BSEngine"; 12 | 13 | LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 14 | { 15 | switch (msg) 16 | { 17 | case WM_DESTROY: 18 | PostQuitMessage(0); 19 | break; 20 | } 21 | 22 | return DefWindowProc(hWnd, msg, wParam, lParam); 23 | } 24 | } 25 | 26 | struct Handle final 27 | { 28 | HINSTANCE hInstance = nullptr; 29 | HWND hWnd = nullptr; 30 | }; 31 | 32 | bool WindowManager::Init() noexcept 33 | { 34 | handle = new Handle; 35 | handle->hInstance = GetModuleHandle(nullptr); 36 | 37 | WNDCLASSEX wc; 38 | DEVMODE dmScreenSettings; 39 | memset(&wc, 0, sizeof(wc)); 40 | 41 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; 42 | wc.lpfnWndProc = &MsgProc; 43 | wc.cbClsExtra = 0; 44 | wc.cbWndExtra = 0; 45 | wc.hInstance = handle->hInstance; 46 | wc.hIcon = wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION); 47 | wc.hCursor = nullptr; 48 | wc.hbrBackground = nullptr; 49 | wc.lpszMenuName = nullptr; 50 | wc.lpszClassName = ClassName; 51 | wc.cbSize = sizeof(WNDCLASSEX); 52 | 53 | if (!RegisterClassEx(&wc)) 54 | return false; 55 | 56 | size.x = GetSystemMetrics(SM_CXSCREEN); 57 | size.y = GetSystemMetrics(SM_CYSCREEN); 58 | 59 | memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); 60 | dmScreenSettings.dmSize = sizeof(dmScreenSettings); 61 | dmScreenSettings.dmPelsWidth = static_cast(size.x); 62 | dmScreenSettings.dmPelsHeight = static_cast(size.y); 63 | dmScreenSettings.dmBitsPerPel = 32; 64 | dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 65 | ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); 66 | 67 | /// @todo: Support window mode (not full screen) 68 | 69 | const std::string nameStr = CastCharSet(StringView{ gameName.ToString().c_str() }); 70 | HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, ClassName, nameStr.c_str(), 71 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, 72 | 0, 0, size.x, size.y, nullptr, nullptr, handle->hInstance, nullptr); 73 | 74 | if (!hWnd) return false; 75 | 76 | ShowWindow(hWnd, SW_SHOWDEFAULT); 77 | SetForegroundWindow(hWnd); 78 | SetFocus(hWnd); 79 | 80 | UpdateWindow(hWnd); 81 | return true; 82 | } 83 | 84 | bool WindowManager::Update(float deltaTime) noexcept 85 | { 86 | MSG msg; 87 | if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) 88 | { 89 | TranslateMessage(&msg); 90 | DispatchMessage(&msg); 91 | } 92 | 93 | return msg.message != WM_QUIT; 94 | } 95 | 96 | void WindowManager::Release() noexcept 97 | { 98 | ShowCursor(true); 99 | 100 | ChangeDisplaySettings(nullptr, 0); 101 | DestroyWindow(handle->hWnd); 102 | UnregisterClass(ClassName, handle->hInstance); 103 | delete handle; 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /Tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | cmake_minimum_required (VERSION 3.12) 4 | 5 | project (BSEngine-Tests 6 | VERSION 0.1.0 7 | LANGUAGES CXX 8 | ) 9 | 10 | if (GTest_FOUND) 11 | file(GLOB_RECURSE TEST_FILES "*.cpp") 12 | add_executable(BSEngine-Tests ${TEST_FILES}) 13 | target_link_libraries(BSEngine-Tests PRIVATE Core GTest::GTest) 14 | 15 | enable_testing() 16 | add_test(NAME BSEngine-Test COMMAND BSEngine-Tests) 17 | endif () -------------------------------------------------------------------------------- /Tests/CoreTest.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Core.h" 3 | 4 | TEST(CoreTest, AssertTest) 5 | { 6 | const int32 lhs = 1, rhs = 1; 7 | 8 | Assert(lhs == rhs); 9 | AssertMsg(lhs == rhs, u"{} and {} are different.", lhs, rhs); 10 | 11 | EXPECT_TRUE(Ensure(lhs == rhs)); 12 | EXPECT_TRUE(EnsureMsg(lhs == rhs, u"{} and {} are different.", lhs, rhs)); 13 | } 14 | 15 | bool TestA(int32 lhs, int32 rhs) 16 | { 17 | return true; 18 | } 19 | 20 | struct Foo final 21 | { 22 | bool Boo(int32 lhs, int32 rhs) 23 | { 24 | return true; 25 | } 26 | }; 27 | 28 | TEST(CoreTest, DelegateTest) 29 | { 30 | auto lambda = [](int32 lhs, int32 rhs) 31 | { 32 | return lhs == rhs; 33 | }; 34 | 35 | Foo foo; 36 | Delegate delegate; 37 | 38 | delegate = &TestA; 39 | EXPECT_TRUE(delegate(1, 1)); 40 | 41 | delegate = { &foo, &Foo::Boo }; 42 | EXPECT_TRUE(delegate(1, 1)); 43 | 44 | delegate = lambda; 45 | EXPECT_TRUE(delegate(1, 1)); 46 | } 47 | 48 | static bool ReturnTrue(int32 lhs, int32 rhs) 49 | { 50 | return true; 51 | } 52 | 53 | TEST(CoreTest, EventTest) 54 | { 55 | Foo foo; 56 | Event event; 57 | 58 | event += &TestA; 59 | event += { &foo, &Foo::Boo }; 60 | event += [](int32 lhs, int32 rhs) 61 | { 62 | return lhs == rhs; 63 | }; 64 | 65 | event(1, 1); 66 | 67 | event -= &TestA; 68 | 69 | EXPECT_TRUE(event([](bool result, bool newResult) 70 | { 71 | return result && newResult; 72 | }, 1, 1)); 73 | } 74 | 75 | TEST(CoreTest, NameTest) 76 | { 77 | Name lhs{ STR("Hello") }; 78 | Name rhs{ STR("hello") }; 79 | 80 | EXPECT_EQ(lhs, rhs); 81 | EXPECT_EQ(lhs.ToString(), STR("hello")); 82 | EXPECT_EQ(lhs.GetLength(), 5); 83 | } 84 | -------------------------------------------------------------------------------- /Tests/TestMain.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | int main(int argc, char* argv[]) 4 | { 5 | testing::InitGoogleTest(&argc, argv); 6 | return RUN_ALL_TESTS(); 7 | } 8 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "registries": [ 3 | { 4 | "kind": "git", 5 | "repository": "https://github.com/blAs1N/vcpkg-registry", 6 | "packages": [ "bsbase", "bsmath" ] 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bsengine", 3 | "version-string": "0.1.0", 4 | "description": "Game Engine made by blAs1N", 5 | "homepage": "https://github.com/blAs1N/BS_Engine", 6 | "license": "MIT", 7 | "dependencies": [ 8 | "bsbase", 9 | "bsmath", 10 | "fmt", 11 | "gtest", 12 | "nlohmann-json", 13 | "spdlog", 14 | "utfcpp" 15 | ] 16 | } --------------------------------------------------------------------------------