├── .github └── workflows │ ├── build_linux.yml │ └── build_windows.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md └── src ├── AnimationComponent.h ├── AnimationEvent.h ├── AnimationSystem.h ├── CardComponent.h ├── Component.h ├── Components.h ├── DefferEvent.h ├── DefferSystem.h ├── DrawSystem.h ├── ECSManager.h ├── ECSlib.h ├── Entity.h ├── Event.cpp ├── Event.h ├── EventManager.h ├── Events.h ├── External.cpp ├── External.h ├── GameArenaSystem.h ├── GridAddRemoveEvent.h ├── GridContainerChildComponent.h ├── GridContainerComponent.h ├── GridContainerSystem.h ├── Helpers.h ├── HitBoxComponent.h ├── HitBoxEvent.h ├── HitBoxSystem.h ├── KeyboardEvent.h ├── KeyboardInputComponent.h ├── KeyboardInputSystem.h ├── MouseEvent.h ├── MouseInputComponent.h ├── MouseInputSystem.h ├── NetworkEvent.h ├── NetworkSystem.cpp ├── NetworkSystem.h ├── PhyFunctions.cpp ├── PhyFunctions.h ├── PhyFunctions2.cpp ├── PhyFunctions2.h ├── PhysicsComponent.cpp ├── PhysicsComponent.h ├── PhysicsSystem.cpp ├── PhysicsSystem.h ├── Pool.h ├── SpriteComponent.h ├── System.h ├── SystemControlEvent.h ├── Systems.h ├── TextureManager.h ├── TransformComponent.h ├── Utils.h ├── json.hpp ├── nvidia.h └── win_physac.h /.github/workflows/build_linux.yml: -------------------------------------------------------------------------------- 1 | name: BuildLinux 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | with: 17 | submodules: recursive 18 | 19 | - name: Install dependency 20 | run: | 21 | sudo apt-get -y install xorg-dev libglu1-mesa-dev 22 | 23 | - name: Run cmake 24 | run: | 25 | mkdir build 26 | cd build 27 | cmake .. -DCMAKE_C_COMPILER=/usr/bin/gcc-9 -DCMAKE_CXX_COMPILER=/usr/bin/g++-9 28 | 29 | - name: Build ECSlib 30 | run: | 31 | cd build 32 | cmake --build . --target ECSlib -- -j 4 33 | -------------------------------------------------------------------------------- /.github/workflows/build_windows.yml: -------------------------------------------------------------------------------- 1 | name: BuildWindows 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | with: 17 | submodules: recursive 18 | 19 | - name: setup-msbuild 20 | uses: microsoft/setup-msbuild@v1.0.1 21 | 22 | - name: Run cmake 23 | run: | 24 | mkdir build 25 | cd build 26 | cmake .. 27 | 28 | - name: Build ECSlib 29 | run: | 30 | cd build 31 | msbuild ECSlib.vcxproj 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Coverlet is a free, cross platform Code Coverage Tool 145 | coverage*[.json, .xml, .info] 146 | 147 | # Visual Studio code coverage results 148 | *.coverage 149 | *.coveragexml 150 | 151 | # NCrunch 152 | _NCrunch_* 153 | .*crunch*.local.xml 154 | nCrunchTemp_* 155 | 156 | # MightyMoose 157 | *.mm.* 158 | AutoTest.Net/ 159 | 160 | # Web workbench (sass) 161 | .sass-cache/ 162 | 163 | # Installshield output folder 164 | [Ee]xpress/ 165 | 166 | # DocProject is a documentation generator add-in 167 | DocProject/buildhelp/ 168 | DocProject/Help/*.HxT 169 | DocProject/Help/*.HxC 170 | DocProject/Help/*.hhc 171 | DocProject/Help/*.hhk 172 | DocProject/Help/*.hhp 173 | DocProject/Help/Html2 174 | DocProject/Help/html 175 | 176 | # Click-Once directory 177 | publish/ 178 | 179 | # Publish Web Output 180 | *.[Pp]ublish.xml 181 | *.azurePubxml 182 | # Note: Comment the next line if you want to checkin your web deploy settings, 183 | # but database connection strings (with potential passwords) will be unencrypted 184 | *.pubxml 185 | *.publishproj 186 | 187 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 188 | # checkin your Azure Web App publish settings, but sensitive information contained 189 | # in these scripts will be unencrypted 190 | PublishScripts/ 191 | 192 | # NuGet Packages 193 | *.nupkg 194 | # NuGet Symbol Packages 195 | *.snupkg 196 | # The packages folder can be ignored because of Package Restore 197 | **/[Pp]ackages/* 198 | # except build/, which is used as an MSBuild target. 199 | !**/[Pp]ackages/build/ 200 | # Uncomment if necessary however generally it will be regenerated when needed 201 | #!**/[Pp]ackages/repositories.config 202 | # NuGet v3's project.json files produces more ignorable files 203 | *.nuget.props 204 | *.nuget.targets 205 | 206 | # Microsoft Azure Build Output 207 | csx/ 208 | *.build.csdef 209 | 210 | # Microsoft Azure Emulator 211 | ecf/ 212 | rcf/ 213 | 214 | # Windows Store app package directories and files 215 | AppPackages/ 216 | BundleArtifacts/ 217 | Package.StoreAssociation.xml 218 | _pkginfo.txt 219 | *.appx 220 | *.appxbundle 221 | *.appxupload 222 | 223 | # Visual Studio cache files 224 | # files ending in .cache can be ignored 225 | *.[Cc]ache 226 | # but keep track of directories ending in .cache 227 | !?*.[Cc]ache/ 228 | 229 | # Others 230 | ClientBin/ 231 | ~$* 232 | *~ 233 | *.dbmdl 234 | *.dbproj.schemaview 235 | *.jfm 236 | *.pfx 237 | *.publishsettings 238 | orleans.codegen.cs 239 | 240 | # Including strong name files can present a security risk 241 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 242 | #*.snk 243 | 244 | # Since there are multiple workflows, uncomment next line to ignore bower_components 245 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 246 | #bower_components/ 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | ServiceFabricBackup/ 259 | *.rptproj.bak 260 | 261 | # SQL Server files 262 | *.mdf 263 | *.ldf 264 | *.ndf 265 | 266 | # Business Intelligence projects 267 | *.rdl.data 268 | *.bim.layout 269 | *.bim_*.settings 270 | *.rptproj.rsuser 271 | *- [Bb]ackup.rdl 272 | *- [Bb]ackup ([0-9]).rdl 273 | *- [Bb]ackup ([0-9][0-9]).rdl 274 | 275 | # Microsoft Fakes 276 | FakesAssemblies/ 277 | 278 | # GhostDoc plugin setting file 279 | *.GhostDoc.xml 280 | 281 | # Node.js Tools for Visual Studio 282 | .ntvs_analysis.dat 283 | node_modules/ 284 | 285 | # Visual Studio 6 build log 286 | *.plg 287 | 288 | # Visual Studio 6 workspace options file 289 | *.opt 290 | 291 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 292 | *.vbw 293 | 294 | # Visual Studio LightSwitch build output 295 | **/*.HTMLClient/GeneratedArtifacts 296 | **/*.DesktopClient/GeneratedArtifacts 297 | **/*.DesktopClient/ModelManifest.xml 298 | **/*.Server/GeneratedArtifacts 299 | **/*.Server/ModelManifest.xml 300 | _Pvt_Extensions 301 | 302 | # Paket dependency manager 303 | .paket/paket.exe 304 | paket-files/ 305 | 306 | # FAKE - F# Make 307 | .fake/ 308 | 309 | # CodeRush personal settings 310 | .cr/personal 311 | 312 | # Python Tools for Visual Studio (PTVS) 313 | __pycache__/ 314 | *.pyc 315 | 316 | # Cake - Uncomment if you are using it 317 | # tools/** 318 | # !tools/packages.config 319 | 320 | # Tabs Studio 321 | *.tss 322 | 323 | # Telerik's JustMock configuration file 324 | *.jmconfig 325 | 326 | # BizTalk build output 327 | *.btp.cs 328 | *.btm.cs 329 | *.odx.cs 330 | *.xsd.cs 331 | 332 | # OpenCover UI analysis results 333 | OpenCover/ 334 | 335 | # Azure Stream Analytics local run output 336 | ASALocalRun/ 337 | 338 | # MSBuild Binary and Structured Log 339 | *.binlog 340 | 341 | # NVidia Nsight GPU debugger configuration file 342 | *.nvuser 343 | 344 | # MFractors (Xamarin productivity tool) working folder 345 | .mfractor/ 346 | 347 | # Local History for Visual Studio 348 | .localhistory/ 349 | 350 | # BeatPulse healthcheck temp database 351 | healthchecksdb 352 | 353 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 354 | MigrationBackup/ 355 | 356 | # Ionide (cross platform F# VS Code tools) working folder 357 | .ionide/ 358 | 359 | # Fody - auto-generated XML schema 360 | FodyWeavers.xsd 361 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "raylib"] 2 | path = raylib 3 | url = https://github.com/raysan5/raylib.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) 2 | project(ECSlib) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | if(MSVC) 7 | set(BUILD_EXAMPLES CACHE BOOL OFF) 8 | set(BUILD_GAMES CACHE BOOL OFF) 9 | else() 10 | set(BUILD_EXAMPLES OFF) 11 | set(BUILD_GAMES OFF) 12 | endif() 13 | add_compile_options(-DSHARED=ON -DSTATIC=ON) 14 | 15 | add_definitions(-DCIOPILLIS_ROOT="${CMAKE_CURRENT_SOURCE_DIR}") 16 | add_subdirectory(raylib) 17 | 18 | ##################### Variables ############################ 19 | # Change if you want modify path or other values # 20 | ############################################################ 21 | 22 | # Project 23 | get_filename_component(PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) 24 | set(DEPENDENCIES_DIR ${PROJECT_DIR}/dependencies) 25 | set(PROJECT_NAME ECSlib) 26 | 27 | 28 | # Outputs 29 | set(OUTPUT_DEBUG Debug) 30 | set(OUTPUT_RELEASE Release) 31 | 32 | ################# CMake Project ############################ 33 | # The main options of project # 34 | ############################################################ 35 | 36 | project(${PROJECT_NAME} CXX) 37 | 38 | # Define Debug by default. 39 | if(NOT CMAKE_BUILD_TYPE) 40 | set(CMAKE_BUILD_TYPE "Debug") 41 | message(STATUS "Build type not specified: Use Debug by default.") 42 | endif(NOT CMAKE_BUILD_TYPE) 43 | 44 | ############## Artefacts Output ############################ 45 | # Defines outputs , depending BUILD TYPE # 46 | ############################################################ 47 | 48 | if(MSVC) 49 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_DIR}") 50 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_DIR}") 51 | set(CMAKE_EXECUTABLE_OUTPUT_DIRECTORY "${PROJECT_DIR}") 52 | else() 53 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 54 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_DIR}/${OUTPUT_DEBUG}") 55 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_DIR}/${OUTPUT_DEBUG}") 56 | set(CMAKE_EXECUTABLE_OUTPUT_DIRECTORY "${PROJECT_DIR}/${OUTPUT_DEBUG}") 57 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 58 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_DIR}/${OUTPUT_RELEASE}") 59 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_DIR}/${OUTPUT_RELEASE}") 60 | set(CMAKE_EXECUTABLE_OUTPUT_DIRECTORY "${PROJECT_DIR}/${OUTPUT_RELEASE}") 61 | endif() 62 | endif() 63 | 64 | # Messages 65 | message("${PROJECT_NAME}: MAIN PROJECT: ${CMAKE_PROJECT_NAME}") 66 | message("${PROJECT_NAME}: CURR PROJECT: ${CMAKE_CURRENT_SOURCE_DIR}") 67 | message("${PROJECT_NAME}: CURR BIN DIR: ${CMAKE_CURRENT_BINARY_DIR}") 68 | 69 | ############### Files & Targets ############################ 70 | # Files of project and target to build # 71 | ############################################################ 72 | 73 | # Source Files 74 | set(SRC_FILES 75 | ./src/External.cpp 76 | ./src/NetworkSystem.cpp 77 | ./src/PhyFunctions.cpp 78 | ./src/PhyFunctions2.cpp 79 | ./src/PhysicsComponent.cpp 80 | ./src/PhysicsSystem.cpp 81 | ./src/Event.cpp 82 | ) 83 | source_group("Sources" FILES ${SRC_FILES}) 84 | 85 | # Header Files 86 | set(HEADERS_FILES 87 | src/AnimationComponent.h 88 | src/AnimationEvent.h 89 | src/AnimationSystem.h 90 | src/Component.h 91 | src/Components.h 92 | src/DefferEvent.h 93 | src/DefferSystem.h 94 | src/DrawSystem.h 95 | src/ECSManager.h 96 | src/ECSlib.h 97 | src/Entity.h 98 | src/Event.h 99 | src/EventManager.h 100 | src/Events.h 101 | src/External.h 102 | src/GridAddRemoveEvent.h 103 | src/GridContainerChildComponent.h 104 | src/GridContainerComponent.h 105 | src/GridContainerSystem.h 106 | src/Helpers.h 107 | src/HitBoxComponent.h 108 | src/HitBoxEvent.h 109 | src/HitBoxSystem.h 110 | src/KeyboardEvent.h 111 | src/KeyboardInputComponent.h 112 | src/KeyboardInputSystem.h 113 | src/MouseEvent.h 114 | src/MouseInputSystem.h 115 | src/NetworkEvent.h 116 | src/NetworkSystem.h 117 | src/nvidia.h 118 | src/PhyFunctions.h 119 | src/PhyFunctions2.h 120 | src/PhysicsComponent.h 121 | src/PhysicsSystem.h 122 | src/Pool.h 123 | src/SpriteComponent.h 124 | src/System.h 125 | src/SystemControlEvent.h 126 | src/Systems.h 127 | src/TextureManager.h 128 | src/TransformComponent.h 129 | ) 130 | source_group("Headers" FILES ${HEADERS_FILES}) 131 | 132 | # Add library to build. 133 | add_library(${PROJECT_NAME} STATIC 134 | ${SRC_FILES} ${HEADERS_FILES} 135 | ) 136 | 137 | ######################### Flags ############################ 138 | # Defines Flags for Windows and Linux # 139 | ############################################################ 140 | 141 | if(NOT MSVC) 142 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 143 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 144 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++17") 145 | endif() 146 | endif(NOT MSVC) 147 | 148 | # Preprocessor definitions 149 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 150 | target_compile_definitions(${PROJECT_NAME} PRIVATE 151 | -DGRAPHICS_API_OPENGL_33 152 | -DPLATFORM_DESKTOP 153 | -D_CRT_SECURE_NO_WARNINGS 154 | -D_CRT_NONSTDC_NO_DEPRECATE 155 | ) 156 | if(MSVC) 157 | target_compile_options(${PROJECT_NAME} PRIVATE /W3 /MD /Od /EHsc) 158 | endif() 159 | endif() 160 | 161 | if(CMAKE_BUILD_TYPE STREQUAL "Release") 162 | target_compile_definitions(${PROJECT_NAME} PRIVATE 163 | -DGRAPHICS_API_OPENGL_33 164 | -DPLATFORM_DESKTOP 165 | -D_CRT_SECURE_NO_WARNINGS 166 | ) 167 | if(MSVC) 168 | target_compile_options(${PROJECT_NAME} PRIVATE /W3 /GL /Oi /Gy /EHsc) 169 | endif() 170 | endif() 171 | 172 | ########### Link & Dependencies ############################ 173 | # Add project dependencies and Link to project # 174 | ############################################################ 175 | 176 | add_dependencies(${PROJECT_NAME} raylib) 177 | if(MSVC) 178 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 179 | target_link_libraries(${PROJECT_NAME} ${CMAKE_BINARY_DIR}/raylib/src/Debug/raylib_static.lib winmm.lib ws2_32.lib) 180 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 181 | target_link_libraries(${PROJECT_NAME} ${CMAKE_BINARY_DIR}/raylib/src/Release/raylib_static.lib winmm.lib ws2_32.lib) 182 | endif() 183 | else() 184 | target_link_libraries(${PROJECT_NAME} raylib) 185 | endif() 186 | target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/raylib/src/) 187 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ștefan Petrovici 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 | # ECSlib 2 | 3 | Build status: ![WinBuild](https://github.com/firststef/ECSlib/workflows/BuildWindows/badge.svg) ![LinuxBuild](https://github.com/firststef/ECSlib/workflows/BuildLinux/badge.svg) 4 | 5 | A library with ECS classes in c++ using raylib 6 | 7 | ECSlib was created as a collection of ECS classes for developing the Ciopillis game, found here: https://github.com/firststef/Ciopillis. It was separated as a library for use in other projects. 8 | 9 | ### Install 10 | 11 | ECSlib is configured with cmake, you can either build it on Linux directly or generate a .sln file for Windows. 12 | 13 | ### ECSlib Features: 14 | 15 | #### Entities 16 | 17 | #### Systems & Components 18 | 19 | * DrawSystem -- SpriteComponent -- TransformComponent 20 | * MouseInputSystem -- MouseInputComponent 21 | * KeyboardInputSystem -- KeyboardInputComponent 22 | * GridContainerSystem -- GridContainerComponent -- GridContainerChildComponent 23 | * PhysicsSystem -- PhysicsComponent 24 | * AnimationSystem -- AnimationComponent 25 | * DefferSystem 26 | * HitBoxSystem -- HitBoxComponent 27 | * NetworkSystem 28 | 29 | #### Events 30 | 31 | * MouseEvent -> input 32 | * KeyboardEvent -> input 33 | * GridAddRemoveEvent -> add/remove from a grid container 34 | * SystemControlEvent -> enable/disable systems 35 | * AnimationEvent -> on change animation, notify 36 | * DefferEvent -> do something later 37 | * HitBoxEvent -> on collision, notify 38 | * NetworkEvent -> on packet, notify 39 | 40 | ### ECS 41 | 42 | The game was built with the use of an [Entity Component System](https://en.wikipedia.org/wiki/Entity_component_system) framework, followingly referenced as ECSlib. 43 | 44 | To understand the ECS design better we compare it to what was considered the natural way of implementing a game system: **object-oriented inheritance hierarchies**. Everything in the game was an instance of the class **Object** or its derived classes. Each object would interact most commonly with either it's base or derived classes instantiations, and in some cases with other classes. **The game logic** was based on the **coordination between the classes**, most often in the form of *hierarchical trees*, with parents controlling the actions of the children and the children transmitting responses. 45 | 46 | But at a certain point, several problems arose. One of the most important was the process of maintaining the code: Adding new functionalities inevitably involved the use of increasingly complex issue-solving techniques, which even though was educational, proved tedious and hindered the development of the game. 47 | 48 | With ECS, this aspect is solved: 49 | 50 | > **Entity–component–system** (**ECS**) is an architectural pattern that is mostly used in the game development industry. ECS follows the composition over the inheritance principle that allows greater flexibility in defining game object types. The behavior of each entity can change depending on what components it holds at a certain moment. 51 | 52 | **Entities** (the correspondent of Objects) now held attributes called **Components** (essentially data units), that are evaluated independently in such a matter that these structures are seemingly dependency-free. **The new game logic** is driven by **Systems**, each taking a specific type of Component and editing the data in these units. Adding new functionalities now consists of adding a new System with an independent logic and a matching component (though this is not mandatory). Also see the [Appendix ](#Appendix ). 53 | 54 | Because these systems are independent, the adding, removing, runtime disabling or enabling would not crash the game or impact in any way other modular functionalities. Also, the behavior of the main game unit, the Entity, can easily change during the game based on the contained components. 55 | 56 | ### Raylib 57 | 58 | Raylib is an open-source, cross-platform library for video game written in c99 that is used by the ECSlib for rendering and physics computations. 59 | 60 | ### The framework architecture 61 | 62 | A game instance is usually managed by an object of the class **ECSManager**. ECSManager ties together a **SystemManager**, an **Event Manager** and a **Resource Manager**. Each system can be initilalized and runs idependently, but can sometimes communicate with other systems by **Events**. 63 | 64 | Just to mention a few important systems, ECSlib implements the **DrawSystem** (graphical manager), **InputSystem**, **AnimationSystem**, **PhysicsSystem** etc. Each system can run on a separate thread, however the current implementation works with an iterative approach to the concept. 65 | 66 | # Appendix 67 | 68 | How ECS Systems work 69 | ![Systems](https://drive.google.com/uc?export=download&id=1ONHaTytjKBFrBXvTDf1QOMA3MaFs5X4O) 70 | 71 | *Each System only changes data by small, modular logic rules without interfering too much with other system's logic*. 72 | -------------------------------------------------------------------------------- /src/AnimationComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "Component.h" 5 | 6 | struct AnimationUnit 7 | { 8 | //resources 9 | std::string name; 10 | 11 | Texture2D texture; 12 | Rectangle sourceRec; 13 | unsigned numOfFrames; 14 | unsigned timePerFrame; 15 | std::shared_ptr mirrorSprite; 16 | std::shared_ptr reverseOrder; 17 | unsigned repeats; 18 | 19 | //utils 20 | unsigned currentFrame; 21 | bool waitFrameFlag; 22 | unsigned currentRepeat; 23 | 24 | bool started; 25 | 26 | std::chrono::duration> animationSpeed; 27 | 28 | AnimationUnit( 29 | std::string name, 30 | Texture2D texture, 31 | Rectangle sourceRec, 32 | unsigned numOfFrames, 33 | const unsigned timePerFrame, 34 | std::shared_ptr mirrorSprite, 35 | std::shared_ptr reverseOrder, 36 | unsigned repeats) 37 | : 38 | name(std::move(name)), 39 | texture(texture), 40 | sourceRec(sourceRec), 41 | numOfFrames(numOfFrames), 42 | timePerFrame(timePerFrame), 43 | mirrorSprite(mirrorSprite), 44 | reverseOrder(reverseOrder), 45 | repeats(repeats), 46 | currentFrame(0), 47 | waitFrameFlag(false), 48 | currentRepeat(0), 49 | started(false), 50 | animationSpeed(std::chrono::duration>(timePerFrame)) 51 | { 52 | } 53 | }; 54 | 55 | struct AnimationGraph; 56 | 57 | struct AnimationNode 58 | { 59 | std::shared_ptr animationUnit; 60 | 61 | struct NextAnimationNodeWrapper 62 | { 63 | std::shared_ptr node; 64 | std::function cond; 65 | void* context; 66 | }; 67 | 68 | std::vector nextNodes; 69 | 70 | explicit AnimationNode(AnimationUnit anim) 71 | : animationUnit(std::make_shared(anim)) 72 | {} 73 | 74 | //DOCS: Next() is a function called at the end of each animation (node), 75 | //Returns true if the animation must pass to the next node 76 | std::shared_ptr Next(std::shared_ptr next, std::function cond, void* context) 77 | { 78 | nextNodes.push_back(NextAnimationNodeWrapper{ next, cond, context }); 79 | 80 | return next; 81 | } 82 | }; 83 | 84 | struct AnimationGraph 85 | { 86 | std::shared_ptr zero; 87 | std::shared_ptr currentNode; 88 | 89 | std::chrono::system_clock::time_point lastIterationTime; 90 | 91 | explicit AnimationGraph(std::shared_ptr zero) : zero(zero), currentNode(zero) {} 92 | }; 93 | 94 | struct AnimationComponent : IComponent 95 | { 96 | AnimationGraph graph; 97 | 98 | AnimationComponent(AnimationGraph graph) : graph(std::move(graph)) {} 99 | }; -------------------------------------------------------------------------------- /src/AnimationEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct AnimationEvent : IEvent 5 | { 6 | EntityPtr entity; 7 | AnimationNode& node; 8 | 9 | AnimationEvent( 10 | EntityPtr entity, 11 | AnimationNode& node 12 | ): 13 | entity(entity), 14 | node(node) 15 | { 16 | } 17 | }; -------------------------------------------------------------------------------- /src/AnimationSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AnimationEvent.h" 3 | #include "AnimationComponent.h" 4 | 5 | class AnimationSystem : public ISystem 6 | { 7 | public: 8 | void static StepAnimation(EntityPtr entity) 9 | { 10 | auto& anim = entity->Get(); 11 | auto& graph = anim.graph; 12 | auto& node = *graph.currentNode; 13 | auto& unit = *node.animationUnit; 14 | auto& sprite = entity->Get(); 15 | 16 | sprite.texture = unit.texture; 17 | 18 | unsigned frame = (*unit.reverseOrder ? (unit.numOfFrames - 1 - unit.currentFrame) : unit.currentFrame); 19 | unsigned nextFrame; 20 | if (unit.repeats == 0) 21 | nextFrame = (unit.currentFrame + 1) % unit.numOfFrames; 22 | else 23 | nextFrame = (unit.currentFrame + 1 == unit.numOfFrames) ? frame : unit.currentFrame + 1; 24 | 25 | if (*unit.mirrorSprite) 26 | sprite.sourceRec = { unit.sourceRec.x + frame * unit.sourceRec.width, 0, -unit.sourceRec.width, unit.sourceRec.height }; 27 | else 28 | sprite.sourceRec = { unit.sourceRec.x + frame * unit.sourceRec.width, 0, unit.sourceRec.width, unit.sourceRec.height }; 29 | 30 | if (unit.currentFrame + 1 >= unit.numOfFrames) 31 | { 32 | if (unit.waitFrameFlag) 33 | { 34 | unit.currentRepeat++; 35 | unit.waitFrameFlag = false; 36 | } 37 | else 38 | unit.waitFrameFlag = true; 39 | } 40 | 41 | unit.currentFrame = nextFrame; 42 | } 43 | 44 | bool static GoToNextNode(EntityPtr entity) 45 | { 46 | auto& anim = entity->Get(); 47 | auto& graph = anim.graph; 48 | auto& node = *graph.currentNode; 49 | auto& unit = *node.animationUnit; 50 | 51 | for (auto& next : node.nextNodes) { 52 | if ((unit.currentRepeat == unit.repeats == 0) || unit.currentRepeat >= unit.repeats) 53 | { 54 | if (next.cond(node, next.context)) 55 | { 56 | //reinit 57 | unit.started = false; 58 | unit.currentFrame = 0; 59 | unit.currentRepeat = 0; 60 | 61 | //next 62 | graph.currentNode = next.node; 63 | return true; 64 | } 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | AnimationSystem() : ISystem("AnimationSystem") {} 71 | 72 | void Initialize() override {} 73 | 74 | void Execute() override 75 | { 76 | auto entities = pool->GetEntities(1 << GetComponentTypeID() | 1 << GetComponentTypeID()); 77 | 78 | auto now = std::chrono::system_clock::now(); 79 | 80 | for (auto& entity : entities) 81 | { 82 | auto& anim = entity->Get(); 83 | auto& graph = anim.graph; 84 | auto& node = *graph.currentNode; 85 | auto& unit = *node.animationUnit; 86 | 87 | if (GoToNextNode(entity)) 88 | continue; 89 | 90 | if (!unit.started) 91 | { 92 | anim.graph.lastIterationTime = now - unit.animationSpeed - std::chrono::duration>(1); 93 | unit.started = true; 94 | } 95 | 96 | if (now - anim.graph.lastIterationTime > unit.animationSpeed) 97 | { 98 | StepAnimation(entity); 99 | 100 | anim.graph.lastIterationTime = now; 101 | 102 | eventManager->Notify(entity, node); 103 | } 104 | } 105 | } 106 | }; 107 | -------------------------------------------------------------------------------- /src/CardComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | #include "../GameServer/Card.h" 4 | 5 | struct CardComponent : IComponent 6 | { 7 | Card card; 8 | 9 | CardComponent(Card card) : card(card) {} 10 | }; 11 | -------------------------------------------------------------------------------- /src/Component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct IComponent; 7 | 8 | using ComponentID = std::size_t; 9 | constexpr std::size_t maxComponents = 32; 10 | 11 | inline ComponentID GetComponentID() 12 | { 13 | static ComponentID lastID = 0; 14 | 15 | return lastID++; 16 | } 17 | 18 | template 19 | ComponentID GetComponentTypeID() 20 | { 21 | static_assert(std::is_base_of::value, "T is not derived from component"); 22 | 23 | static ComponentID typeID = GetComponentID(); 24 | 25 | return typeID; 26 | } 27 | 28 | class Entity; 29 | 30 | struct IComponent 31 | { 32 | std::weak_ptr entity; 33 | 34 | virtual ~IComponent() {} 35 | }; 36 | 37 | using ComponentBitset = std::bitset; 38 | using ComponentArray = std::array; 39 | 40 | using ComponentPtr = std::shared_ptr; -------------------------------------------------------------------------------- /src/Components.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TransformComponent.h" 3 | #include "SpriteComponent.h" 4 | #include "MouseInputComponent.h" 5 | #include "GridContainerComponent.h" 6 | #include "GridContainerChildComponent.h" 7 | #include "PhysicsComponent.h" 8 | #include "AnimationComponent.h" 9 | #include "KeyboardInputComponent.h" 10 | #include "HitBoxComponent.h" -------------------------------------------------------------------------------- /src/DefferEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct DefferEvent : IEvent 5 | { 6 | unsigned delayTime; 7 | std::function func; 8 | void* context; 9 | 10 | DefferEvent(unsigned delayTime, std::function func, void* context) 11 | : delayTime(delayTime), func(std::move(func)), context(context) 12 | {} 13 | }; 14 | 15 | struct DefferTriggerEvent : IEvent 16 | { 17 | unsigned delayTime; 18 | std::vector buffer; 19 | 20 | DefferTriggerEvent(unsigned delayTime, std::vector buffer) 21 | : delayTime(delayTime), buffer(std::move(buffer)) 22 | {} 23 | }; 24 | 25 | struct TriggerEvent : IEvent 26 | { 27 | std::vector buffer; 28 | 29 | TriggerEvent(std::vector buffer) 30 | : buffer(std::move(buffer)) 31 | {} 32 | }; -------------------------------------------------------------------------------- /src/DefferSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "DefferEvent.h" 4 | 5 | struct VoidContextFuncCoverter 6 | { 7 | VoidContextFuncCoverter(std::function func, void* context) : func(std::move(func)), context(context) {} 8 | void operator()() { func(context); } 9 | std::function func; 10 | void* context; 11 | }; 12 | 13 | struct DelayHolder 14 | { 15 | std::chrono::system_clock::time_point endPoint; 16 | VoidContextFuncCoverter f; 17 | }; 18 | 19 | struct DelayTriggerHolder 20 | { 21 | std::chrono::system_clock::time_point endPoint; 22 | std::vector buffer; 23 | }; 24 | 25 | class DefferSystem : public ISystem 26 | { 27 | std::vector delayVector; 28 | std::vector delayTriggerVector; 29 | 30 | public: 31 | 32 | DefferSystem() : ISystem("DefferSystem") {} 33 | 34 | void Initialize() override {} 35 | 36 | void Execute() override 37 | { 38 | auto now = std::chrono::system_clock::now(); 39 | 40 | for (unsigned i = 0; i < delayVector.size();++i) 41 | { 42 | if (now >= delayVector[i].endPoint) 43 | { 44 | delayVector[i].f(); 45 | } 46 | } 47 | 48 | //Erasing is done after because f() could add events 49 | for (auto it = delayVector.begin(); it != delayVector.end();) 50 | { 51 | if (now >= it->endPoint) 52 | { 53 | it = delayVector.erase(it); 54 | } 55 | else 56 | { 57 | ++it; 58 | } 59 | } 60 | 61 | for (unsigned i = 0; i < delayTriggerVector.size(); ++i) 62 | { 63 | if (now >= delayTriggerVector[i].endPoint) 64 | { 65 | eventManager->Notify(delayTriggerVector[i].buffer); 66 | } 67 | } 68 | 69 | for (auto it = delayTriggerVector.begin(); it != delayTriggerVector.end();) 70 | { 71 | if (now >= it->endPoint) 72 | { 73 | it = delayTriggerVector.erase(it); 74 | } 75 | else 76 | { 77 | ++it; 78 | } 79 | } 80 | } 81 | 82 | void Receive(const DefferEvent& event) 83 | { 84 | delayVector.push_back(DelayHolder{ 85 | std::chrono::system_clock::now() + std::chrono::duration>(event.delayTime), 86 | VoidContextFuncCoverter{ 87 | event.func, 88 | event.context 89 | } 90 | } 91 | ); 92 | } 93 | 94 | void Receive(const DefferTriggerEvent& event) 95 | { 96 | delayTriggerVector.push_back(DelayTriggerHolder{ 97 | std::chrono::system_clock::now() + std::chrono::duration>(event.delayTime), 98 | std::move(event.buffer) 99 | } 100 | ); 101 | } 102 | }; -------------------------------------------------------------------------------- /src/DrawSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "raylib.h" 3 | #include "Components.h" 4 | #include "System.h" 5 | 6 | class DrawSystem : public ISystem 7 | { 8 | public: 9 | DrawSystem() : ISystem(std::string("DrawSystem")) {} 10 | 11 | void Initialize() override {} 12 | 13 | void Execute() override 14 | { 15 | BeginDrawing(); 16 | ClearBackground(RAYWHITE); 17 | 18 | //TODO: ar trebui sa existe o componenta draw component care sa contina o functie simpla care permite desenare custom - not recommended 19 | auto entities = pool->GetEntities(1 << GetComponentTypeID() | 1 << GetComponentTypeID()); 20 | std::sort(entities.begin(), entities.end(), [](EntityPtr a, EntityPtr b) 21 | { 22 | return a->Get().zIndex < b->Get().zIndex; 23 | }); 24 | 25 | for (auto& e : entities) 26 | { 27 | const auto transform = e->Get(); 28 | const auto sprite = e->Get(); 29 | 30 | if (sprite.texture.id == 0) 31 | DrawRectangleRec(transform.position, sprite.color); 32 | else { 33 | //TODO: Jittering is caused by unscaled resize 34 | DrawTexturePro(sprite.texture, 35 | sprite.sourceRec, 36 | transform.position, 37 | Vector2{ 0, 0 }, 38 | 0.0f, 39 | sprite.color 40 | ); 41 | } 42 | } 43 | 44 | #if defined(ARENA) 45 | 46 | int bodiesCount = GetPhysicsBodiesCount(); 47 | for (int i = 0; i < bodiesCount; i++) 48 | { 49 | PhysicsBody body = GetPhysicsBody(i); 50 | 51 | if (body != NULL) 52 | { 53 | int vertexCount = GetPhysicsShapeVerticesCount(i); 54 | for (int j = 0; j < vertexCount; j++) 55 | { 56 | // Get physics bodies shape vertices to draw lines 57 | Vector2 vertexA = GetPhysicsShapeVertex(body, j); 58 | 59 | int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape 60 | Vector2 vertexB = GetPhysicsShapeVertex(body, jj); 61 | 62 | DrawLineV(vertexA, vertexB, GREEN); // Draw a line between two vertex positions 63 | } 64 | } 65 | } 66 | 67 | auto hitBoxEntities = pool->GetEntities(1 << GetComponentTypeID()); 68 | for (auto& e : hitBoxEntities) 69 | { 70 | auto box = e->Get(); 71 | if (box.current_container) 72 | box.current_container->Draw(); 73 | } 74 | 75 | #endif 76 | EndDrawing(); 77 | } 78 | }; -------------------------------------------------------------------------------- /src/ECSManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TextureManager.h" 3 | #include "EventManager.h" 4 | 5 | class ECSManager 6 | { 7 | public: 8 | Pool pool; 9 | TextureManager textureManager; 10 | EventManager eventManager; 11 | 12 | SystemManager systemManager; 13 | 14 | ECSManager() 15 | :systemManager(&pool, &textureManager, &eventManager) 16 | { 17 | } 18 | 19 | void Initialize() 20 | { 21 | systemManager.Initialize(); 22 | } 23 | 24 | void Update() 25 | { 26 | systemManager.Execute(); 27 | } 28 | 29 | void Destroy() 30 | { 31 | systemManager.Destroy(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/ECSlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nvidia.h" 3 | 4 | #include 5 | 6 | #include "Helpers.h" 7 | #include "External.h" 8 | #include "ECSManager.h" 9 | #include "Components.h" 10 | #include "Systems.h" -------------------------------------------------------------------------------- /src/Entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using time_point = std::chrono::system_clock::time_point; 8 | 9 | class Entity : public std::enable_shared_from_this 10 | { 11 | bool active = true; 12 | std::vector components; 13 | 14 | ComponentArray componentArray = {}; 15 | ComponentBitset componentBitset; 16 | std::array componentTimeStamps; 17 | public: 18 | 19 | bool IsActive() const { return active; }; 20 | void RemoveAllComponents() 21 | { 22 | components.clear(); 23 | componentBitset.reset(); 24 | } 25 | void Destroy() 26 | { 27 | RemoveAllComponents(); 28 | active = false; 29 | } 30 | 31 | template 32 | bool Has() const 33 | { 34 | return componentBitset[GetComponentTypeID()]; 35 | } 36 | bool Has(const ComponentBitset& bitset) const 37 | { 38 | return ((bitset & componentBitset)==bitset); 39 | } 40 | 41 | template 42 | T& Add(TArgs&&... mArgs) 43 | { 44 | T* c(new T(std::forward(mArgs)...)); 45 | c->entity = shared_from_this(); 46 | ComponentPtr uPtr{ c }; 47 | components.push_back(uPtr); 48 | 49 | const auto comp_id = GetComponentTypeID(); 50 | 51 | componentArray[comp_id] = c; 52 | componentBitset[comp_id] = true; 53 | 54 | componentTimeStamps[comp_id] = std::chrono::system_clock::now(); 55 | 56 | return *c; 57 | } 58 | 59 | template 60 | T& Get() const 61 | { 62 | auto ptr = componentArray[GetComponentTypeID()]; 63 | return *static_cast(ptr); 64 | } 65 | 66 | template 67 | T* GetPtr() const 68 | { 69 | return static_cast(componentArray[GetComponentTypeID()]); 70 | } 71 | 72 | template 73 | T& Replace(TArgs&&... mArgs) 74 | { 75 | componentArray[GetComponentTypeID()] = { std::forward(mArgs)... }; 76 | return componentArray[GetComponentTypeID()]; 77 | } 78 | 79 | template 80 | void Remove() 81 | { 82 | int idx = 0; 83 | for (auto& ptr : components) 84 | { 85 | if (ptr.get() == componentArray[GetComponentTypeID()]) 86 | { 87 | components.erase(components.begin() + idx); 88 | break; 89 | } 90 | ++idx; 91 | } 92 | componentArray[GetComponentTypeID()] = nullptr; 93 | componentBitset[GetComponentTypeID()] = 0; 94 | } 95 | }; 96 | 97 | using EntityPtr = std::shared_ptr; -------------------------------------------------------------------------------- /src/Event.cpp: -------------------------------------------------------------------------------- 1 | #include "Event.h" 2 | 3 | EventID BaseEvent::totalEventTypes = 0; -------------------------------------------------------------------------------- /src/Event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct IEvent 7 | { 8 | }; 9 | 10 | using EventID = std::size_t; 11 | 12 | struct BaseEvent { 13 | 14 | static EventID totalEventTypes; 15 | 16 | static EventID GetEventID() 17 | { 18 | return totalEventTypes++; 19 | } 20 | }; 21 | 22 | template 23 | EventID GetEventTypeID() noexcept 24 | { 25 | static_assert(std::is_base_of::value, "T is not derived from event"); 26 | 27 | static EventID typeID = BaseEvent::GetEventID(); 28 | return typeID; 29 | } -------------------------------------------------------------------------------- /src/EventManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "System.h" 3 | #include 4 | #include 5 | 6 | struct ReceiverCallbacksHolder 7 | { 8 | std::vector> typeSubscribers; 9 | }; 10 | 11 | template 12 | struct TFuncToVoidFuncConverter 13 | { 14 | TFuncToVoidFuncConverter(std::function callback) : callback(std::move(callback)) {} 15 | void operator()(const void *event) { callback(*(static_cast(event))); } 16 | std::function callback; 17 | }; 18 | 19 | class EventManager 20 | { 21 | std::array holders{ nullptr }; 22 | 23 | public: 24 | 25 | template 26 | void Subscribe(const std::shared_ptr& system) 27 | { 28 | void (System::*Receive)(const Event &) = &System::Receive; 29 | if (Receive == nullptr) 30 | throw std::bad_cast(); 31 | 32 | auto converter = TFuncToVoidFuncConverter(std::bind(Receive, static_cast(system.get()), std::placeholders::_1)); 33 | 34 | if (holders[GetEventTypeID()] == nullptr) 35 | { 36 | holders[GetEventTypeID()] = new ReceiverCallbacksHolder; 37 | } 38 | 39 | holders[GetEventTypeID()]->typeSubscribers.push_back(converter); 40 | } 41 | 42 | template 43 | void Unsubscribe(const SystemPtr& system) 44 | { 45 | void (System::*receive)(const Event &) = &System::receive; 46 | auto func = std::bind(receive, system.get(), std::placeholders::_1); 47 | 48 | int i = 0; 49 | for (auto& f : holders[GetEventTypeID()]->typeSubscribers) 50 | { 51 | if (f == func) 52 | holders[GetEventTypeID()]->typeSubscribers.erase(holders[GetEventTypeID()]->typeSubscribers.begin() + i); 53 | ++i; 54 | } 55 | } 56 | 57 | template 58 | void Notify(TArgs&&... mArgs) 59 | { 60 | if (holders[GetEventTypeID()] == nullptr) 61 | return; 62 | 63 | auto it = holders[GetEventTypeID()]->typeSubscribers.begin(); 64 | auto end = holders[GetEventTypeID()]->typeSubscribers.end(); 65 | for (auto func = *it; it != end; ++it) 66 | { 67 | func = *it; 68 | T event = T(std::forward(mArgs)...); 69 | func(&event); 70 | } 71 | } 72 | }; -------------------------------------------------------------------------------- /src/Events.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MouseEvent.h" 3 | #include "GridAddRemoveEvent.h" 4 | #include "SystemControlEvent.h" 5 | #include "AnimationEvent.h" 6 | #include "DefferEvent.h" 7 | #include "KeyboardEvent.h" 8 | #include "HitBoxEvent.h" 9 | #include "NetworkEvent.h" -------------------------------------------------------------------------------- /src/External.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WIN32 2 | #include 3 | #elif __linux__ 4 | #include 5 | #include 6 | #include 7 | 8 | #endif 9 | void SleepFunc(unsigned long milliseconds) { 10 | #ifdef WIN32 11 | Sleep(milliseconds); 12 | #elif _POSIX_C_SOURCE >= 199309L 13 | timespec ts; 14 | ts.tv_sec = milliseconds / 1000; 15 | ts.tv_nsec = (milliseconds % 1000) * 1000000; 16 | nanosleep(&ts, NULL); 17 | #else 18 | usleep(milliseconds * 1000); 19 | #endif 20 | } -------------------------------------------------------------------------------- /src/External.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | void SleepFunc(unsigned long time); 3 | -------------------------------------------------------------------------------- /src/GameArenaSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "System.h" 3 | 4 | class -------------------------------------------------------------------------------- /src/GridAddRemoveEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Entity.h" 3 | 4 | struct GridAddRemoveEvent : IEvent 5 | { 6 | enum EventType 7 | { 8 | ADD, 9 | REMOVE 10 | } type = ADD; 11 | 12 | EntityPtr entity; 13 | EntityPtr parent; 14 | 15 | GridAddRemoveEvent(EventType t, EntityPtr e, EntityPtr p) : type(t), entity(e), parent(p) {} 16 | }; 17 | -------------------------------------------------------------------------------- /src/GridContainerChildComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | #include "Entity.h" 4 | 5 | struct GridContainerChildComponent : IComponent 6 | { 7 | EntityPtr parent; 8 | int indexInParent; 9 | 10 | GridContainerChildComponent(EntityPtr p, int index) : parent(p), indexInParent(index) {} 11 | }; 12 | -------------------------------------------------------------------------------- /src/GridContainerComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Component.h" 4 | #include "Entity.h" 5 | 6 | struct GridContainerComponent : IComponent 7 | { 8 | //Engine-related 9 | bool needsUpdate = true; 10 | 11 | //System-related 12 | enum ItemSetMode 13 | { 14 | FIXED_GET_FIRST_AVAILABLE, 15 | FIXED_SET_IN_PLACE, 16 | DYNAMIC_ERASE_SPACES, 17 | INFINITE_STACK 18 | } itemSetMode = FIXED_GET_FIRST_AVAILABLE; 19 | 20 | int numOfColumns = 1; 21 | int numOfLines = 1; 22 | 23 | int maxNumOfColumns = 1; 24 | int maxNumOfLines = 1; 25 | 26 | float marginLeft = 0; 27 | float marginUp = 0; 28 | float marginRight = 0; 29 | float marginDown = 0; 30 | 31 | float spaceBetween = 0; 32 | 33 | bool stretchEnabled = false; 34 | bool reversedPositions = false; 35 | 36 | std::vector positionTable; 37 | 38 | int numberOfContainedElements = 0; 39 | std::vector items; 40 | 41 | GridContainerComponent(int columns, int lines, float left, float up, float right, float down, float space, 42 | bool stretch = false, ItemSetMode itemSetMode = FIXED_GET_FIRST_AVAILABLE, bool reversed = false) 43 | : itemSetMode(itemSetMode), numOfColumns(columns), numOfLines(lines), maxNumOfColumns(columns), maxNumOfLines(lines), 44 | marginLeft(left), marginUp(up), marginRight(right), marginDown(down), spaceBetween(space), stretchEnabled(stretch), 45 | reversedPositions(reversed) 46 | { 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/GridContainerSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "raylib.h" 3 | #include "System.h" 4 | #include "Components.h" 5 | #include "GridAddRemoveEvent.h" 6 | #include 7 | 8 | class GridContainerSystem : public ISystem 9 | { 10 | public: 11 | 12 | GridContainerSystem() : ISystem(std::string("GridContainerSystem")) {} 13 | 14 | void Initialize() override 15 | { 16 | for (auto& e : pool->GetEntities(1 << GetComponentTypeID() | 1 << GetComponentTypeID())) 17 | { 18 | auto& grid = e->Get(); 19 | Update(e); 20 | grid.needsUpdate = false; 21 | } 22 | } 23 | 24 | void Execute() override 25 | { 26 | for (auto& e : pool->GetEntities(1 << GetComponentTypeID() | 1 << GetComponentTypeID())) 27 | { 28 | auto& grid = e->Get(); 29 | if (grid.needsUpdate) 30 | { 31 | Update(e); 32 | grid.needsUpdate = false; 33 | } 34 | } 35 | } 36 | 37 | void CreateFrame(EntityPtr e) 38 | { 39 | auto& grid = e->Get(); 40 | const auto pos = e->Get(); 41 | 42 | float totalHeight; 43 | float totalWidth; 44 | 45 | float width; 46 | float height; 47 | 48 | if (grid.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES && !grid.items.empty()) 49 | { 50 | const auto transfComp = grid.items.front()->Get().position; 51 | width = transfComp.width; 52 | height = transfComp.height; 53 | 54 | totalWidth = width * grid.numOfColumns + (grid.numOfColumns - 1)*grid.spaceBetween; 55 | totalHeight = height * grid.numOfLines + (grid.numOfLines - 1)*grid.spaceBetween; 56 | } 57 | else 58 | { 59 | totalWidth = pos.position.width - grid.marginLeft - grid.marginRight - grid.spaceBetween * (static_cast(grid.numOfColumns) - 1.0f); 60 | totalHeight = pos.position.height - grid.marginUp - grid.marginDown - grid.spaceBetween * (static_cast(grid.numOfLines) - 1.0f); 61 | 62 | width = totalWidth / static_cast(grid.numOfColumns); 63 | height = totalHeight / static_cast(grid.numOfLines); 64 | } 65 | 66 | for (int lin = 0; lin < grid.numOfLines; lin++) 67 | for (int col = 0; col < grid.numOfColumns; col++) 68 | {//CAUTION: turning corner position into center positions 69 | float x; 70 | float y; 71 | 72 | if (grid.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES) 73 | { 74 | x = pos.position.x + pos.position.width / 2 - totalWidth/2 + col * width + width/2 + grid.spaceBetween * col; 75 | y = pos.position.y + pos.position.height / 2 - totalHeight/2 + lin * height + height/2 + grid.spaceBetween* lin; 76 | } 77 | else { 78 | x = pos.position.x + grid.marginLeft + grid.spaceBetween * static_cast(col) + width * static_cast(col) + width / 2; 79 | y = pos.position.y + grid.marginUp + grid.spaceBetween * static_cast(lin) + height * static_cast(lin) + height / 2; 80 | } 81 | 82 | const Rectangle aux{ x,y,width,height }; 83 | 84 | if (!grid.reversedPositions) 85 | grid.positionTable[(grid.numOfLines - 1)* lin + col] = aux; 86 | else 87 | grid.positionTable[grid.numOfLines*grid.numOfColumns - 1 - (grid.numOfLines - 1)* lin - col] = aux; 88 | } 89 | } 90 | int GetFreeFrameIdx(EntityPtr e) 91 | { 92 | auto& grid = e->Get(); 93 | 94 | unsigned idx = 0; 95 | for (auto& el : grid.items) 96 | { 97 | if (el == nullptr) 98 | { 99 | break; 100 | } 101 | ++idx; 102 | } 103 | 104 | if (grid.itemSetMode == GridContainerComponent::FIXED_GET_FIRST_AVAILABLE) { 105 | if (idx == grid.items.size()) 106 | return -1; 107 | return idx; 108 | } 109 | if (grid.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES || grid.itemSetMode == GridContainerComponent::INFINITE_STACK) { 110 | return grid.items.size(); 111 | } 112 | //IF SETINPLACE - TREBuie verificat in ce locatie trebuie pusa cartea 113 | 114 | return -1; 115 | } 116 | void RecountFrameCells(EntityPtr e) 117 | { 118 | auto& grid = e->Get(); 119 | 120 | if (grid.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES) 121 | { 122 | grid.numOfColumns = 0; 123 | grid.numOfLines = 0; 124 | 125 | if (grid.numberOfContainedElements == 0) 126 | return; 127 | 128 | grid.numOfLines = 1; 129 | if (grid.numberOfContainedElements <= grid.maxNumOfColumns) 130 | { 131 | grid.numOfColumns = grid.numberOfContainedElements; 132 | return; 133 | } 134 | 135 | grid.numOfColumns = grid.maxNumOfColumns; 136 | 137 | while(grid.numOfColumns*grid.numOfLines < grid.numberOfContainedElements) 138 | { 139 | ++grid.numOfLines; 140 | } 141 | } 142 | } 143 | void PlaceItemsInFrame(EntityPtr e) 144 | { 145 | auto& grid = e->Get(); 146 | auto& transf = e->Get(); 147 | 148 | if (grid.itemSetMode == GridContainerComponent::FIXED_GET_FIRST_AVAILABLE || grid.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES) { 149 | int idx = 0; 150 | for (auto& obj : grid.items) { 151 | 152 | if (obj == nullptr) 153 | continue; 154 | 155 | auto& transfComp = obj->Get(); 156 | 157 | auto& pos = transfComp.position; 158 | auto& z = transfComp.zIndex; 159 | 160 | auto getPos = grid.positionTable[idx]; 161 | 162 | if (grid.stretchEnabled) 163 | { 164 | getPos.x -= getPos.width / 2; 165 | getPos.y -= getPos.height / 2; 166 | } 167 | else 168 | { 169 | getPos.width = pos.width; 170 | getPos.height = pos.height; 171 | getPos.x -= getPos.width / 2; 172 | getPos.y -= getPos.height / 2; 173 | } 174 | 175 | pos = getPos; 176 | z = transf.zIndex + grid.maxNumOfColumns * grid.maxNumOfColumns - idx; 177 | 178 | ++idx; 179 | } 180 | } 181 | else if (grid.itemSetMode == GridContainerComponent::FIXED_SET_IN_PLACE) 182 | { 183 | int idx = 0; 184 | for (auto& obj : grid.items) { 185 | 186 | if (obj == nullptr) 187 | { 188 | ++idx; 189 | continue; 190 | } 191 | 192 | auto& transfComp = obj->Get(); 193 | 194 | auto& pos = transfComp.position; 195 | auto& z = transfComp.zIndex; 196 | 197 | auto getPos = grid.positionTable[idx]; 198 | 199 | if (grid.stretchEnabled) 200 | { 201 | getPos.x -= getPos.width / 2; 202 | getPos.y -= getPos.height / 2; 203 | } 204 | else 205 | { 206 | getPos.width = pos.width; 207 | getPos.height = pos.height; 208 | getPos.x -= getPos.width / 2; 209 | getPos.y -= getPos.height / 2; 210 | } 211 | 212 | pos = getPos; 213 | z = transf.zIndex + grid.maxNumOfColumns * grid.maxNumOfColumns - idx; 214 | 215 | ++idx; 216 | } 217 | } 218 | else if (grid.itemSetMode == GridContainerComponent::INFINITE_STACK) { 219 | int idx = 1; 220 | for (auto& obj : grid.items) { 221 | 222 | if (obj == nullptr) 223 | continue; 224 | 225 | auto& transfComp = obj->Get(); 226 | 227 | auto& pos = transfComp.position; 228 | auto& z = transfComp.zIndex; 229 | 230 | auto getPos = grid.positionTable[0]; 231 | 232 | if (grid.stretchEnabled) 233 | { 234 | getPos.x -= getPos.width / 2; 235 | getPos.y -= getPos.height / 2; 236 | } 237 | else 238 | { 239 | getPos.width = pos.width; 240 | getPos.height = pos.height; 241 | getPos.x -= getPos.width / 2; 242 | getPos.y -= getPos.height / 2; 243 | } 244 | 245 | pos = getPos; 246 | z = transf.zIndex + idx++; 247 | } 248 | } 249 | } 250 | bool AddItem(EntityPtr grid, EntityPtr item) 251 | { 252 | auto& cont = grid->Get(); 253 | 254 | auto idx = GetFreeFrameIdx(grid); 255 | 256 | if (cont.itemSetMode == GridContainerComponent::FIXED_GET_FIRST_AVAILABLE || cont.itemSetMode == GridContainerComponent::FIXED_SET_IN_PLACE) 257 | if (idx == -1) 258 | return false; 259 | 260 | if (cont.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES || cont.itemSetMode == GridContainerComponent::INFINITE_STACK) 261 | cont.items.push_back(nullptr); 262 | 263 | cont.items[idx] = item; 264 | item->Add(grid, idx); 265 | 266 | ++cont.numberOfContainedElements; 267 | 268 | cont.needsUpdate = true; 269 | 270 | return true; 271 | } 272 | void ReleaseItem(EntityPtr e, EntityPtr item) 273 | { 274 | auto& grid = e->Get(); 275 | 276 | auto it = std::find(grid.items.begin(), grid.items.end(), item); 277 | if (it == grid.items.end()) 278 | return; 279 | 280 | int idx = std::distance(grid.items.begin(), it); 281 | 282 | grid.items[idx] = nullptr; 283 | item->Remove(); 284 | 285 | grid.numberOfContainedElements--; 286 | 287 | 288 | grid.items.erase(grid.items.begin() + idx, grid.items.begin() + idx + 1);//erase 289 | 290 | if (grid.itemSetMode == GridContainerComponent::FIXED_GET_FIRST_AVAILABLE) 291 | { 292 | grid.items.push_back(nullptr); 293 | } 294 | 295 | grid.needsUpdate = true; 296 | } 297 | void ReinitFrame(EntityPtr e) 298 | { 299 | auto& grid = e->Get(); 300 | 301 | if (grid.itemSetMode == GridContainerComponent::DYNAMIC_ERASE_SPACES && grid.itemSetMode == GridContainerComponent::FIXED_GET_FIRST_AVAILABLE && grid.items.size() != grid.numOfColumns * grid.numOfLines) 302 | grid.items.resize(grid.numOfColumns * grid.numOfLines); 303 | 304 | grid.positionTable.clear(); 305 | grid.positionTable.resize(grid.numOfColumns * grid.numOfLines); 306 | } 307 | void Update(EntityPtr e) 308 | { 309 | RecountFrameCells(e); 310 | ReinitFrame(e); 311 | CreateFrame(e); 312 | PlaceItemsInFrame(e); 313 | } 314 | 315 | void Receive(const GridAddRemoveEvent& event) 316 | { 317 | if (event.type == GridAddRemoveEvent::ADD) 318 | { 319 | AddItem(event.parent, event.entity); 320 | } 321 | else if(event.type == GridAddRemoveEvent::REMOVE) 322 | { 323 | ReleaseItem(event.parent, event.entity); 324 | } 325 | } 326 | }; 327 | -------------------------------------------------------------------------------- /src/Helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | inline Rectangle operator+(Rectangle &a, Rectangle b) 10 | { 11 | return Rectangle { a.x + b.x,a.y + b.y,a.width + b.width,a.height + b.height }; 12 | } 13 | 14 | inline Rectangle operator-(Rectangle &a, Rectangle b) 15 | { 16 | return Rectangle{ a.x - b.x,a.y - b.y,a.width - b.width,a.height - b.height }; 17 | } 18 | 19 | inline Vector2 operator+(Vector2 &a, Vector2 b) 20 | { 21 | return Vector2{ a.x + b.x,a.y + b.y}; 22 | } 23 | 24 | inline Vector2 operator-(Vector2 &a, Vector2 b) 25 | { 26 | return Vector2{ a.x - b.x,a.y - b.y }; 27 | } 28 | 29 | inline Vector2 operator* (Vector2 &a, Vector2 b) 30 | { 31 | return Vector2{ a.x * b.x,a.y * b.y }; 32 | } 33 | 34 | inline bool operator==(Texture2D& a, Texture2D& b) 35 | { 36 | return a.id == b.id; 37 | } 38 | 39 | inline bool operator==(const Vector2 a, const Vector2 b) 40 | { 41 | return a.x == b.x && a.y == b.y; 42 | } 43 | 44 | inline bool operator!=(const Vector2 a, const Vector2 b) 45 | { 46 | return a.x != b.x || a.y != b.y; 47 | } 48 | 49 | inline bool operator==(Rectangle &a, Rectangle b) 50 | { 51 | return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height; 52 | } 53 | 54 | inline bool operator!=(Rectangle &a, Rectangle b) 55 | { 56 | return !(a == b); 57 | } 58 | 59 | inline bool operator<(const Vector2& a, const Vector2& b) 60 | { 61 | return a.x < b.x && a.y < b.y; 62 | } 63 | 64 | inline bool operator>(const Vector2& a, const Vector2& b) 65 | { 66 | return !(a.x < b.x && a.y < b.y); 67 | } 68 | 69 | inline bool operator<=(const Vector2& a, const Vector2& b) 70 | { 71 | return a.x <= b.x && a.y <= b.y; 72 | } 73 | 74 | inline bool operator>=(const Vector2& a, const Vector2& b) 75 | { 76 | return a.x >= b.x && a.y >= b.y; 77 | } 78 | 79 | template int sgn(T val) { 80 | return (T(0) < val) - (val < T(0)); 81 | } 82 | 83 | constexpr float radiansToDegrees(float rad) { return rad / (2 * PI) * 360; } 84 | constexpr float degreesToRadians(float deg) { return deg / 360 * (2 * PI); } 85 | 86 | struct Shape 87 | { 88 | std::string name; 89 | 90 | enum ShapeType 91 | { 92 | NONE, 93 | CIRCLE, 94 | RECTANGLE, 95 | TRIANGLE, 96 | POLYGON 97 | } 98 | type = NONE; 99 | 100 | Shape(std::string name, ShapeType type) 101 | : name(std::move(name)), type(type) 102 | {} 103 | 104 | float rotation; 105 | 106 | union 107 | { 108 | struct 109 | { 110 | Vector2 center; 111 | float radius; 112 | } circle; 113 | Rectangle rectangle; 114 | struct 115 | { 116 | Vector2 a; 117 | Vector2 b; 118 | Vector2 c; 119 | } triangle; 120 | struct 121 | { 122 | Vector2 center; 123 | int sides; 124 | float radius; 125 | } poly; 126 | }; 127 | 128 | float GetCenterX() 129 | { 130 | switch (type) 131 | { 132 | case CIRCLE: 133 | return circle.center.x; 134 | case RECTANGLE: 135 | return rectangle.x + rectangle.width / 2; 136 | case TRIANGLE: 137 | return (triangle.a.x + triangle.b.x + triangle.c.x) / 3; 138 | case POLYGON: 139 | return poly.center.x; 140 | default: 141 | break; 142 | } 143 | 144 | return 0.0f; 145 | } 146 | 147 | float GetCenterY() 148 | { 149 | switch (type) 150 | { 151 | case CIRCLE: 152 | return circle.center.y; 153 | case RECTANGLE: 154 | return rectangle.y + rectangle.height / 2; 155 | case TRIANGLE: 156 | return (triangle.a.y + triangle.b.y + triangle.c.y) / 3; 157 | case POLYGON: 158 | return poly.center.y; 159 | default: 160 | break; 161 | } 162 | return 0.0f; 163 | } 164 | 165 | void SetCenterX(float newX) 166 | { 167 | float change = newX - GetCenterX(); 168 | switch (type) 169 | { 170 | case CIRCLE: 171 | circle.center.x = newX; 172 | break; 173 | case RECTANGLE: 174 | rectangle.x = newX - rectangle.width / 2; 175 | break; 176 | case TRIANGLE: 177 | triangle.a.x += change; 178 | triangle.b.x += change; 179 | triangle.c.x += change; 180 | 181 | break; 182 | case POLYGON: 183 | poly.center.x = newX; 184 | break; 185 | default: 186 | break; 187 | } 188 | } 189 | 190 | void SetCenterY(float newY) 191 | { 192 | float change = newY - GetCenterY(); 193 | switch (type) 194 | { 195 | case CIRCLE: 196 | circle.center.y = newY; 197 | break; 198 | case RECTANGLE: 199 | rectangle.y = newY - rectangle.height / 2; 200 | break; 201 | case TRIANGLE: 202 | triangle.a.y += change; 203 | triangle.b.y += change; 204 | triangle.c.y += change; 205 | break; 206 | case POLYGON: 207 | poly.center.y = newY; 208 | break; 209 | default: 210 | break; 211 | } 212 | } 213 | 214 | void Draw() 215 | { 216 | switch (type) 217 | { 218 | case CIRCLE: 219 | 220 | DrawCircle(circle.center.x, circle.center.y, circle.radius, Fade(BLUE, 0.3f)); 221 | break; 222 | case RECTANGLE: 223 | DrawRectanglePro(Rectangle{ rectangle.x, rectangle.y, rectangle.width, rectangle.height }, Vector2{ 0, 0 }, rotation * (180.0 / 3.141592653589793238463), Fade(BLUE, 0.3f)); 224 | DrawCircle(rectangle.x, rectangle.y, 10, BLACK); 225 | break; 226 | case TRIANGLE: 227 | //DrawTriangle(shape.triangle.a, shape.triangle.b, shape.triangle.c, shape.color); 228 | break; 229 | case POLYGON: 230 | DrawPoly(poly.center, poly.sides, poly.radius, rotation, BLUE); 231 | break; 232 | default: 233 | break; 234 | } 235 | } 236 | 237 | //Attached Shape 238 | Vector2 mainBodyCenter; 239 | float angleFromMainBody; // in radians 240 | 241 | void SetMainBodyCenter(Vector2 mainBodyCenter) 242 | { 243 | this->mainBodyCenter.x = mainBodyCenter.x; 244 | this->mainBodyCenter.y = mainBodyCenter.y; 245 | 246 | UpdateAngleFromMainBody(); 247 | } 248 | 249 | void SetMainBodyCenter(Shape mainBody) 250 | { 251 | mainBodyCenter.x = mainBody.GetCenterX(); 252 | mainBodyCenter.y = mainBody.GetCenterY(); 253 | 254 | UpdateAngleFromMainBody(); 255 | } 256 | 257 | void SetMainBodyCenterWithoutUpdate(Shape mainBody) 258 | { 259 | mainBodyCenter.x = mainBody.GetCenterX(); 260 | mainBodyCenter.y = mainBody.GetCenterY(); 261 | } 262 | 263 | void UpdateAngleFromMainBody() 264 | { 265 | float delta_x = GetCenterX() - mainBodyCenter.x; 266 | float delta_y = GetCenterY() - mainBodyCenter.y; 267 | angleFromMainBody = atan2(delta_y, delta_x); 268 | } 269 | 270 | float GetDistanceFromMainX() 271 | { 272 | return abs(GetCenterX() - mainBodyCenter.x); 273 | } 274 | 275 | float GetDistanceFromMainY() 276 | { 277 | return abs(GetCenterY() - mainBodyCenter.y); 278 | } 279 | 280 | float GetDistanceFromMain() 281 | { 282 | return sqrtf(powf(GetDistanceFromMainX(), 2) + powf(GetDistanceFromMainY(), 2)); 283 | } 284 | }; 285 | 286 | struct ShapeContainer 287 | { 288 | std::string name; 289 | 290 | Shape origin_position; 291 | Vector2 origin_orientation; 292 | 293 | std::vector shapes; 294 | 295 | ShapeContainer(std::string name, Shape mainBody, Vector2 orientation) 296 | : name(std::move(name)), origin_position(mainBody),origin_orientation(orientation) 297 | {} 298 | 299 | void AddShape(Shape s) 300 | { 301 | shapes.push_back(s); 302 | } 303 | 304 | void Update() 305 | { 306 | for (auto& shape : shapes) 307 | { 308 | Vector2 newCenter; 309 | 310 | newCenter.x = origin_position.GetCenterX() - shape.mainBodyCenter.x; 311 | newCenter.y = origin_position.GetCenterY() - shape.mainBodyCenter.y; 312 | 313 | shape.SetMainBodyCenterWithoutUpdate(origin_position); 314 | 315 | shape.SetCenterX(shape.GetCenterX() + newCenter.x); 316 | shape.SetCenterY(shape.GetCenterY() + newCenter.y); 317 | 318 | if (origin_position.rotation != 0.00f && origin_position.rotation != shape.rotation) 319 | { 320 | 321 | float NX = shape.mainBodyCenter.x + shape.GetDistanceFromMain() * cosf(shape.angleFromMainBody + origin_position.rotation); 322 | float NY = shape.mainBodyCenter.y + shape.GetDistanceFromMain() * sinf(shape.angleFromMainBody + origin_position.rotation); 323 | 324 | shape.SetCenterX(NX); 325 | shape.SetCenterY(NY); 326 | 327 | shape.rotation = origin_position.rotation; 328 | } 329 | } 330 | } 331 | 332 | //Mirror by the x or y axis 333 | void Mirror(Vector2 orientation) 334 | { 335 | if (bool(orientation.x) != bool(origin_orientation.x)) 336 | { 337 | for (auto& shape : shapes) 338 | { 339 | float NX = shape.mainBodyCenter.x + (bool(orientation.x) ? -1 : 1) * shape.GetDistanceFromMain() * cosf(shape.angleFromMainBody + origin_position.rotation); 340 | 341 | shape.SetCenterX(NX); 342 | } 343 | 344 | origin_orientation.x = bool(orientation.x); 345 | } 346 | 347 | if (bool(orientation.y) != bool(origin_orientation.y)) 348 | { 349 | for (auto& shape : shapes) 350 | { 351 | float NY = shape.mainBodyCenter.y + (bool(orientation.y) ? -1 : 1) * shape.GetDistanceFromMain() * sinf(shape.angleFromMainBody + origin_position.rotation); 352 | 353 | shape.SetCenterY(NY); 354 | } 355 | 356 | origin_orientation.y = bool(orientation.y); 357 | } 358 | 359 | Update(); 360 | } 361 | 362 | void Draw() 363 | { 364 | origin_position.Draw(); 365 | for (auto& shape : shapes) 366 | { 367 | shape.Draw(); 368 | } 369 | } 370 | }; 371 | 372 | inline bool operator < (const char* c, const std::string& str){ 373 | return str.find(c) != std::string::npos; 374 | } 375 | 376 | #define _in < -------------------------------------------------------------------------------- /src/HitBoxComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | struct HitBoxComponent : IComponent 3 | { 4 | std::vector containers; 5 | ShapeContainer* current_container = nullptr; 6 | 7 | Vector2 last_origin_position; 8 | 9 | HitBoxComponent(std::vector containers, Vector2 origin_position) 10 | :containers(std::move(containers)), last_origin_position(origin_position) 11 | { 12 | if (!this->containers.empty()) 13 | current_container = &(this->containers[0]); 14 | } 15 | 16 | void Update() 17 | { 18 | for (auto& c : containers) 19 | { 20 | c.Update(); 21 | } 22 | } 23 | 24 | void Mirror(Vector2 orientation) 25 | { 26 | for (auto& c : containers) 27 | { 28 | c.Mirror(orientation); 29 | } 30 | } 31 | }; -------------------------------------------------------------------------------- /src/HitBoxEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Helpers.h" 3 | #include 4 | 5 | struct HitBoxTriggerInfo 6 | { 7 | EntityPtr e1; 8 | Shape s1; 9 | 10 | EntityPtr e2; 11 | Shape s2; 12 | }; 13 | 14 | struct HitBoxEvent : IEvent 15 | { 16 | std::vector allTriggerInfos; 17 | 18 | HitBoxEvent(std::vector allTriggerInfos) 19 | :allTriggerInfos(std::move(allTriggerInfos)) 20 | {} 21 | }; -------------------------------------------------------------------------------- /src/HitBoxSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "HitBoxEvent.h" 3 | 4 | class HitBoxSystem : public ISystem 5 | { 6 | public: 7 | HitBoxSystem() : ISystem("HitBoxSystem") {} 8 | 9 | void Initialize() override {} 10 | 11 | //DOCS:Ive decided that the dependencies between components should be 12 | //(for now) solved by one of the system that works with the components 13 | //heavily. Ex: Physics and Transform should be solved by Physics 14 | void Execute() override 15 | { 16 | auto entities = pool->GetEntities(1 << GetComponentTypeID()); 17 | for (auto e : entities) 18 | { 19 | auto& box = e->Get(); 20 | 21 | if (!box.current_container) 22 | continue; 23 | 24 | if (e->Has()) { 25 | auto& transf = e->Get(); 26 | 27 | Vector2 delta = {transf.position.x - box.last_origin_position.x, transf.position.y - box.last_origin_position.y }; 28 | 29 | box.current_container->origin_position.SetCenterX(box.current_container->origin_position.GetCenterX() + delta.x); 30 | box.current_container->origin_position.SetCenterY(box.current_container->origin_position.GetCenterY() + delta.y); 31 | 32 | box.current_container->origin_position.rotation = transf.rotation; 33 | 34 | box.last_origin_position = { transf.position.x, transf.position.y }; 35 | } 36 | 37 | box.current_container->Update(); 38 | } 39 | 40 | std::vector allTriggerInfos; 41 | 42 | for (unsigned i = 0; i < entities.size() - 1; ++i) { 43 | for (unsigned j = i + 1; j < entities.size(); ++j) 44 | { 45 | auto& box1 = entities[i]->Get(); 46 | auto& box2 = entities[j]->Get(); 47 | 48 | if (!box1.current_container || !box2.current_container) 49 | continue; 50 | 51 | auto shapes1 = box1.current_container->shapes; 52 | shapes1.push_back(box1.current_container->origin_position); 53 | 54 | auto shapes2 = box2.current_container->shapes; 55 | shapes2.push_back(box2.current_container->origin_position); 56 | 57 | for (auto& shape_left : shapes1) 58 | { 59 | for (auto& shape_right : shapes2) 60 | { 61 | if (shape_left.type == Shape::RECTANGLE && shape_right.type == Shape::RECTANGLE) 62 | { 63 | if (CheckCollisionRecs(shape_left.rectangle, shape_right.rectangle)) 64 | { 65 | allTriggerInfos.push_back(HitBoxTriggerInfo{ entities[i], shape_left, entities[j],shape_right }); 66 | } 67 | } 68 | else if (shape_left.type == Shape::CIRCLE && shape_right.type == Shape::CIRCLE) 69 | { 70 | if (CheckCollisionCircles(Vector2{ float(shape_left.circle.center.x), float(shape_left.circle.center.x) }, shape_left.circle.radius, 71 | Vector2{ float(shape_right.circle.center.x), float(shape_right.circle.center.x) }, shape_right.circle.radius)) 72 | { 73 | allTriggerInfos.push_back(HitBoxTriggerInfo{ entities[i], shape_left, entities[j],shape_right }); 74 | } 75 | } 76 | else if (shape_left.type == Shape::CIRCLE && Shape::RECTANGLE == shape_right.type) 77 | { 78 | if (CheckCollisionCircleRec(Vector2{ float(shape_left.circle.center.x), float(shape_left.circle.center.x) }, shape_left.circle.radius, 79 | shape_right.rectangle)) 80 | { 81 | allTriggerInfos.push_back(HitBoxTriggerInfo{ entities[i], shape_left, entities[j],shape_right }); 82 | } 83 | } 84 | else if (Shape::CIRCLE == shape_right.type && shape_left.type == Shape::RECTANGLE) 85 | { 86 | if (CheckCollisionCircleRec(Vector2{ float(shape_right.circle.center.x), float(shape_right.circle.center.x) }, shape_right.circle.radius, 87 | shape_left.rectangle)) 88 | { 89 | allTriggerInfos.push_back(HitBoxTriggerInfo{ entities[i], shape_left, entities[j],shape_right }); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | if (! allTriggerInfos.empty()) 98 | eventManager->Notify(allTriggerInfos); 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /src/KeyboardEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | struct KeyboardEvent : IEvent 3 | { 4 | struct TriggeredEntity { 5 | EntityPtr entity; 6 | 7 | std::vector pressedKeys; 8 | std::vector releasedKeys; 9 | std::vector heldKeys; 10 | }; 11 | 12 | std::vector triggered_entities; 13 | 14 | KeyboardEvent(std::vector triggered_entities) 15 | : triggered_entities(std::move(triggered_entities)) 16 | {} 17 | }; -------------------------------------------------------------------------------- /src/KeyboardInputComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | #include 4 | 5 | struct KeyboardInputComponent : IComponent 6 | { 7 | std::vector gestures; 8 | 9 | template 10 | KeyboardInputComponent(TArgs&&... mArgs) : gestures(std::vector{std::forward(mArgs)...}) 11 | { 12 | } 13 | }; -------------------------------------------------------------------------------- /src/KeyboardInputSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KeyboardEvent.h" 3 | #include "KeyboardInputComponent.h" 4 | 5 | class KeyboardInputSystem : public ISystem 6 | { 7 | std::vector trackedKeys = { KEY_LEFT, KEY_RIGHT,KEY_UP, KEY_DOWN, KEY_X, KEY_Z, KEY_ENTER, KEY_ESCAPE }; 8 | 9 | public: 10 | KeyboardInputSystem() : ISystem("KeyboardInputSystem") {} 11 | 12 | void Initialize() override {} 13 | 14 | void Execute() override 15 | { 16 | std::vector pressedKeys; 17 | std::vector releasedKeys; 18 | std::vector heldKeys; 19 | 20 | for (auto k : trackedKeys) 21 | { 22 | if (IsKeyPressed(k)) 23 | pressedKeys.push_back(k); 24 | else if (IsKeyReleased(k)) 25 | releasedKeys.push_back(k); 26 | else if (IsKeyDown(k)) 27 | heldKeys.push_back(k); 28 | } 29 | 30 | std::vector triggered_entities; 31 | 32 | for (auto& e : pool->GetEntities(1 << GetComponentTypeID())) 33 | { 34 | auto& comp = e->Get(); 35 | 36 | std::vector componentPressedKeys; 37 | std::vector componentReleasedKeys; 38 | std::vector componentHeldKeys; 39 | 40 | for (auto pkey : pressedKeys) 41 | { 42 | if (std::find(comp.gestures.begin(), comp.gestures.end(), pkey) != comp.gestures.end()) 43 | componentPressedKeys.push_back(pkey); 44 | } 45 | 46 | for (auto rkey : releasedKeys) 47 | { 48 | if (std::find(comp.gestures.begin(), comp.gestures.end(), rkey) != comp.gestures.end()) 49 | componentReleasedKeys.push_back(rkey); 50 | } 51 | 52 | for (auto hkey : heldKeys) 53 | { 54 | if (std::find(comp.gestures.begin(), comp.gestures.end(), hkey) != comp.gestures.end()) 55 | componentHeldKeys.push_back(hkey); 56 | } 57 | 58 | if (!componentPressedKeys.empty() || !componentReleasedKeys.empty() || !componentHeldKeys.empty()) 59 | triggered_entities.push_back(KeyboardEvent::TriggeredEntity{ e, componentPressedKeys, componentReleasedKeys, componentHeldKeys }); 60 | } 61 | 62 | if (! triggered_entities.empty()) 63 | eventManager->Notify(triggered_entities); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /src/MouseEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Event.h" 3 | 4 | struct MouseEvent : IEvent 5 | { 6 | enum MouseAction 7 | { 8 | NONE, 9 | MOUSE_PRESS, 10 | MOUSE_BEGIN_DRAG, 11 | MOUSE_CONTINUE_DRAG, 12 | MOUSE_END_DRAG, 13 | } type = NONE; 14 | 15 | EntityPtr entity; 16 | Vector2 mousePos; 17 | 18 | MouseEvent(MouseAction action, EntityPtr entity, Vector2 mouse) : type(action), entity(entity), mousePos(mouse) 19 | { 20 | } 21 | }; -------------------------------------------------------------------------------- /src/MouseInputComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | 4 | struct MouseInputComponent : IComponent 5 | { 6 | enum Gestures 7 | { 8 | SELECT, 9 | PRESS, 10 | DRAG 11 | }; 12 | 13 | std::bitset<32> gestures; 14 | 15 | MouseInputComponent(std::bitset<32> gestures) : gestures(gestures) {} 16 | }; -------------------------------------------------------------------------------- /src/MouseInputSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "raylib.h" 3 | #include "System.h" 4 | #include "Components.h" 5 | #include "MouseEvent.h" 6 | 7 | class MouseInputSystem : public ISystem 8 | { 9 | EntityPtr entity; 10 | Vector2 mouseGrab = {0,0}; 11 | bool dragStarted = false; 12 | int previousGesture = GESTURE_NONE; 13 | 14 | void OnSelect(Vector2 mouse) 15 | { 16 | auto entities = pool->GetEntities([](EntityPtr ptr, void* context)->bool 17 | { 18 | return ptr->Has(1 << GetComponentTypeID() | 1 << GetComponentTypeID()) && 19 | CheckCollisionPointRec(*static_cast(context), ptr->Get().position); 20 | }, &mouse); 21 | 22 | std::sort(entities.begin(), entities.end(), [](EntityPtr a, EntityPtr b) 23 | { 24 | return a->Get().zIndex < b->Get().zIndex; 25 | }); 26 | 27 | if (entities.back()->Has(1 << GetComponentTypeID()) && entities.back()->Get().gestures[MouseInputComponent::SELECT]) 28 | { 29 | entity = entities.back(); 30 | } 31 | } 32 | 33 | void OnPress(Vector2 mouse) 34 | { 35 | eventManager->Notify(MouseEvent::MOUSE_PRESS, entity, mouse); 36 | } 37 | 38 | void OnBeginDrag(Vector2 mouse) 39 | { 40 | dragStarted = true; 41 | auto r = entity->Get().position; 42 | mouseGrab = { mouse.x - r.x, mouse.y - r.y }; 43 | 44 | eventManager->Notify(MouseEvent::MOUSE_BEGIN_DRAG, entity, mouse); 45 | } 46 | 47 | void OnContinueDrag(Vector2 mouse) 48 | { 49 | auto r = entity->Get().position; 50 | entity->Get().position = { mouse.x - mouseGrab.x,mouse.y - mouseGrab.y, r.width, r.height }; 51 | 52 | eventManager->Notify(MouseEvent::MOUSE_CONTINUE_DRAG, entity, mouse); 53 | } 54 | 55 | void OnEndDrag(Vector2 mouse)//aici o sa am nevoie de coordonate mouse deci cred ca trebuie sa scap de MouseEvent sau sa il modific sau cv 56 | { 57 | eventManager->Notify(MouseEvent::MOUSE_END_DRAG, entity, mouse); 58 | 59 | dragStarted = false; 60 | mouseGrab = { 0,0 }; 61 | entity = nullptr; 62 | } 63 | 64 | public: 65 | 66 | MouseInputSystem() : ISystem(std::string("MouseInputSystem")) {} 67 | 68 | void Initialize() override {} 69 | 70 | void Execute() override 71 | { 72 | const int lastGesture = GetGestureDetected(); 73 | const Vector2 mouse = GetMousePosition(); 74 | 75 | if (lastGesture == GESTURE_NONE) 76 | { 77 | //this could be the place for uninit 78 | } 79 | 80 | if (lastGesture == GESTURE_TAP) 81 | { 82 | OnSelect(mouse); 83 | } 84 | else if( (previousGesture == GESTURE_TAP && lastGesture == GESTURE_NONE ) || (previousGesture == GESTURE_HOLD && lastGesture == GESTURE_NONE) || (previousGesture == GESTURE_DOUBLETAP && lastGesture == GESTURE_NONE)) 85 | { 86 | if (entity != nullptr && entity->Get().gestures[MouseInputComponent::PRESS]) 87 | OnPress(mouse); 88 | } 89 | 90 | if (lastGesture == GESTURE_DRAG) 91 | { 92 | if (!dragStarted) { 93 | 94 | if (entity != nullptr && (entity->Get().gestures[MouseInputComponent::DRAG])) { 95 | OnBeginDrag(mouse); 96 | } 97 | } 98 | else 99 | { 100 | OnContinueDrag(mouse); 101 | } 102 | } 103 | else if (previousGesture == GESTURE_DRAG || previousGesture == GESTURE_HOLD) { 104 | if (dragStarted) 105 | { 106 | if (lastGesture == GESTURE_HOLD) 107 | OnContinueDrag(mouse); 108 | else 109 | OnEndDrag(mouse); 110 | } 111 | } 112 | 113 | previousGesture = lastGesture; 114 | } 115 | }; 116 | -------------------------------------------------------------------------------- /src/NetworkEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Event.h" 3 | #include "json.hpp" 4 | #include 5 | 6 | struct NetworkEvent : IEvent 7 | { 8 | enum NetworkEventType 9 | { 10 | SEND, 11 | RECEIVE 12 | } type; 13 | 14 | std::vector> packets; 15 | 16 | NetworkEvent(NetworkEventType type, std::vector> packets) 17 | : type(type), packets(std::move(packets)) 18 | { 19 | } 20 | 21 | NetworkEvent(NetworkEventType type, const nlohmann::json& json) 22 | : type(type) 23 | { 24 | auto str = json.dump(); 25 | std::vector packet; 26 | packet.insert(packet.begin(), str.begin(), str.end() + 1); 27 | packets.push_back(packet); 28 | } 29 | }; -------------------------------------------------------------------------------- /src/NetworkSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "NetworkSystem.h" 2 | #include 3 | #include "NetworkEvent.h" 4 | #include "EventManager.h" 5 | #include "External.h" 6 | #ifdef WIN32 7 | #include 8 | #elif __linux__ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | struct ClientSocket 17 | { 18 | #ifdef WIN32 19 | sockaddr_in address; 20 | int clientSize; 21 | SOCKET clientSocket; 22 | #elif __linux__ 23 | int cl; 24 | #endif 25 | }; 26 | 27 | bool INetworkSystem::signal_access(AccessType type, bool value) 28 | { 29 | #ifdef WIN32 30 | std::lock_guard guard(signal_mutex); 31 | #else 32 | pthread_mutex_lock(&signal_mutex); 33 | #endif 34 | 35 | if (type == WRITE_TYPE) 36 | { 37 | stop_thread = value; 38 | } 39 | 40 | #ifdef __linux__ 41 | pthread_mutex_unlock(&signal_mutex); 42 | #endif 43 | 44 | return stop_thread; 45 | } 46 | 47 | void* main_thread_f_wrapper(void* ptr) 48 | { 49 | std::bind(&INetworkSystem::RunMainThread, (INetworkSystem*)ptr)(); 50 | return nullptr; 51 | } 52 | 53 | void INetworkSystem::Initialize() 54 | { 55 | if (type == CLIENT) { 56 | #ifdef WIN32 57 | // Initialize WinSock 58 | WSAData data; 59 | WORD ver = MAKEWORD(2, 2); 60 | int wsResult = WSAStartup(ver, &data); 61 | if (wsResult != 0) 62 | { 63 | std::cout << "Can't start Winsock, Err #" << wsResult << std::endl; 64 | signal_access(WRITE_TYPE, true); 65 | return; 66 | } 67 | 68 | // Create socket 69 | SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); 70 | if (sock == INVALID_SOCKET) 71 | { 72 | std::cout << "Can't create socket, Err #" << WSAGetLastError() << std::endl; 73 | WSACleanup(); 74 | signal_access(WRITE_TYPE, true); 75 | return; 76 | } 77 | 78 | socket_ptr = sock; 79 | 80 | // Fill in a hint structure 81 | sockaddr_in hint; 82 | hint.sin_family = AF_INET; 83 | hint.sin_port = htons(port); 84 | inet_pton(AF_INET, server_address.c_str(), &hint.sin_addr); 85 | 86 | // Connect to server 87 | int connResult = connect(sock, (sockaddr*)&hint, sizeof(hint)); 88 | if (connResult == SOCKET_ERROR) 89 | { 90 | std::cout << "Can't connect to server, Err #" << WSAGetLastError() << std::endl; 91 | closesocket(sock); 92 | WSACleanup(); 93 | signal_access(WRITE_TYPE, true); 94 | return; 95 | } 96 | 97 | u_long iMode = 1; 98 | auto iResult = ioctlsocket(sock, FIONBIO, &iMode); 99 | if (iResult != NO_ERROR) 100 | printf("ioctlsocket failed with error: %ld\n", iResult); 101 | 102 | #elif __linux__ 103 | sockaddr_in server; 104 | 105 | if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 106 | { 107 | perror("Error on socket().\n"); 108 | signal_access(WRITE_TYPE, true); 109 | return; 110 | } 111 | 112 | server.sin_family = AF_INET; 113 | server.sin_addr.s_addr = inet_addr(server_address.c_str()); 114 | server.sin_port = htons(port); 115 | 116 | if (connect(sd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) 117 | { 118 | perror("Error on connect().\n"); 119 | signal_access(WRITE_TYPE, true); 120 | return; 121 | } 122 | 123 | int flags = fcntl(sd, F_GETFL); 124 | fcntl(sd, F_SETFL, flags | O_NONBLOCK); 125 | #endif 126 | } 127 | 128 | #ifdef WIN32 129 | nt = std::make_shared(&INetworkSystem::RunMainThread, this); 130 | #elif __linux__ 131 | if (pthread_mutex_init(&buffer_mutex, NULL) != 0 || pthread_mutex_init(&signal_mutex, NULL) != 0) 132 | { 133 | printf("\n mutex init failed\n"); 134 | signal_access(WRITE_TYPE, true); 135 | return; 136 | } 137 | 138 | int err = pthread_create(&nt, NULL, &main_thread_f_wrapper, (void*)this); 139 | if (err != 0) 140 | printf("\ncan't create thread :[%s]", strerror(err)); 141 | #endif 142 | std::cout << "Thread started" << std::endl; 143 | } 144 | 145 | std::vector> INetworkSystem::gather_packets() 146 | { 147 | std::vector> packets; 148 | if (type == SERVER) { 149 | int got_one; 150 | 151 | do { 152 | got_one = false; 153 | 154 | for (auto &client : client_sockets) { 155 | char buffer[4096]; 156 | 157 | int bytesReceived; 158 | std::vector packets_socket; 159 | packets.push_back(packets_socket); 160 | #ifdef WIN32 161 | ZeroMemory(buffer, 4096); 162 | 163 | // Wait for client to send data 164 | bytesReceived = recv(client->clientSocket, buffer, 4096, 0); 165 | if (bytesReceived <= 0 ) 166 | { 167 | if (WSAGetLastError() != WSAEWOULDBLOCK) { 168 | std::cout << "Error in recv(). Quitting" << std::endl; 169 | signal_access(WRITE_TYPE, true); 170 | return packets; 171 | } 172 | else 173 | { 174 | continue; 175 | } 176 | } 177 | 178 | #elif __linux__ 179 | bytesReceived = read(client->cl, buffer, 4096); 180 | 181 | if (bytesReceived <= 0) { 182 | if (errno != EWOULDBLOCK){ 183 | perror("Error in read(). Quitting.\n"); 184 | signal_access(WRITE_TYPE, true); 185 | return packets; 186 | } 187 | else { 188 | continue; 189 | } 190 | } 191 | 192 | #endif 193 | if (signal_access(READ_TYPE, false)) 194 | break; 195 | 196 | got_one = true; 197 | 198 | if (bytesReceived > 0) { 199 | std::vector packet; 200 | packet.insert(packet.begin(), buffer, buffer + bytesReceived); 201 | packets.back().push_back(packet); 202 | } 203 | } 204 | } while(got_one == true); 205 | } 206 | else { 207 | char buffer[4096]; 208 | 209 | int bytesReceived; 210 | #ifdef WIN32 211 | ZeroMemory(buffer, 4096); 212 | bytesReceived = recv(socket_ptr, buffer, 4096, 0); 213 | if(bytesReceived <= 0) 214 | { 215 | if (WSAGetLastError() != WSAEWOULDBLOCK) { 216 | std::cout << "Error on recv(). Quitting " << WSAGetLastError() << "\n"; 217 | signal_access(WRITE_TYPE, true); 218 | return packets; 219 | } 220 | } 221 | 222 | #elif __linux__ 223 | bytesReceived = read(sd, buffer, 4096); 224 | if (bytesReceived <= 0) { 225 | if (errno != EWOULDBLOCK){ 226 | perror("Error in read(). Quitting.\n"); 227 | signal_access(WRITE_TYPE, true); 228 | return packets; 229 | } 230 | } 231 | #endif 232 | std::vector packet; 233 | if (bytesReceived > 0) 234 | packet.insert(packet.begin(), buffer, buffer + bytesReceived); 235 | packets.push_back({ packet }); 236 | } 237 | 238 | return packets; 239 | } 240 | 241 | void INetworkSystem::send_packets(std::vector> packets) 242 | { 243 | if (type == SERVER) { 244 | if (client_sockets.size() != packets.size()) { 245 | packets.resize(client_sockets.size()); 246 | } 247 | 248 | for (unsigned int i = 0; i < client_sockets.size(); ++i) { 249 | for (auto& packet : packets[i]) { 250 | if (packet.empty()) 251 | continue; 252 | #ifdef WIN32 253 | if (send(client_sockets[i]->clientSocket, &packet[0], packet.size(), 0) <= 0) 254 | { 255 | printf("[Server] Error on send\n"); 256 | signal_access(WRITE_TYPE, true); 257 | } 258 | #elif __linux__ 259 | if (write(client_sockets[i]->cl, &packet[0], packet.size()) <= 0) { 260 | perror("Error in write(). Quitting.\n"); 261 | signal_access(WRITE_TYPE, true); 262 | } 263 | printf("[Server] Sent message\n"); 264 | #endif 265 | } 266 | } 267 | } 268 | else { 269 | for (auto& packet : packets[0]){ 270 | if (packet.empty()) 271 | continue; 272 | #ifdef WIN32 273 | int sendResult = send(socket_ptr, &packet[0], packet.size(), 0); 274 | if (sendResult <= 0) 275 | { 276 | std::cout << "Error on send(). Quitting\n" << WSAGetLastError() << "\n"; 277 | signal_access(WRITE_TYPE, true); 278 | } 279 | #elif __linux__ 280 | if (write(sd, &packet[0], packet.size()) <= 0) { 281 | perror("Error in write(). Quitting.\n"); 282 | signal_access(WRITE_TYPE, true); 283 | } 284 | printf("[Client] Sent message\n"); 285 | #endif 286 | } 287 | } 288 | } 289 | 290 | //NOTE: this must be closed after the listening socket is closed 291 | void INetworkSystem::Destroy() 292 | { 293 | signal_access(WRITE_TYPE, true); 294 | if (type == SERVER) { 295 | #ifdef WIN32 296 | for (auto &c : client_sockets) { 297 | closesocket(c->clientSocket); 298 | } 299 | #elif __linux__ 300 | for (auto &c : client_sockets) { 301 | shutdown(c->cl, 2); 302 | close(c->cl); 303 | c->cl = -1; 304 | } 305 | #endif 306 | } 307 | else { 308 | #ifdef WIN32 309 | closesocket(socket_ptr); 310 | #elif __linux__ 311 | close(sd); 312 | #endif 313 | } 314 | 315 | #ifdef WIN32 316 | nt->join(); 317 | #elif __linux__ 318 | pthread_join(nt, NULL); 319 | pthread_mutex_destroy(&buffer_mutex); 320 | pthread_mutex_destroy(&signal_mutex); 321 | #endif 322 | } 323 | 324 | void NetworkSystem::RunMainThread() 325 | { 326 | std::cout << "Running Main Thread" << std::endl; 327 | if (type == SERVER) { 328 | while(true) 329 | { 330 | if (signal_access(READ_TYPE, false)) 331 | break; 332 | 333 | auto packets = gather_packets(); 334 | 335 | if (signal_access(READ_TYPE, false)) 336 | break; 337 | 338 | Packet new_packet; 339 | for (auto& ps : packets) 340 | { 341 | for (auto& p : ps) { 342 | new_packet.insert(new_packet.end(), p.begin(), p.end()); 343 | } 344 | } 345 | receive_queue_access(WRITE_TYPE, &new_packet); 346 | 347 | std::vector> new_packets; 348 | for (auto& client : client_sockets) { 349 | Packet pack; 350 | for (auto& ps : packets) 351 | { 352 | for (auto& packet : ps) { 353 | pack.insert(pack.end(), packet.begin(), packet.end()); 354 | } 355 | } 356 | new_packets.push_back({ pack }); 357 | } 358 | 359 | send_packets(new_packets); 360 | } 361 | } 362 | else 363 | { 364 | while(true) { 365 | 366 | if (signal_access(READ_TYPE, false)) 367 | break; 368 | 369 | auto send = send_queue_access(READ_TYPE, nullptr); 370 | send_packets({ send }); 371 | 372 | SleepFunc(10); 373 | 374 | auto p = gather_packets(); 375 | 376 | if (signal_access(READ_TYPE, false)) 377 | break; 378 | 379 | for(auto& pack : p[0]) 380 | { 381 | receive_queue_access(WRITE_TYPE, &pack); 382 | } 383 | } 384 | } 385 | } 386 | 387 | void NetworkSystem::Execute() 388 | { 389 | auto packets = receive_queue_access(READ_TYPE, nullptr); 390 | if (!packets.empty()) 391 | eventManager->Notify(NetworkEvent::RECEIVE, packets); 392 | } 393 | 394 | std::vector NetworkSystem::send_queue_access(AccessType type, const Packet* packet) 395 | { 396 | #ifdef WIN32 397 | std::lock_guard guard(buffer_mutex); 398 | #else 399 | pthread_mutex_lock(&buffer_mutex); 400 | #endif 401 | 402 | std::vector packets; 403 | const Packet& data = *packet; 404 | 405 | if (type == WRITE_TYPE) 406 | { 407 | //if (send_queue.is_full()){std::cout << "Warning: Send queue full, dropping data\n";} 408 | send_queue.push(data); 409 | } 410 | else 411 | { 412 | while(! send_queue.empty()) 413 | { 414 | packets.push_back(send_queue.front()); 415 | send_queue.pop(); 416 | } 417 | } 418 | 419 | #ifdef __linux__ 420 | pthread_mutex_unlock(&buffer_mutex); 421 | #endif 422 | 423 | return packets; 424 | } 425 | 426 | std::vector NetworkSystem::receive_queue_access(AccessType type, const Packet* packet) 427 | { 428 | #ifdef WIN32 429 | std::lock_guard guard(buffer_mutex); 430 | #else 431 | pthread_mutex_lock(&buffer_mutex); 432 | #endif 433 | 434 | std::vector packets; 435 | const Packet& data = *packet; 436 | 437 | if (type == WRITE_TYPE) 438 | { 439 | //if (receive_queue.is_full()){std::cout << "Warning: Receive queue full, dropping data\n";} 440 | if (!data.empty()) 441 | receive_queue.push(data); 442 | } 443 | else 444 | { 445 | while (!receive_queue.empty()) 446 | { 447 | packets.push_back(receive_queue.front()); 448 | receive_queue.pop(); 449 | } 450 | } 451 | 452 | #ifdef __linux__ 453 | pthread_mutex_unlock(&buffer_mutex); 454 | #endif 455 | 456 | return packets; 457 | } 458 | 459 | void NetworkSystem::Receive(const NetworkEvent& event) 460 | { 461 | if (event.type == NetworkEvent::SEND) 462 | { 463 | for (auto& p : event.packets) 464 | { 465 | send_queue_access(WRITE_TYPE, &p); 466 | } 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /src/NetworkSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "System.h" 3 | #include "Utils.h" 4 | #include "NetworkEvent.h" 5 | 6 | #include 7 | 8 | #ifdef WIN32 9 | #include 10 | #include 11 | #include 12 | #elif __linux__ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #endif 18 | 19 | struct ClientSocket; 20 | 21 | 22 | using Packet = std::vector; 23 | 24 | class INetworkSystem : public ISystem 25 | { 26 | public: 27 | 28 | enum Type 29 | { 30 | SERVER, 31 | CLIENT 32 | } type; 33 | 34 | enum AccessType 35 | { 36 | WRITE_TYPE, 37 | READ_TYPE 38 | }; 39 | 40 | //SERVER 41 | std::vector < std::shared_ptr > client_sockets; 42 | 43 | //CLIENT 44 | std::string server_address; 45 | int port; 46 | 47 | INetworkSystem(std::string system_name, std::vector> client_sockets = {}) 48 | : ISystem(std::move(system_name)), type(SERVER), client_sockets(std::move(client_sockets)) 49 | {} 50 | 51 | INetworkSystem(std::string system_name, std::string server_address, int port) 52 | : ISystem(std::move(system_name)), type(CLIENT), server_address(server_address), port(port) 53 | {} 54 | 55 | #ifdef WIN32 56 | std::shared_ptr nt = nullptr; 57 | std::mutex buffer_mutex; 58 | std::mutex signal_mutex; 59 | 60 | unsigned socket_ptr = 0; 61 | #elif __linux__ 62 | pthread_t nt; 63 | pthread_mutex_t buffer_mutex; 64 | pthread_mutex_t signal_mutex; 65 | 66 | int sd = 0; 67 | #endif 68 | 69 | bool stop_thread = false; 70 | 71 | bool signal_access(AccessType type, bool value); 72 | 73 | std::vector> gather_packets(); 74 | void send_packets(std::vector> packets); 75 | 76 | virtual void RunMainThread() = 0; 77 | 78 | void Initialize() override; 79 | 80 | void Destroy() override; 81 | }; 82 | 83 | //The Network system, an EXAMPLE system for use as a model. Instantiated: 84 | // as a SERVER: receives client sockets and serves them continuously 85 | // as a CLIENT: connects to a server continuously 86 | //NOTE: this system does not contain a socket from a server, it only receives its 87 | //connections from somewhere else 88 | //THEREFORE: the closing of the server socket must be done BEFORE this system 89 | //calls Destroy() 90 | class NetworkSystem : public INetworkSystem 91 | { 92 | public: 93 | 94 | NetworkSystem(std::vector> client_sockets = {}) 95 | : INetworkSystem("NetworkSystem",client_sockets) 96 | { 97 | } 98 | 99 | NetworkSystem(std::string server_address, int port) 100 | : INetworkSystem("NetworkSystem", server_address, port) 101 | { 102 | std::vector conn_msg; 103 | std::string msg = "connect"; 104 | conn_msg.insert(conn_msg.begin(), msg.begin(), msg.end() + 1); 105 | send_queue.push(conn_msg); 106 | } 107 | 108 | FixedQueue receive_queue; 109 | FixedQueue send_queue; 110 | 111 | void RunMainThread() override; 112 | 113 | void Execute() override; 114 | 115 | std::vector send_queue_access(AccessType type, const Packet* packet); 116 | std::vector receive_queue_access(AccessType type, const Packet* packet); 117 | 118 | void Receive(const NetworkEvent& event); 119 | }; -------------------------------------------------------------------------------- /src/PhyFunctions.cpp: -------------------------------------------------------------------------------- 1 | #define _STDBOOL_H 2 | #define PHYSAC_IMPLEMENTATION 3 | #define PHYSAC_STANDALONE 4 | #define PHYSAC_NO_THREADS 5 | 6 | enum Type 7 | { 8 | CIRCLE, 9 | RECTANGLE, 10 | POLYGON 11 | }; 12 | 13 | #ifdef WIN32 14 | #include "PhyFunctions2.h" 15 | #include "win_physac.h" 16 | 17 | 18 | 19 | PhysicsBody WIN_GetBody(int type, float x, float y, float first, float second, float third) 20 | { 21 | PhysicsBody body; 22 | 23 | switch (type) 24 | { 25 | case CIRCLE: 26 | body = CreatePhysicsBodyCircle(Vector2{ x,y }, first, second); 27 | break; 28 | case RECTANGLE: 29 | body = CreatePhysicsBodyRectangle(Vector2{ x,y }, first, second, third); 30 | break; 31 | default: 32 | case POLYGON: 33 | body = CreatePhysicsBodyPolygon(Vector2{ x,y }, first, second, third); 34 | break; 35 | } 36 | 37 | return body; 38 | } 39 | 40 | void WIN_InitPhysics() 41 | { 42 | InitPhysics(); 43 | } 44 | 45 | void WIN_SetPhysicsGravity(float x, float y) 46 | { 47 | SetPhysicsGravity(x, y); 48 | } 49 | 50 | void WIN_RunPhysicsStep() 51 | { 52 | RunPhysicsStep(); 53 | } 54 | 55 | void WIN_ClosePhysics() 56 | { 57 | ClosePhysics(); 58 | } 59 | #else 60 | #include 61 | #include "PhyFunctions.h" 62 | 63 | PhysicsBody LNX_GetBody(int type, float x, float y, float first, float second, float third){ 64 | PhysicsBody body; 65 | switch (type) 66 | { 67 | case CIRCLE: 68 | body = CreatePhysicsBodyCircle(Vector2{ x,y }, first, second); 69 | break; 70 | case RECTANGLE: 71 | body = CreatePhysicsBodyRectangle(Vector2{ x,y }, first, second, third); 72 | break; 73 | default: 74 | case POLYGON: 75 | body = CreatePhysicsBodyPolygon(Vector2{ x,y }, first, second, third); 76 | break; 77 | } 78 | return body; 79 | } 80 | #endif 81 | #undef _STDBOOL_H 82 | #undef PHYSAC_STANDALONE 83 | #undef PHYSAC_IMPLEMENTATION -------------------------------------------------------------------------------- /src/PhyFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef WIN32 3 | PhysicsBody WIN_GetBody(int type, float x, float y, float first, float second, float third); 4 | void WIN_InitPhysics(); 5 | void WIN_SetPhysicsGravity(float x, float y); 6 | void WIN_RunPhysicsStep(); 7 | void WIN_ClosePhysics(); 8 | #else 9 | PhysicsBody LNX_GetBody(int type, float x, float y, float first, float second, float third); 10 | #endif -------------------------------------------------------------------------------- /src/PhyFunctions2.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WIN32 2 | #include "Windows.h" 3 | #endif -------------------------------------------------------------------------------- /src/PhyFunctions2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef WIN32 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | __declspec(dllimport) int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); 7 | __declspec(dllimport) int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); 8 | #ifdef __cplusplus 9 | } 10 | #endif 11 | #endif -------------------------------------------------------------------------------- /src/PhysicsComponent.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firststef/ECSlib/25997c4bac994ac2559194be7b2162fa53af6346/src/PhysicsComponent.cpp -------------------------------------------------------------------------------- /src/PhysicsComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | #include 4 | 5 | #ifdef WIN32 6 | #include "win_physac.h" 7 | #else 8 | #include 9 | #endif 10 | #include "PhyFunctions.h" 11 | 12 | struct PhysicsComponent : IComponent 13 | { 14 | PhysicsBody body; 15 | Vector2 last_position; 16 | 17 | enum Type 18 | { 19 | CIRCLE, 20 | RECTANGLE, 21 | POLYGON 22 | }type; 23 | 24 | PhysicsComponent(Type type, float x, float y, float first, float second, float third) : type(type) 25 | { 26 | last_position = { x, y }; 27 | #ifdef WIN32 28 | body = WIN_GetBody((int) type, x, y, first, second, third); 29 | #else 30 | body = LNX_GetBody((int) type, x, y, first, second, third); 31 | #endif 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/PhysicsSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "PhysicsSystem.h" 2 | #include "PhysicsComponent.h" 3 | #include "TransformComponent.h" 4 | #include "Helpers.h" 5 | 6 | void PhysicsSystem::Initialize() 7 | { 8 | #ifdef WIN32 9 | WIN_InitPhysics(); 10 | WIN_SetPhysicsGravity(0, 0); 11 | #else 12 | InitPhysics(); 13 | SetPhysicsGravity(0, 0); 14 | #endif 15 | } 16 | 17 | void PhysicsSystem::Execute() 18 | { 19 | #ifdef WIN32 20 | WIN_RunPhysicsStep(); 21 | #else 22 | RunPhysicsStep(); 23 | #endif 24 | 25 | for (auto& e : pool->GetEntities(1 << GetComponentTypeID() | 1 << GetComponentTypeID())) 26 | { 27 | auto& transComp = e->Get(); 28 | auto& phyComp = e->Get(); 29 | 30 | transComp.position.x += phyComp.body->position.x - phyComp.last_position.x; 31 | transComp.position.y += phyComp.body->position.y - phyComp.last_position.y; 32 | 33 | phyComp.last_position = { phyComp.body->position.x , phyComp.body->position.y }; 34 | } 35 | } 36 | 37 | void PhysicsSystem::Destroy() 38 | { 39 | #ifdef WIN32 40 | WIN_ClosePhysics(); 41 | #else 42 | ClosePhysics(); 43 | #endif 44 | } -------------------------------------------------------------------------------- /src/PhysicsSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "System.h" 3 | 4 | class PhysicsSystem : public ISystem 5 | { 6 | public: 7 | PhysicsSystem() : ISystem(std::string("PhysicsSystem")) {}; 8 | 9 | void Initialize() override; 10 | 11 | void Execute() override; 12 | 13 | void Destroy() override; 14 | }; 15 | -------------------------------------------------------------------------------- /src/Pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Entity.h" 3 | 4 | class Pool 5 | { 6 | std::vector entities; 7 | 8 | public: 9 | 10 | EntityPtr AddEntity() 11 | { 12 | EntityPtr e = std::make_shared(); 13 | entities.push_back(e); 14 | 15 | return e; 16 | } 17 | 18 | void RemoveEntity(EntityPtr e) 19 | { 20 | for (auto it = entities.begin(); it != entities.end(); ++it) 21 | { 22 | if (*it == e) 23 | { 24 | entities.erase(it); 25 | break; 26 | } 27 | } 28 | } 29 | 30 | std::vector GetEntities() 31 | { 32 | return std::vector(entities.begin(), entities.end()); 33 | } 34 | 35 | std::vector GetEntities(const ComponentBitset& bitset) 36 | { 37 | std::vector group; 38 | for (auto& ptr : entities) 39 | { 40 | if (ptr->Has(bitset)) 41 | { 42 | group.push_back(ptr); 43 | } 44 | } 45 | 46 | return group; 47 | } 48 | 49 | std::vector GetEntities(bool(*func)(EntityPtr ptr, void* context), void* context) 50 | { 51 | std::vector group; 52 | for (auto& ptr : entities) 53 | { 54 | if (func(ptr, context)) 55 | { 56 | group.push_back(ptr); 57 | } 58 | } 59 | 60 | return group; 61 | } 62 | 63 | EntityPtr GetEntity(const ComponentBitset& bitset) 64 | { 65 | for (auto& ptr : entities) 66 | { 67 | if (ptr->Has(bitset)) 68 | { 69 | return ptr; 70 | } 71 | } 72 | 73 | return nullptr; 74 | } 75 | 76 | EntityPtr GetEntity(bool(*func)(EntityPtr ptr)) 77 | { 78 | for (auto& ptr : entities) 79 | { 80 | if (func(ptr)) 81 | { 82 | return ptr; 83 | } 84 | } 85 | 86 | return nullptr; 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /src/SpriteComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Component.h" 3 | #include 4 | 5 | struct SpriteComponent : IComponent 6 | { 7 | std::string name; 8 | Texture2D texture {0}; 9 | Rectangle sourceRec = { 0,0,0,0 }; 10 | Color color = RED; 11 | 12 | SpriteComponent(std::string name, Texture2D tex, Color color, Rectangle source = {0,0,0,0}) 13 | : name(std::move(name)), texture(tex),sourceRec(source), color(color) 14 | { 15 | if (sourceRec == Rectangle{0,0,0,0}) 16 | { 17 | sourceRec = { 0,0, (float) texture.width, (float) texture.height }; 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/System.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Pool.h" 3 | #include "SystemControlEvent.h" 4 | 5 | class TextureManager; 6 | class EventManager; 7 | class SystemManager; 8 | 9 | class ISystem 10 | { 11 | protected: 12 | Pool* pool = nullptr; 13 | TextureManager* textureManager = nullptr; 14 | SystemManager* systemManager = nullptr; 15 | EventManager* eventManager = nullptr; 16 | public: 17 | bool enabled = true; 18 | std::string name; 19 | 20 | ISystem(std::string name) : name(name) {} 21 | 22 | virtual void SetDependencies(Pool* pool, TextureManager* textureManager, SystemManager* systemManager, EventManager* eventManager) 23 | { 24 | this->pool = pool; 25 | this->textureManager = textureManager; 26 | this->systemManager = systemManager; 27 | this->eventManager = eventManager; 28 | } 29 | 30 | //TODO: initialize might fail, must be changed to bool 31 | virtual void Initialize() {} 32 | virtual void Execute() {} 33 | virtual void Destroy() {} 34 | 35 | void Receive(const SystemControlEvent& event) 36 | { 37 | if (event.systemName == this->name) 38 | { 39 | if (event.controlAction == SystemControlEvent::DISABLE) 40 | { 41 | enabled = false; 42 | } 43 | else if (event.controlAction == SystemControlEvent::ENABLE) 44 | { 45 | enabled = true; 46 | } 47 | } 48 | } 49 | }; 50 | 51 | using SystemPtr = std::shared_ptr; 52 | 53 | class SystemManager 54 | { 55 | Pool* pool = nullptr; 56 | TextureManager* textureManager = nullptr; 57 | EventManager* eventManager = nullptr; 58 | public: 59 | SystemManager(Pool* pool, TextureManager* textureManager, EventManager* eventManager) 60 | :pool(pool), textureManager(textureManager), eventManager(eventManager) 61 | { 62 | } 63 | 64 | std::vector systems; 65 | 66 | void Initialize() 67 | { 68 | for (const auto& ptr : systems) 69 | {//TODO: systems might rather need Initialize(enabled) or Execute(Enabled) to run in "stealth" mode. 70 | ptr->Initialize(); 71 | } 72 | } 73 | 74 | void Execute() 75 | { 76 | for (const auto& ptr : systems) 77 | { 78 | if (ptr->enabled) 79 | ptr->Execute(); 80 | } 81 | } 82 | 83 | void Destroy() 84 | { 85 | for (const auto& ptr : systems) 86 | { 87 | ptr->Destroy(); 88 | } 89 | } 90 | 91 | void AddSystem(const SystemPtr& ptr, bool enabled = true) 92 | { 93 | systems.push_back(std::dynamic_pointer_cast(ptr)); 94 | 95 | ptr->SetDependencies(pool, textureManager, this, eventManager); 96 | ptr->enabled = enabled; 97 | } 98 | 99 | template 100 | T* Get() const 101 | { 102 | for (const auto& ptr : systems) 103 | { 104 | if (std::dynamic_pointer_cast(std::make_shared(*(ptr.get()))) != nullptr) 105 | return std::dynamic_pointer_cast(std::make_shared(*(ptr.get()))).get(); 106 | } 107 | return nullptr; 108 | } 109 | }; -------------------------------------------------------------------------------- /src/SystemControlEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Event.h" 3 | struct SystemControlEvent : IEvent 4 | { 5 | enum ControlAction 6 | { 7 | ENABLE, 8 | DISABLE 9 | } controlAction; 10 | 11 | std::string systemName; 12 | 13 | SystemControlEvent(ControlAction action, std::string name) : controlAction(action), systemName(name) 14 | { 15 | 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/Systems.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "DrawSystem.h" 3 | #include "MouseInputSystem.h" 4 | #include "GridContainerSystem.h" 5 | #include "PhysicsSystem.h" 6 | #include "AnimationSystem.h" 7 | #include "DefferSystem.h" 8 | #include "KeyboardInputSystem.h" 9 | #include "HitBoxSystem.h" 10 | #include "NetworkSystem.h" 11 | -------------------------------------------------------------------------------- /src/TextureManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class TextureManager 6 | { 7 | std::map> textures; 8 | public: 9 | Texture2D Load(std::string path) 10 | { 11 | auto it = textures.find(path); 12 | if (it == textures.end()) { 13 | Texture2D texture = LoadTexture(path.c_str()); 14 | textures.insert(std::pair>(path, std::pair(texture, 1))); 15 | return texture; 16 | } 17 | else 18 | { 19 | it->second.second++; 20 | return it->second.first; 21 | } 22 | } 23 | 24 | void Unload(Texture2D texture) 25 | { 26 | auto it = textures.begin(); 27 | for (; it != textures.end(); ++it) 28 | { 29 | if (it->second.first == texture) 30 | break; 31 | } 32 | 33 | if (it != textures.end()) { 34 | if (it->second.second <= 1) 35 | { 36 | UnloadTexture(it->second.first); 37 | textures.erase(it); 38 | } 39 | else 40 | { 41 | it->second.second--; 42 | } 43 | } 44 | } 45 | 46 | ~TextureManager() 47 | { 48 | for (auto tex : textures) 49 | { 50 | UnloadTexture(tex.second.first); 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/TransformComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Component.h" 4 | 5 | struct TransformComponent : IComponent 6 | { 7 | Rectangle position = {0,0,100,100}; 8 | int zIndex = -1; 9 | float rotation = 0.0f; 10 | 11 | TransformComponent() 12 | { 13 | } 14 | 15 | TransformComponent(Rectangle rect, int zIndex = -1, float rotation = 0.0f) 16 | : position(rect), zIndex(zIndex), rotation(rotation) {} 17 | 18 | }; -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | template > 6 | class FixedQueue : public std::queue { 7 | public: 8 | void push(const T& value) { 9 | if (this->size() == MaxLen) { 10 | this->c.pop_front(); 11 | } 12 | std::queue::push(value); 13 | } 14 | 15 | bool is_full() 16 | { 17 | return MaxLen == std::queue::size(); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/nvidia.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef _WIN32 3 | extern "C" { 4 | _declspec(dllexport) unsigned long int NvOptimusEnablement = 0x00000001; 5 | } 6 | #endif -------------------------------------------------------------------------------- /src/win_physac.h: -------------------------------------------------------------------------------- 1 | /********************************************************************************************** 2 | * 3 | * Physac v1.0 - 2D Physics library for videogames 4 | * 5 | * DESCRIPTION: 6 | * 7 | * Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop 8 | * to simluate physics. A physics step contains the following phases: get collision information, 9 | * apply dynamics, collision solving and position correction. It uses a very simple struct for physic 10 | * bodies with a position vector to be used in any 3D rendering API. 11 | * 12 | * CONFIGURATION: 13 | * 14 | * #define PHYSAC_IMPLEMENTATION 15 | * Generates the implementation of the library into the included file. 16 | * If not defined, the library is in header only mode and can be included in other headers 17 | * or source files without problems. But only ONE file should hold the implementation. 18 | * 19 | * #define PHYSAC_STATIC (defined by default) 20 | * The generated implementation will stay private inside implementation file and all 21 | * internal symbols and functions will only be visible inside that file. 22 | * 23 | * #define PHYSAC_NO_THREADS 24 | * The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). 25 | * It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. 26 | * 27 | * #define PHYSAC_STANDALONE 28 | * Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined 29 | * internally in the library and input management and drawing functions must be provided by 30 | * the user (check library implementation for further details). 31 | * 32 | * #define PHYSAC_DEBUG 33 | * Traces log messages when creating and destroying physics bodies and detects errors in physics 34 | * calculations and reference exceptions; it is useful for debug purposes 35 | * 36 | * #define PHYSAC_MALLOC() 37 | * #define PHYSAC_FREE() 38 | * You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. 39 | * Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. 40 | * 41 | * 42 | * NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. 43 | * NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) 44 | * 45 | * Use the following code to compile: 46 | * gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lpthread -lopengl32 -lgdi32 -lwinmm -std=c99 47 | * 48 | * VERY THANKS TO: 49 | * Ramon Santamaria (github: @raysan5) 50 | * 51 | * 52 | * LICENSE: zlib/libpng 53 | * 54 | * Copyright (c) 2016-2018 Victor Fisac (github: @victorfisac) 55 | * 56 | * This software is provided "as-is", without any express or implied warranty. In no event 57 | * will the authors be held liable for any damages arising from the use of this software. 58 | * 59 | * Permission is granted to anyone to use this software for any purpose, including commercial 60 | * applications, and to alter it and redistribute it freely, subject to the following restrictions: 61 | * 62 | * 1. The origin of this software must not be misrepresented; you must not claim that you 63 | * wrote the original software. If you use this software in a product, an acknowledgment 64 | * in the product documentation would be appreciated but is not required. 65 | * 66 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 67 | * as being the original software. 68 | * 69 | * 3. This notice may not be removed or altered from any source distribution. 70 | * 71 | **********************************************************************************************/ 72 | 73 | #if !defined(PHYSAC_H) 74 | #define PHYSAC_H 75 | 76 | // #define PHYSAC_STATIC 77 | // #define PHYSAC_NO_THREADS 78 | // #define PHYSAC_STANDALONE 79 | // #define PHYSAC_DEBUG 80 | 81 | #if defined(PHYSAC_STATIC) 82 | #define PHYSACDEF static // Functions just visible to module including this file 83 | #else 84 | #if defined(__cplusplus) 85 | #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) 86 | #else 87 | #define PHYSACDEF extern // Functions visible from other files 88 | #endif 89 | #endif 90 | 91 | //---------------------------------------------------------------------------------- 92 | // Defines and Macros 93 | //---------------------------------------------------------------------------------- 94 | #define PHYSAC_MAX_BODIES 64 95 | #define PHYSAC_MAX_MANIFOLDS 4096 96 | #define PHYSAC_MAX_VERTICES 24 97 | #define PHYSAC_CIRCLE_VERTICES 24 98 | 99 | #define PHYSAC_COLLISION_ITERATIONS 100 100 | #define PHYSAC_PENETRATION_ALLOWANCE 0.05f 101 | #define PHYSAC_PENETRATION_CORRECTION 0.4f 102 | 103 | #define PHYSAC_PI 3.14159265358979323846 104 | #define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) 105 | 106 | #define PHYSAC_MALLOC(size) malloc(size) 107 | #define PHYSAC_FREE(ptr) free(ptr) 108 | 109 | //---------------------------------------------------------------------------------- 110 | // Types and Structures Definition 111 | // NOTE: Below types are required for PHYSAC_STANDALONE usage 112 | //---------------------------------------------------------------------------------- 113 | #if defined(PHYSAC_STANDALONE) 114 | // Vector2 type 115 | typedef struct Vector2 { 116 | float x; 117 | float y; 118 | } Vector2; 119 | 120 | // Boolean type 121 | #if !defined(_STDBOOL_H) 122 | typedef enum { false, true } bool; 123 | #define _STDBOOL_H 124 | #endif 125 | #endif 126 | 127 | #if defined(__cplusplus) 128 | #define VECTOR2(...) Vector2{__VA_ARGS__} 129 | #else 130 | #define VECTOR2(...) (Vector2){__VA_ARGS__} 131 | #endif 132 | 133 | typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; 134 | 135 | // Previously defined to be used in PhysicsShape struct as circular dependencies 136 | typedef struct PhysicsBodyData *PhysicsBody; 137 | 138 | // Mat2 type (used for polygon shape rotation matrix) 139 | typedef struct Mat2 { 140 | float m00; 141 | float m01; 142 | float m10; 143 | float m11; 144 | } Mat2; 145 | 146 | #if defined(__cplusplus) 147 | #define MAT2(...) Mat2{__VA_ARGS__} 148 | #else 149 | #define MAT2(...) Mat2{__VA_ARGS__} 150 | #endif 151 | 152 | typedef struct PolygonData { 153 | unsigned int vertexCount; // Current used vertex and normals count 154 | Vector2 positions[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors 155 | Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors 156 | } PolygonData; 157 | 158 | typedef struct PhysicsShape { 159 | PhysicsShapeType type; // Physics shape type (circle or polygon) 160 | PhysicsBody body; // Shape physics body reference 161 | float radius; // Circle shape radius (used for circle shapes) 162 | Mat2 transform; // Vertices transform matrix 2x2 163 | PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) 164 | } PhysicsShape; 165 | 166 | typedef struct PhysicsBodyData { 167 | unsigned int id; // Reference unique identifier 168 | bool enabled; // Enabled dynamics state (collisions are calculated anyway) 169 | Vector2 position; // Physics body shape pivot 170 | Vector2 velocity; // Current linear velocity applied to position 171 | Vector2 force; // Current linear force (reset to 0 every step) 172 | float angularVelocity; // Current angular velocity applied to orient 173 | float torque; // Current angular force (reset to 0 every step) 174 | float orient; // Rotation in radians 175 | float inertia; // Moment of inertia 176 | float inverseInertia; // Inverse value of inertia 177 | float mass; // Physics body mass 178 | float inverseMass; // Inverse value of mass 179 | float staticFriction; // Friction when the body has not movement (0 to 1) 180 | float dynamicFriction; // Friction when the body has movement (0 to 1) 181 | float restitution; // Restitution coefficient of the body (0 to 1) 182 | bool useGravity; // Apply gravity force to dynamics 183 | bool isGrounded; // Physics grounded on other body state 184 | bool freezeOrient; // Physics rotation constraint 185 | PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals) 186 | } PhysicsBodyData; 187 | 188 | typedef struct PhysicsManifoldData { 189 | unsigned int id; // Reference unique identifier 190 | PhysicsBody bodyA; // Manifold first physics body reference 191 | PhysicsBody bodyB; // Manifold second physics body reference 192 | float penetration; // Depth of penetration from collision 193 | Vector2 normal; // Normal direction vector from 'a' to 'b' 194 | Vector2 contacts[2]; // Points of contact during collision 195 | unsigned int contactsCount; // Current collision number of contacts 196 | float restitution; // Mixed restitution during collision 197 | float dynamicFriction; // Mixed dynamic friction during collision 198 | float staticFriction; // Mixed static friction during collision 199 | } PhysicsManifoldData, *PhysicsManifold; 200 | 201 | #if defined(__cplusplus) 202 | extern "C" { // Prevents name mangling of functions 203 | #endif 204 | 205 | //---------------------------------------------------------------------------------- 206 | // Module Functions Declaration 207 | //---------------------------------------------------------------------------------- 208 | PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread 209 | PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop 210 | PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default 211 | PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled 212 | PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force 213 | PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters 214 | PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters 215 | PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters 216 | PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body 217 | PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body 218 | PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force 219 | PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies 220 | PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index 221 | PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) 222 | PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape 223 | PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) 224 | PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter 225 | PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body 226 | PHYSACDEF void ResetPhysics(void); // Destroys created physics bodies and manifolds and resets global values 227 | PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread 228 | 229 | #if defined(__cplusplus) 230 | } 231 | #endif 232 | 233 | #endif // PHYSAC_H 234 | 235 | /*********************************************************************************** 236 | * 237 | * PHYSAC IMPLEMENTATION 238 | * 239 | ************************************************************************************/ 240 | 241 | #if defined(PHYSAC_IMPLEMENTATION) 242 | 243 | #if !defined(PHYSAC_NO_THREADS) 244 | #include // Required for: pthread_t, pthread_create() 245 | #endif 246 | 247 | #if defined(PHYSAC_DEBUG) 248 | #include // Required for: printf() 249 | #endif 250 | 251 | #include // Required for: malloc(), free(), srand(), rand() 252 | #include // Required for: cosf(), sinf(), fabs(), sqrtf() 253 | #include // Required for: uint64_t 254 | 255 | #if !defined(PHYSAC_STANDALONE) 256 | #include "raymath.h" // Required for: Vector2Add(), Vector2Subtract() 257 | #endif 258 | 259 | // Time management functionality 260 | #include // Required for: time(), clock_gettime() 261 | #if defined(_WIN32) 262 | // Functions required to query time on Windows 263 | int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); 264 | int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); 265 | #elif defined(__linux__) 266 | #if _POSIX_C_SOURCE < 199309L 267 | #undef _POSIX_C_SOURCE 268 | #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. 269 | #endif 270 | #include // Required for: timespec 271 | #elif defined(__APPLE__) // macOS also defines __MACH__ 272 | #include // Required for: mach_absolute_time() 273 | #endif 274 | 275 | //---------------------------------------------------------------------------------- 276 | // Defines and Macros 277 | //---------------------------------------------------------------------------------- 278 | #define min(a,b) (((a)<(b))?(a):(b)) 279 | #define max(a,b) (((a)>(b))?(a):(b)) 280 | #define PHYSAC_FLT_MAX 3.402823466e+38f 281 | #define PHYSAC_EPSILON 0.000001f 282 | #define PHYSAC_K 1.0f/3.0f 283 | #define PHYSAC_VECTOR_ZERO VECTOR2( 0.0f, 0.0f ) 284 | 285 | //---------------------------------------------------------------------------------- 286 | // Global Variables Definition 287 | //---------------------------------------------------------------------------------- 288 | #if !defined(PHYSAC_NO_THREADS) 289 | static pthread_t physicsThreadId; // Physics thread id 290 | #endif 291 | static unsigned int usedMemory = 0; // Total allocated dynamic memory 292 | static bool physicsThreadEnabled = false; // Physics thread enabled state 293 | static double baseTime = 0.0; // Offset time for MONOTONIC clock 294 | static double startTime = 0.0; // Start time in milliseconds 295 | static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds 296 | static double currentTime = 0.0; // Current time in milliseconds 297 | static uint64_t frequency = 0; // Hi-res clock frequency 298 | 299 | static double accumulator = 0.0; // Physics time step delta time accumulator 300 | static unsigned int stepsCount = 0; // Total physics steps processed 301 | static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force 302 | static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array 303 | static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter 304 | static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array 305 | static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter 306 | 307 | //---------------------------------------------------------------------------------- 308 | // Module Internal Functions Declaration 309 | //---------------------------------------------------------------------------------- 310 | static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization 311 | static PolygonData CreateRandomPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot 312 | static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions 313 | static void *PhysicsLoop(void *arg); // Physics loop thread function 314 | static void PhysicsStep(void); // Physics steps calculations (dynamics, collisions and position corrections) 315 | static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization 316 | static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision 317 | static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold 318 | static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies 319 | static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies 320 | static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies 321 | static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies 322 | static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies 323 | static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity 324 | static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions 325 | static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions 326 | static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces 327 | static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information 328 | static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration 329 | static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face 330 | static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB); // Calculates clipping based on a normal and two faces 331 | static bool BiasGreaterThan(float valueA, float valueB); // Check if values are between bias range 332 | static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points 333 | 334 | static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer 335 | static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds 336 | static double GetCurrentTime(void); // Get current time measure in milliseconds 337 | 338 | // Math functions 339 | static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value 340 | static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors 341 | static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector 342 | static float MathDot(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors 343 | static inline float DistSqr(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors 344 | static void MathNormalize(Vector2 *vector); // Returns the normalized values of a vector 345 | #if defined(PHYSAC_STANDALONE) 346 | static Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors 347 | static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors 348 | #endif 349 | 350 | static Mat2 Mat2Radians(float radians); // Creates a matrix 2x2 from a given radians value 351 | static void Mat2Set(Mat2 *matrix, float radians); // Set values from radians to a created matrix 2x2 352 | static inline Mat2 Mat2Transpose(Mat2 matrix); // Returns the transpose of a given matrix 2x2 353 | static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector); // Multiplies a vector by a matrix 2x2 354 | 355 | //---------------------------------------------------------------------------------- 356 | // Module Functions Definition 357 | //---------------------------------------------------------------------------------- 358 | // Initializes physics values, pointers and creates physics loop thread 359 | PHYSACDEF void InitPhysics(void) 360 | { 361 | #if !defined(PHYSAC_NO_THREADS) 362 | // NOTE: if defined, user will need to create a thread for PhysicsThread function manually 363 | // Create physics thread using POSIXS thread libraries 364 | pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL); 365 | #endif 366 | 367 | // Initialize high resolution timer 368 | InitTimer(); 369 | 370 | #if defined(PHYSAC_DEBUG) 371 | printf("[PHYSAC] physics module initialized successfully\n"); 372 | #endif 373 | 374 | accumulator = 0.0; 375 | } 376 | 377 | // Returns true if physics thread is currently enabled 378 | PHYSACDEF bool IsPhysicsEnabled(void) 379 | { 380 | return physicsThreadEnabled; 381 | } 382 | 383 | // Sets physics global gravity force 384 | PHYSACDEF void SetPhysicsGravity(float x, float y) 385 | { 386 | gravityForce.x = x; 387 | gravityForce.y = y; 388 | } 389 | 390 | // Creates a new circle physics body with generic parameters 391 | PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) 392 | { 393 | PhysicsBody newBody = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_CIRCLE_VERTICES, density); 394 | return newBody; 395 | } 396 | 397 | // Creates a new rectangle physics body with generic parameters 398 | PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) 399 | { 400 | PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); 401 | usedMemory += sizeof(PhysicsBodyData); 402 | 403 | int newId = FindAvailableBodyIndex(); 404 | if (newId != -1) 405 | { 406 | // Initialize new body with generic values 407 | newBody->id = newId; 408 | newBody->enabled = true; 409 | newBody->position = pos; 410 | newBody->velocity = VECTOR2( 0.0f ); 411 | newBody->force = VECTOR2( 0.0f ); 412 | newBody->angularVelocity = 0.0f; 413 | newBody->torque = 0.0f; 414 | newBody->orient = 0.0f; 415 | newBody->shape.type = PHYSICS_POLYGON; 416 | newBody->shape.body = newBody; 417 | newBody->shape.radius = 0.0f; 418 | newBody->shape.transform = Mat2Radians(0.0f); 419 | newBody->shape.vertexData = CreateRectanglePolygon(pos, VECTOR2( width, height )); 420 | 421 | // Calculate centroid and moment of inertia 422 | Vector2 center = { 0.0f, 0.0f }; 423 | float area = 0.0f; 424 | float inertia = 0.0f; 425 | 426 | for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) 427 | { 428 | // Triangle vertices, third vertex implied as (0, 0) 429 | Vector2 p1 = newBody->shape.vertexData.positions[i]; 430 | int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); 431 | Vector2 p2 = newBody->shape.vertexData.positions[nextIndex]; 432 | 433 | float D = MathCrossVector2(p1, p2); 434 | float triangleArea = D/2; 435 | 436 | area += triangleArea; 437 | 438 | // Use area to weight the centroid average, not just vertex position 439 | center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); 440 | center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); 441 | 442 | float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; 443 | float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; 444 | inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); 445 | } 446 | 447 | center.x *= 1.0f/area; 448 | center.y *= 1.0f/area; 449 | 450 | // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) 451 | // Note: this is not really necessary 452 | for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) 453 | { 454 | newBody->shape.vertexData.positions[i].x -= center.x; 455 | newBody->shape.vertexData.positions[i].y -= center.y; 456 | } 457 | 458 | newBody->mass = density*area; 459 | newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); 460 | newBody->inertia = density*inertia; 461 | newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); 462 | newBody->staticFriction = 0.4f; 463 | newBody->dynamicFriction = 0.2f; 464 | newBody->restitution = 0.0f; 465 | newBody->useGravity = true; 466 | newBody->isGrounded = false; 467 | newBody->freezeOrient = false; 468 | 469 | // Add new body to bodies pointers array and update bodies count 470 | bodies[physicsBodiesCount] = newBody; 471 | physicsBodiesCount++; 472 | 473 | #if defined(PHYSAC_DEBUG) 474 | printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); 475 | #endif 476 | } 477 | #if defined(PHYSAC_DEBUG) 478 | else printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); 479 | #endif 480 | 481 | return newBody; 482 | } 483 | 484 | // Creates a new polygon physics body with generic parameters 485 | PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) 486 | { 487 | PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); 488 | usedMemory += sizeof(PhysicsBodyData); 489 | 490 | int newId = FindAvailableBodyIndex(); 491 | if (newId != -1) 492 | { 493 | // Initialize new body with generic values 494 | newBody->id = newId; 495 | newBody->enabled = true; 496 | newBody->position = pos; 497 | newBody->velocity = PHYSAC_VECTOR_ZERO; 498 | newBody->force = PHYSAC_VECTOR_ZERO; 499 | newBody->angularVelocity = 0.0f; 500 | newBody->torque = 0.0f; 501 | newBody->orient = 0.0f; 502 | newBody->shape.type = PHYSICS_POLYGON; 503 | newBody->shape.body = newBody; 504 | newBody->shape.transform = Mat2Radians(0.0f); 505 | newBody->shape.vertexData = CreateRandomPolygon(radius, sides); 506 | 507 | // Calculate centroid and moment of inertia 508 | Vector2 center = { 0.0f, 0.0f }; 509 | float area = 0.0f; 510 | float inertia = 0.0f; 511 | 512 | for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) 513 | { 514 | // Triangle vertices, third vertex implied as (0, 0) 515 | Vector2 position1 = newBody->shape.vertexData.positions[i]; 516 | int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); 517 | Vector2 position2 = newBody->shape.vertexData.positions[nextIndex]; 518 | 519 | float cross = MathCrossVector2(position1, position2); 520 | float triangleArea = cross/2; 521 | 522 | area += triangleArea; 523 | 524 | // Use area to weight the centroid average, not just vertex position 525 | center.x += triangleArea*PHYSAC_K*(position1.x + position2.x); 526 | center.y += triangleArea*PHYSAC_K*(position1.y + position2.y); 527 | 528 | float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x; 529 | float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y; 530 | inertia += (0.25f*PHYSAC_K*cross)*(intx2 + inty2); 531 | } 532 | 533 | center.x *= 1.0f/area; 534 | center.y *= 1.0f/area; 535 | 536 | // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) 537 | // Note: this is not really necessary 538 | for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) 539 | { 540 | newBody->shape.vertexData.positions[i].x -= center.x; 541 | newBody->shape.vertexData.positions[i].y -= center.y; 542 | } 543 | 544 | newBody->mass = density*area; 545 | newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); 546 | newBody->inertia = density*inertia; 547 | newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); 548 | newBody->staticFriction = 0.4f; 549 | newBody->dynamicFriction = 0.2f; 550 | newBody->restitution = 0.0f; 551 | newBody->useGravity = true; 552 | newBody->isGrounded = false; 553 | newBody->freezeOrient = false; 554 | 555 | // Add new body to bodies pointers array and update bodies count 556 | bodies[physicsBodiesCount] = newBody; 557 | physicsBodiesCount++; 558 | 559 | #if defined(PHYSAC_DEBUG) 560 | printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); 561 | #endif 562 | } 563 | #if defined(PHYSAC_DEBUG) 564 | else printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); 565 | #endif 566 | 567 | return newBody; 568 | } 569 | 570 | // Adds a force to a physics body 571 | PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) 572 | { 573 | if (body != NULL) body->force = Vector2Add(body->force, force); 574 | } 575 | 576 | // Adds an angular force to a physics body 577 | PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount) 578 | { 579 | if (body != NULL) body->torque += amount; 580 | } 581 | 582 | // Shatters a polygon shape physics body to little physics bodies with explosion force 583 | PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) 584 | { 585 | if (body != NULL) 586 | { 587 | if (body->shape.type == PHYSICS_POLYGON) 588 | { 589 | PolygonData vertexData = body->shape.vertexData; 590 | bool collision = false; 591 | 592 | for (int i = 0; i < vertexData.vertexCount; i++) 593 | { 594 | Vector2 positionA = body->position; 595 | Vector2 positionB = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[i])); 596 | int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); 597 | Vector2 positionC = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[nextIndex])); 598 | 599 | // Check collision between each triangle 600 | float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/ 601 | ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); 602 | 603 | float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/ 604 | ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); 605 | 606 | float gamma = 1.0f - alpha - beta; 607 | 608 | if ((alpha > 0.0f) && (beta > 0.0f) & (gamma > 0.0f)) 609 | { 610 | collision = true; 611 | break; 612 | } 613 | } 614 | 615 | if (collision) 616 | { 617 | int count = vertexData.vertexCount; 618 | Vector2 bodyPos = body->position; 619 | Vector2 *vertices = (Vector2*)malloc(sizeof(Vector2) * count); 620 | Mat2 trans = body->shape.transform; 621 | for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i]; 622 | 623 | // Destroy shattered physics body 624 | DestroyPhysicsBody(body); 625 | 626 | for (int i = 0; i < count; i++) 627 | { 628 | int nextIndex = (((i + 1) < count) ? (i + 1) : 0); 629 | Vector2 center = TriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); 630 | center = Vector2Add(bodyPos, center); 631 | Vector2 offset = Vector2Subtract(center, bodyPos); 632 | 633 | PhysicsBody newBody = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values 634 | 635 | PolygonData newData = { 0 }; 636 | newData.vertexCount = 3; 637 | 638 | newData.positions[0] = Vector2Subtract(vertices[i], offset); 639 | newData.positions[1] = Vector2Subtract(vertices[nextIndex], offset); 640 | newData.positions[2] = Vector2Subtract(position, center); 641 | 642 | // Separate vertices to avoid unnecessary physics collisions 643 | newData.positions[0].x *= 0.95f; 644 | newData.positions[0].y *= 0.95f; 645 | newData.positions[1].x *= 0.95f; 646 | newData.positions[1].y *= 0.95f; 647 | newData.positions[2].x *= 0.95f; 648 | newData.positions[2].y *= 0.95f; 649 | 650 | // Calculate polygon faces normals 651 | for (int j = 0; j < newData.vertexCount; j++) 652 | { 653 | int nextVertex = (((j + 1) < newData.vertexCount) ? (j + 1) : 0); 654 | Vector2 face = Vector2Subtract(newData.positions[nextVertex], newData.positions[j]); 655 | 656 | newData.normals[j] = VECTOR2( face.y, -face.x ); 657 | MathNormalize(&newData.normals[j]); 658 | } 659 | 660 | // Apply computed vertex data to new physics body shape 661 | newBody->shape.vertexData = newData; 662 | newBody->shape.transform = trans; 663 | 664 | // Calculate centroid and moment of inertia 665 | center = PHYSAC_VECTOR_ZERO; 666 | float area = 0.0f; 667 | float inertia = 0.0f; 668 | 669 | for (int j = 0; j < newBody->shape.vertexData.vertexCount; j++) 670 | { 671 | // Triangle vertices, third vertex implied as (0, 0) 672 | Vector2 p1 = newBody->shape.vertexData.positions[j]; 673 | int nextVertex = (((j + 1) < newBody->shape.vertexData.vertexCount) ? (j + 1) : 0); 674 | Vector2 p2 = newBody->shape.vertexData.positions[nextVertex]; 675 | 676 | float D = MathCrossVector2(p1, p2); 677 | float triangleArea = D/2; 678 | 679 | area += triangleArea; 680 | 681 | // Use area to weight the centroid average, not just vertex position 682 | center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); 683 | center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); 684 | 685 | float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; 686 | float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; 687 | inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); 688 | } 689 | 690 | center.x *= 1.0f/area; 691 | center.y *= 1.0f/area; 692 | 693 | newBody->mass = area; 694 | newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); 695 | newBody->inertia = inertia; 696 | newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); 697 | 698 | // Calculate explosion force direction 699 | Vector2 pointA = newBody->position; 700 | Vector2 pointB = Vector2Subtract(newData.positions[1], newData.positions[0]); 701 | pointB.x /= 2.0f; 702 | pointB.y /= 2.0f; 703 | Vector2 forceDirection = Vector2Subtract(Vector2Add(pointA, Vector2Add(newData.positions[0], pointB)), newBody->position); 704 | MathNormalize(&forceDirection); 705 | forceDirection.x *= force; 706 | forceDirection.y *= force; 707 | 708 | // Apply force to new physics body 709 | PhysicsAddForce(newBody, forceDirection); 710 | } 711 | 712 | free(vertices); 713 | } 714 | } 715 | } 716 | #if defined(PHYSAC_DEBUG) 717 | else printf("[PHYSAC] error when trying to shatter a null reference physics body"); 718 | #endif 719 | } 720 | 721 | // Returns the current amount of created physics bodies 722 | PHYSACDEF int GetPhysicsBodiesCount(void) 723 | { 724 | return physicsBodiesCount; 725 | } 726 | 727 | // Returns a physics body of the bodies pool at a specific index 728 | PHYSACDEF PhysicsBody GetPhysicsBody(int index) 729 | { 730 | PhysicsBody body = NULL; 731 | 732 | if (index < physicsBodiesCount) 733 | { 734 | body = bodies[index]; 735 | 736 | if (body == NULL) 737 | { 738 | #if defined(PHYSAC_DEBUG) 739 | printf("[PHYSAC] error when trying to get a null reference physics body"); 740 | #endif 741 | } 742 | } 743 | #if defined(PHYSAC_DEBUG) 744 | else printf("[PHYSAC] physics body index is out of bounds"); 745 | #endif 746 | 747 | return body; 748 | } 749 | 750 | // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) 751 | PHYSACDEF int GetPhysicsShapeType(int index) 752 | { 753 | int result = -1; 754 | 755 | if (index < physicsBodiesCount) 756 | { 757 | PhysicsBody body = bodies[index]; 758 | 759 | if (body != NULL) result = body->shape.type; 760 | #if defined(PHYSAC_DEBUG) 761 | else printf("[PHYSAC] error when trying to get a null reference physics body"); 762 | #endif 763 | } 764 | #if defined(PHYSAC_DEBUG) 765 | else printf("[PHYSAC] physics body index is out of bounds"); 766 | #endif 767 | 768 | return result; 769 | } 770 | 771 | // Returns the amount of vertices of a physics body shape 772 | PHYSACDEF int GetPhysicsShapeVerticesCount(int index) 773 | { 774 | int result = 0; 775 | 776 | if (index < physicsBodiesCount) 777 | { 778 | PhysicsBody body = bodies[index]; 779 | 780 | if (body != NULL) 781 | { 782 | switch (body->shape.type) 783 | { 784 | case PHYSICS_CIRCLE: result = PHYSAC_CIRCLE_VERTICES; break; 785 | case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break; 786 | default: break; 787 | } 788 | } 789 | #if defined(PHYSAC_DEBUG) 790 | else printf("[PHYSAC] error when trying to get a null reference physics body"); 791 | #endif 792 | } 793 | #if defined(PHYSAC_DEBUG) 794 | else printf("[PHYSAC] physics body index is out of bounds"); 795 | #endif 796 | 797 | return result; 798 | } 799 | 800 | // Returns transformed position of a body shape (body position + vertex transformed position) 801 | PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) 802 | { 803 | Vector2 position = { 0.0f, 0.0f }; 804 | 805 | if (body != NULL) 806 | { 807 | switch (body->shape.type) 808 | { 809 | case PHYSICS_CIRCLE: 810 | { 811 | position.x = body->position.x + cosf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; 812 | position.y = body->position.y + sinf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; 813 | } break; 814 | case PHYSICS_POLYGON: 815 | { 816 | PolygonData vertexData = body->shape.vertexData; 817 | position = Vector2Add(body->position, Mat2MultiplyVector2(body->shape.transform, vertexData.positions[vertex])); 818 | } break; 819 | default: break; 820 | } 821 | } 822 | #if defined(PHYSAC_DEBUG) 823 | else printf("[PHYSAC] error when trying to get a null reference physics body"); 824 | #endif 825 | 826 | return position; 827 | } 828 | 829 | // Sets physics body shape transform based on radians parameter 830 | PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) 831 | { 832 | if (body != NULL) 833 | { 834 | body->orient = radians; 835 | 836 | if (body->shape.type == PHYSICS_POLYGON) body->shape.transform = Mat2Radians(radians); 837 | } 838 | } 839 | 840 | // Unitializes and destroys a physics body 841 | PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) 842 | { 843 | if (body != NULL) 844 | { 845 | int id = body->id; 846 | int index = -1; 847 | 848 | for (int i = 0; i < physicsBodiesCount; i++) 849 | { 850 | if (bodies[i]->id == id) 851 | { 852 | index = i; 853 | break; 854 | } 855 | } 856 | 857 | #if defined(PHYSAC_DEBUG) 858 | if (index == -1) printf("[PHYSAC] cannot find body id %i in pointers array\n", id); 859 | #endif 860 | 861 | // Free body allocated memory 862 | PHYSAC_FREE(body); 863 | usedMemory -= sizeof(PhysicsBodyData); 864 | bodies[index] = NULL; 865 | 866 | // Reorder physics bodies pointers array and its catched index 867 | for (int i = index; i < physicsBodiesCount; i++) 868 | { 869 | if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1]; 870 | } 871 | 872 | // Update physics bodies count 873 | physicsBodiesCount--; 874 | 875 | #if defined(PHYSAC_DEBUG) 876 | printf("[PHYSAC] destroyed physics body id %i\n", id); 877 | #endif 878 | } 879 | #if defined(PHYSAC_DEBUG) 880 | else printf("[PHYSAC] error trying to destroy a null referenced body\n"); 881 | #endif 882 | } 883 | 884 | // Destroys created physics bodies and manifolds and resets global values 885 | PHYSACDEF void ResetPhysics(void) 886 | { 887 | // Unitialize physics bodies dynamic memory allocations 888 | for (int i = physicsBodiesCount - 1; i >= 0; i--) 889 | { 890 | PhysicsBody body = bodies[i]; 891 | 892 | if (body != NULL) 893 | { 894 | PHYSAC_FREE(body); 895 | bodies[i] = NULL; 896 | usedMemory -= sizeof(PhysicsBodyData); 897 | } 898 | } 899 | 900 | physicsBodiesCount = 0; 901 | 902 | // Unitialize physics manifolds dynamic memory allocations 903 | for (int i = physicsManifoldsCount - 1; i >= 0; i--) 904 | { 905 | PhysicsManifold manifold = contacts[i]; 906 | 907 | if (manifold != NULL) 908 | { 909 | PHYSAC_FREE(manifold); 910 | contacts[i] = NULL; 911 | usedMemory -= sizeof(PhysicsManifoldData); 912 | } 913 | } 914 | 915 | physicsManifoldsCount = 0; 916 | 917 | #if defined(PHYSAC_DEBUG) 918 | printf("[PHYSAC] physics module reset successfully\n"); 919 | #endif 920 | } 921 | 922 | // Unitializes physics pointers and exits physics loop thread 923 | PHYSACDEF void ClosePhysics(void) 924 | { 925 | // Exit physics loop thread 926 | physicsThreadEnabled = false; 927 | 928 | #if !defined(PHYSAC_NO_THREADS) 929 | pthread_join(physicsThreadId, NULL); 930 | #endif 931 | 932 | // Unitialize physics manifolds dynamic memory allocations 933 | for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); 934 | 935 | // Unitialize physics bodies dynamic memory allocations 936 | for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); 937 | 938 | #if defined(PHYSAC_DEBUG) 939 | if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); 940 | else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); 941 | else printf("[PHYSAC] physics module closed successfully\n"); 942 | #endif 943 | } 944 | 945 | //---------------------------------------------------------------------------------- 946 | // Module Internal Functions Definition 947 | //---------------------------------------------------------------------------------- 948 | // Finds a valid index for a new physics body initialization 949 | static int FindAvailableBodyIndex() 950 | { 951 | int index = -1; 952 | for (int i = 0; i < PHYSAC_MAX_BODIES; i++) 953 | { 954 | int currentId = i; 955 | 956 | // Check if current id already exist in other physics body 957 | for (int k = 0; k < physicsBodiesCount; k++) 958 | { 959 | if (bodies[k]->id == currentId) 960 | { 961 | currentId++; 962 | break; 963 | } 964 | } 965 | 966 | // If it is not used, use it as new physics body id 967 | if (currentId == i) 968 | { 969 | index = i; 970 | break; 971 | } 972 | } 973 | 974 | return index; 975 | } 976 | 977 | // Creates a random polygon shape with max vertex distance from polygon pivot 978 | static PolygonData CreateRandomPolygon(float radius, int sides) 979 | { 980 | PolygonData data = { 0 }; 981 | data.vertexCount = sides; 982 | 983 | // Calculate polygon vertices positions 984 | for (int i = 0; i < data.vertexCount; i++) 985 | { 986 | data.positions[i].x = cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; 987 | data.positions[i].y = sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; 988 | } 989 | 990 | // Calculate polygon faces normals 991 | for (int i = 0; i < data.vertexCount; i++) 992 | { 993 | int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); 994 | Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); 995 | 996 | data.normals[i] = VECTOR2( face.y, -face.x ); 997 | MathNormalize(&data.normals[i]); 998 | } 999 | 1000 | return data; 1001 | } 1002 | 1003 | // Creates a rectangle polygon shape based on a min and max positions 1004 | static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size) 1005 | { 1006 | PolygonData data = { 0 }; 1007 | data.vertexCount = 4; 1008 | 1009 | // Calculate polygon vertices positions 1010 | data.positions[0] = VECTOR2( pos.x + size.x/2, pos.y - size.y/2 ); 1011 | data.positions[1] = VECTOR2( pos.x + size.x/2, pos.y + size.y/2 ); 1012 | data.positions[2] = VECTOR2( pos.x - size.x/2, pos.y + size.y/2 ); 1013 | data.positions[3] = VECTOR2( pos.x - size.x/2, pos.y - size.y/2 ); 1014 | 1015 | // Calculate polygon faces normals 1016 | for (int i = 0; i < data.vertexCount; i++) 1017 | { 1018 | int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); 1019 | Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); 1020 | 1021 | data.normals[i] = VECTOR2( face.y, -face.x ); 1022 | MathNormalize(&data.normals[i]); 1023 | } 1024 | 1025 | return data; 1026 | } 1027 | 1028 | // Physics loop thread function 1029 | static void *PhysicsLoop(void *arg) 1030 | { 1031 | #if defined(PHYSAC_DEBUG) 1032 | printf("[PHYSAC] physics thread created successfully\n"); 1033 | #endif 1034 | 1035 | // Initialize physics loop thread values 1036 | physicsThreadEnabled = true; 1037 | 1038 | // Physics update loop 1039 | while (physicsThreadEnabled) 1040 | { 1041 | RunPhysicsStep(); 1042 | } 1043 | 1044 | return NULL; 1045 | } 1046 | 1047 | // Physics steps calculations (dynamics, collisions and position corrections) 1048 | static void PhysicsStep(void) 1049 | { 1050 | // Update current steps count 1051 | stepsCount++; 1052 | 1053 | // Clear previous generated collisions information 1054 | for (int i = physicsManifoldsCount - 1; i >= 0; i--) 1055 | { 1056 | PhysicsManifold manifold = contacts[i]; 1057 | if (manifold != NULL) DestroyPhysicsManifold(manifold); 1058 | } 1059 | 1060 | // Reset physics bodies grounded state 1061 | for (int i = 0; i < physicsBodiesCount; i++) 1062 | { 1063 | PhysicsBody body = bodies[i]; 1064 | body->isGrounded = false; 1065 | } 1066 | 1067 | // Generate new collision information 1068 | for (int i = 0; i < physicsBodiesCount; i++) 1069 | { 1070 | PhysicsBody bodyA = bodies[i]; 1071 | 1072 | if (bodyA != NULL) 1073 | { 1074 | for (int j = i + 1; j < physicsBodiesCount; j++) 1075 | { 1076 | PhysicsBody bodyB = bodies[j]; 1077 | 1078 | if (bodyB != NULL) 1079 | { 1080 | if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) continue; 1081 | 1082 | PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); 1083 | SolvePhysicsManifold(manifold); 1084 | 1085 | if (manifold->contactsCount > 0) 1086 | { 1087 | // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot 1088 | PhysicsManifold newManifold = CreatePhysicsManifold(bodyA, bodyB); 1089 | newManifold->penetration = manifold->penetration; 1090 | newManifold->normal = manifold->normal; 1091 | newManifold->contacts[0] = manifold->contacts[0]; 1092 | newManifold->contacts[1] = manifold->contacts[1]; 1093 | newManifold->contactsCount = manifold->contactsCount; 1094 | newManifold->restitution = manifold->restitution; 1095 | newManifold->dynamicFriction = manifold->dynamicFriction; 1096 | newManifold->staticFriction = manifold->staticFriction; 1097 | } 1098 | } 1099 | } 1100 | } 1101 | } 1102 | 1103 | // Integrate forces to physics bodies 1104 | for (int i = 0; i < physicsBodiesCount; i++) 1105 | { 1106 | PhysicsBody body = bodies[i]; 1107 | if (body != NULL) IntegratePhysicsForces(body); 1108 | } 1109 | 1110 | // Initialize physics manifolds to solve collisions 1111 | for (int i = 0; i < physicsManifoldsCount; i++) 1112 | { 1113 | PhysicsManifold manifold = contacts[i]; 1114 | if (manifold != NULL) InitializePhysicsManifolds(manifold); 1115 | } 1116 | 1117 | // Integrate physics collisions impulses to solve collisions 1118 | for (int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++) 1119 | { 1120 | for (int j = 0; j < physicsManifoldsCount; j++) 1121 | { 1122 | PhysicsManifold manifold = contacts[i]; 1123 | if (manifold != NULL) IntegratePhysicsImpulses(manifold); 1124 | } 1125 | } 1126 | 1127 | // Integrate velocity to physics bodies 1128 | for (int i = 0; i < physicsBodiesCount; i++) 1129 | { 1130 | PhysicsBody body = bodies[i]; 1131 | if (body != NULL) IntegratePhysicsVelocity(body); 1132 | } 1133 | 1134 | // Correct physics bodies positions based on manifolds collision information 1135 | for (int i = 0; i < physicsManifoldsCount; i++) 1136 | { 1137 | PhysicsManifold manifold = contacts[i]; 1138 | if (manifold != NULL) CorrectPhysicsPositions(manifold); 1139 | } 1140 | 1141 | // Clear physics bodies forces 1142 | for (int i = 0; i < physicsBodiesCount; i++) 1143 | { 1144 | PhysicsBody body = bodies[i]; 1145 | if (body != NULL) 1146 | { 1147 | body->force = PHYSAC_VECTOR_ZERO; 1148 | body->torque = 0.0f; 1149 | } 1150 | } 1151 | } 1152 | 1153 | // Wrapper to ensure PhysicsStep is run with at a fixed time step 1154 | PHYSACDEF void RunPhysicsStep(void) 1155 | { 1156 | // Calculate current time 1157 | currentTime = GetCurrentTime(); 1158 | 1159 | // Calculate current delta time 1160 | const double delta = currentTime - startTime; 1161 | 1162 | // Store the time elapsed since the last frame began 1163 | accumulator += delta; 1164 | 1165 | // Fixed time stepping loop 1166 | while (accumulator >= deltaTime) 1167 | { 1168 | #ifdef PHYSAC_DEBUG 1169 | //printf("currentTime %f, startTime %f, accumulator-pre %f, accumulator-post %f, delta %f, deltaTime %f\n", 1170 | // currentTime, startTime, accumulator, accumulator-deltaTime, delta, deltaTime); 1171 | #endif 1172 | PhysicsStep(); 1173 | accumulator -= deltaTime; 1174 | } 1175 | 1176 | // Record the starting of this frame 1177 | startTime = currentTime; 1178 | } 1179 | 1180 | PHYSACDEF void SetPhysicsTimeStep(double delta) 1181 | { 1182 | deltaTime = delta; 1183 | } 1184 | 1185 | // Finds a valid index for a new manifold initialization 1186 | static int FindAvailableManifoldIndex() 1187 | { 1188 | int index = -1; 1189 | for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++) 1190 | { 1191 | int currentId = i; 1192 | 1193 | // Check if current id already exist in other physics body 1194 | for (int k = 0; k < physicsManifoldsCount; k++) 1195 | { 1196 | if (contacts[k]->id == currentId) 1197 | { 1198 | currentId++; 1199 | break; 1200 | } 1201 | } 1202 | 1203 | // If it is not used, use it as new physics body id 1204 | if (currentId == i) 1205 | { 1206 | index = i; 1207 | break; 1208 | } 1209 | } 1210 | 1211 | return index; 1212 | } 1213 | 1214 | // Creates a new physics manifold to solve collision 1215 | static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b) 1216 | { 1217 | PhysicsManifold newManifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); 1218 | usedMemory += sizeof(PhysicsManifoldData); 1219 | 1220 | int newId = FindAvailableManifoldIndex(); 1221 | if (newId != -1) 1222 | { 1223 | // Initialize new manifold with generic values 1224 | newManifold->id = newId; 1225 | newManifold->bodyA = a; 1226 | newManifold->bodyB = b; 1227 | newManifold->penetration = 0; 1228 | newManifold->normal = PHYSAC_VECTOR_ZERO; 1229 | newManifold->contacts[0] = PHYSAC_VECTOR_ZERO; 1230 | newManifold->contacts[1] = PHYSAC_VECTOR_ZERO; 1231 | newManifold->contactsCount = 0; 1232 | newManifold->restitution = 0.0f; 1233 | newManifold->dynamicFriction = 0.0f; 1234 | newManifold->staticFriction = 0.0f; 1235 | 1236 | // Add new body to bodies pointers array and update bodies count 1237 | contacts[physicsManifoldsCount] = newManifold; 1238 | physicsManifoldsCount++; 1239 | } 1240 | #if defined(PHYSAC_DEBUG) 1241 | else printf("[PHYSAC] new physics manifold creation failed because there is any available id to use\n"); 1242 | #endif 1243 | 1244 | return newManifold; 1245 | } 1246 | 1247 | // Unitializes and destroys a physics manifold 1248 | static void DestroyPhysicsManifold(PhysicsManifold manifold) 1249 | { 1250 | if (manifold != NULL) 1251 | { 1252 | int id = manifold->id; 1253 | int index = -1; 1254 | 1255 | for (int i = 0; i < physicsManifoldsCount; i++) 1256 | { 1257 | if (contacts[i]->id == id) 1258 | { 1259 | index = i; 1260 | break; 1261 | } 1262 | } 1263 | 1264 | #if defined(PHYSAC_DEBUG) 1265 | if (index == -1) printf("[PHYSAC] cannot find manifold id %i in pointers array\n", id); 1266 | #endif 1267 | 1268 | // Free manifold allocated memory 1269 | PHYSAC_FREE(manifold); 1270 | usedMemory -= sizeof(PhysicsManifoldData); 1271 | contacts[index] = NULL; 1272 | 1273 | // Reorder physics manifolds pointers array and its catched index 1274 | for (int i = index; i < physicsManifoldsCount; i++) 1275 | { 1276 | if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1]; 1277 | } 1278 | 1279 | // Update physics manifolds count 1280 | physicsManifoldsCount--; 1281 | } 1282 | #if defined(PHYSAC_DEBUG) 1283 | else printf("[PHYSAC] error trying to destroy a null referenced manifold\n"); 1284 | #endif 1285 | } 1286 | 1287 | // Solves a created physics manifold between two physics bodies 1288 | static void SolvePhysicsManifold(PhysicsManifold manifold) 1289 | { 1290 | switch (manifold->bodyA->shape.type) 1291 | { 1292 | case PHYSICS_CIRCLE: 1293 | { 1294 | switch (manifold->bodyB->shape.type) 1295 | { 1296 | case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break; 1297 | case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break; 1298 | default: break; 1299 | } 1300 | } break; 1301 | case PHYSICS_POLYGON: 1302 | { 1303 | switch (manifold->bodyB->shape.type) 1304 | { 1305 | case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break; 1306 | case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break; 1307 | default: break; 1308 | } 1309 | } break; 1310 | default: break; 1311 | } 1312 | 1313 | // Update physics body grounded state if normal direction is down and grounded state is not set yet in previous manifolds 1314 | if (!manifold->bodyB->isGrounded) manifold->bodyB->isGrounded = (manifold->normal.y < 0); 1315 | } 1316 | 1317 | // Solves collision between two circle shape physics bodies 1318 | static void SolveCircleToCircle(PhysicsManifold manifold) 1319 | { 1320 | PhysicsBody bodyA = manifold->bodyA; 1321 | PhysicsBody bodyB = manifold->bodyB; 1322 | 1323 | if ((bodyA == NULL) || (bodyB == NULL)) return; 1324 | 1325 | // Calculate translational vector, which is normal 1326 | Vector2 normal = Vector2Subtract(bodyB->position, bodyA->position); 1327 | 1328 | float distSqr = MathLenSqr(normal); 1329 | float radius = bodyA->shape.radius + bodyB->shape.radius; 1330 | 1331 | // Check if circles are not in contact 1332 | if (distSqr >= radius*radius) 1333 | { 1334 | manifold->contactsCount = 0; 1335 | return; 1336 | } 1337 | 1338 | float distance = sqrtf(distSqr); 1339 | manifold->contactsCount = 1; 1340 | 1341 | if (distance == 0.0f) 1342 | { 1343 | manifold->penetration = bodyA->shape.radius; 1344 | manifold->normal = VECTOR2( 1.0f, 0.0f ); 1345 | manifold->contacts[0] = bodyA->position; 1346 | } 1347 | else 1348 | { 1349 | manifold->penetration = radius - distance; 1350 | manifold->normal = VECTOR2( normal.x/distance, normal.y/distance ); // Faster than using MathNormalize() due to sqrt is already performed 1351 | manifold->contacts[0] = VECTOR2( manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y ); 1352 | } 1353 | 1354 | // Update physics body grounded state if normal direction is down 1355 | if (!bodyA->isGrounded) bodyA->isGrounded = (manifold->normal.y < 0); 1356 | } 1357 | 1358 | // Solves collision between a circle to a polygon shape physics bodies 1359 | static void SolveCircleToPolygon(PhysicsManifold manifold) 1360 | { 1361 | PhysicsBody bodyA = manifold->bodyA; 1362 | PhysicsBody bodyB = manifold->bodyB; 1363 | 1364 | if ((bodyA == NULL) || (bodyB == NULL)) return; 1365 | 1366 | manifold->contactsCount = 0; 1367 | 1368 | // Transform circle center to polygon transform space 1369 | Vector2 center = bodyA->position; 1370 | center = Mat2MultiplyVector2(Mat2Transpose(bodyB->shape.transform), Vector2Subtract(center, bodyB->position)); 1371 | 1372 | // Find edge with minimum penetration 1373 | // It is the same concept as using support points in SolvePolygonToPolygon 1374 | float separation = -PHYSAC_FLT_MAX; 1375 | int faceNormal = 0; 1376 | PolygonData vertexData = bodyB->shape.vertexData; 1377 | 1378 | for (int i = 0; i < vertexData.vertexCount; i++) 1379 | { 1380 | float currentSeparation = MathDot(vertexData.normals[i], Vector2Subtract(center, vertexData.positions[i])); 1381 | 1382 | if (currentSeparation > bodyA->shape.radius) return; 1383 | 1384 | if (currentSeparation > separation) 1385 | { 1386 | separation = currentSeparation; 1387 | faceNormal = i; 1388 | } 1389 | } 1390 | 1391 | // Grab face's vertices 1392 | Vector2 v1 = vertexData.positions[faceNormal]; 1393 | int nextIndex = (((faceNormal + 1) < vertexData.vertexCount) ? (faceNormal + 1) : 0); 1394 | Vector2 v2 = vertexData.positions[nextIndex]; 1395 | 1396 | // Check to see if center is within polygon 1397 | if (separation < PHYSAC_EPSILON) 1398 | { 1399 | manifold->contactsCount = 1; 1400 | Vector2 normal = Mat2MultiplyVector2(bodyB->shape.transform, vertexData.normals[faceNormal]); 1401 | manifold->normal = VECTOR2( -normal.x, -normal.y ); 1402 | manifold->contacts[0] = VECTOR2( manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y ); 1403 | manifold->penetration = bodyA->shape.radius; 1404 | return; 1405 | } 1406 | 1407 | // Determine which voronoi region of the edge center of circle lies within 1408 | float dot1 = MathDot(Vector2Subtract(center, v1), Vector2Subtract(v2, v1)); 1409 | float dot2 = MathDot(Vector2Subtract(center, v2), Vector2Subtract(v1, v2)); 1410 | manifold->penetration = bodyA->shape.radius - separation; 1411 | 1412 | if (dot1 <= 0.0f) // Closest to v1 1413 | { 1414 | if (DistSqr(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return; 1415 | 1416 | manifold->contactsCount = 1; 1417 | Vector2 normal = Vector2Subtract(v1, center); 1418 | normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); 1419 | MathNormalize(&normal); 1420 | manifold->normal = normal; 1421 | v1 = Mat2MultiplyVector2(bodyB->shape.transform, v1); 1422 | v1 = Vector2Add(v1, bodyB->position); 1423 | manifold->contacts[0] = v1; 1424 | } 1425 | else if (dot2 <= 0.0f) // Closest to v2 1426 | { 1427 | if (DistSqr(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return; 1428 | 1429 | manifold->contactsCount = 1; 1430 | Vector2 normal = Vector2Subtract(v2, center); 1431 | v2 = Mat2MultiplyVector2(bodyB->shape.transform, v2); 1432 | v2 = Vector2Add(v2, bodyB->position); 1433 | manifold->contacts[0] = v2; 1434 | normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); 1435 | MathNormalize(&normal); 1436 | manifold->normal = normal; 1437 | } 1438 | else // Closest to face 1439 | { 1440 | Vector2 normal = vertexData.normals[faceNormal]; 1441 | 1442 | if (MathDot(Vector2Subtract(center, v1), normal) > bodyA->shape.radius) return; 1443 | 1444 | normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); 1445 | manifold->normal = VECTOR2( -normal.x, -normal.y ); 1446 | manifold->contacts[0] = VECTOR2( manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y ); 1447 | manifold->contactsCount = 1; 1448 | } 1449 | } 1450 | 1451 | // Solves collision between a polygon to a circle shape physics bodies 1452 | static void SolvePolygonToCircle(PhysicsManifold manifold) 1453 | { 1454 | PhysicsBody bodyA = manifold->bodyA; 1455 | PhysicsBody bodyB = manifold->bodyB; 1456 | 1457 | if ((bodyA == NULL) || (bodyB == NULL)) return; 1458 | 1459 | manifold->bodyA = bodyB; 1460 | manifold->bodyB = bodyA; 1461 | SolveCircleToPolygon(manifold); 1462 | 1463 | manifold->normal.x *= -1.0f; 1464 | manifold->normal.y *= -1.0f; 1465 | } 1466 | 1467 | // Solves collision between two polygons shape physics bodies 1468 | static void SolvePolygonToPolygon(PhysicsManifold manifold) 1469 | { 1470 | if ((manifold->bodyA == NULL) || (manifold->bodyB == NULL)) return; 1471 | 1472 | PhysicsShape bodyA = manifold->bodyA->shape; 1473 | PhysicsShape bodyB = manifold->bodyB->shape; 1474 | manifold->contactsCount = 0; 1475 | 1476 | // Check for separating axis with A shape's face planes 1477 | int faceA = 0; 1478 | float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB); 1479 | if (penetrationA >= 0.0f) return; 1480 | 1481 | // Check for separating axis with B shape's face planes 1482 | int faceB = 0; 1483 | float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA); 1484 | if (penetrationB >= 0.0f) return; 1485 | 1486 | int referenceIndex = 0; 1487 | bool flip = false; // Always point from A shape to B shape 1488 | 1489 | PhysicsShape refPoly; // Reference 1490 | PhysicsShape incPoly; // Incident 1491 | 1492 | // Determine which shape contains reference face 1493 | if (BiasGreaterThan(penetrationA, penetrationB)) 1494 | { 1495 | refPoly = bodyA; 1496 | incPoly = bodyB; 1497 | referenceIndex = faceA; 1498 | } 1499 | else 1500 | { 1501 | refPoly = bodyB; 1502 | incPoly = bodyA; 1503 | referenceIndex = faceB; 1504 | flip = true; 1505 | } 1506 | 1507 | // World space incident face 1508 | Vector2 incidentFace[2]; 1509 | FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex); 1510 | 1511 | // Setup reference face vertices 1512 | PolygonData refData = refPoly.vertexData; 1513 | Vector2 v1 = refData.positions[referenceIndex]; 1514 | referenceIndex = (((referenceIndex + 1) < refData.vertexCount) ? (referenceIndex + 1) : 0); 1515 | Vector2 v2 = refData.positions[referenceIndex]; 1516 | 1517 | // Transform vertices to world space 1518 | v1 = Mat2MultiplyVector2(refPoly.transform, v1); 1519 | v1 = Vector2Add(v1, refPoly.body->position); 1520 | v2 = Mat2MultiplyVector2(refPoly.transform, v2); 1521 | v2 = Vector2Add(v2, refPoly.body->position); 1522 | 1523 | // Calculate reference face side normal in world space 1524 | Vector2 sidePlaneNormal = Vector2Subtract(v2, v1); 1525 | MathNormalize(&sidePlaneNormal); 1526 | 1527 | // Orthogonalize 1528 | Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; 1529 | float refC = MathDot(refFaceNormal, v1); 1530 | float negSide = MathDot(sidePlaneNormal, v1)*-1; 1531 | float posSide = MathDot(sidePlaneNormal, v2); 1532 | 1533 | // Clip incident face to reference face side planes (due to floating point error, possible to not have required points 1534 | if (Clip(VECTOR2( -sidePlaneNormal.x, -sidePlaneNormal.y ), negSide, &incidentFace[0], &incidentFace[1]) < 2) return; 1535 | if (Clip(sidePlaneNormal, posSide, &incidentFace[0], &incidentFace[1]) < 2) return; 1536 | 1537 | // Flip normal if required 1538 | manifold->normal = (flip ? VECTOR2( -refFaceNormal.x, -refFaceNormal.y ) : refFaceNormal); 1539 | 1540 | // Keep points behind reference face 1541 | int currentPoint = 0; // Clipped points behind reference face 1542 | float separation = MathDot(refFaceNormal, incidentFace[0]) - refC; 1543 | if (separation <= 0.0f) 1544 | { 1545 | manifold->contacts[currentPoint] = incidentFace[0]; 1546 | manifold->penetration = -separation; 1547 | currentPoint++; 1548 | } 1549 | else manifold->penetration = 0.0f; 1550 | 1551 | separation = MathDot(refFaceNormal, incidentFace[1]) - refC; 1552 | 1553 | if (separation <= 0.0f) 1554 | { 1555 | manifold->contacts[currentPoint] = incidentFace[1]; 1556 | manifold->penetration += -separation; 1557 | currentPoint++; 1558 | 1559 | // Calculate total penetration average 1560 | manifold->penetration /= currentPoint; 1561 | } 1562 | 1563 | manifold->contactsCount = currentPoint; 1564 | } 1565 | 1566 | // Integrates physics forces into velocity 1567 | static void IntegratePhysicsForces(PhysicsBody body) 1568 | { 1569 | if ((body == NULL) || (body->inverseMass == 0.0f) || !body->enabled) return; 1570 | 1571 | body->velocity.x += (body->force.x*body->inverseMass)*(deltaTime/2.0); 1572 | body->velocity.y += (body->force.y*body->inverseMass)*(deltaTime/2.0); 1573 | 1574 | if (body->useGravity) 1575 | { 1576 | body->velocity.x += gravityForce.x*(deltaTime/1000/2.0); 1577 | body->velocity.y += gravityForce.y*(deltaTime/1000/2.0); 1578 | } 1579 | 1580 | if (!body->freezeOrient) body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2.0); 1581 | } 1582 | 1583 | // Initializes physics manifolds to solve collisions 1584 | static void InitializePhysicsManifolds(PhysicsManifold manifold) 1585 | { 1586 | PhysicsBody bodyA = manifold->bodyA; 1587 | PhysicsBody bodyB = manifold->bodyB; 1588 | 1589 | if ((bodyA == NULL) || (bodyB == NULL)) return; 1590 | 1591 | // Calculate average restitution, static and dynamic friction 1592 | manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution); 1593 | manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction); 1594 | manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction); 1595 | 1596 | for (int i = 0; i < manifold->contactsCount; i++) 1597 | { 1598 | // Caculate radius from center of mass to contact 1599 | Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); 1600 | Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); 1601 | 1602 | Vector2 crossA = MathCross(bodyA->angularVelocity, radiusA); 1603 | Vector2 crossB = MathCross(bodyB->angularVelocity, radiusB); 1604 | 1605 | Vector2 radiusV = { 0.0f, 0.0f }; 1606 | radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x; 1607 | radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y; 1608 | 1609 | // Determine if we should perform a resting collision or not; 1610 | // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution 1611 | if (MathLenSqr(radiusV) < (MathLenSqr(VECTOR2( gravityForce.x*(float)deltaTime/1000, gravityForce.y*(float)deltaTime/1000 )) + PHYSAC_EPSILON)) manifold->restitution = 0; 1612 | } 1613 | } 1614 | 1615 | // Integrates physics collisions impulses to solve collisions 1616 | static void IntegratePhysicsImpulses(PhysicsManifold manifold) 1617 | { 1618 | PhysicsBody bodyA = manifold->bodyA; 1619 | PhysicsBody bodyB = manifold->bodyB; 1620 | 1621 | if ((bodyA == NULL) || (bodyB == NULL)) return; 1622 | 1623 | // Early out and positional correct if both objects have infinite mass 1624 | if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON) 1625 | { 1626 | bodyA->velocity = PHYSAC_VECTOR_ZERO; 1627 | bodyB->velocity = PHYSAC_VECTOR_ZERO; 1628 | return; 1629 | } 1630 | 1631 | for (int i = 0; i < manifold->contactsCount; i++) 1632 | { 1633 | // Calculate radius from center of mass to contact 1634 | Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); 1635 | Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); 1636 | 1637 | // Calculate relative velocity 1638 | Vector2 radiusV = { 0.0f, 0.0f }; 1639 | radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; 1640 | radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; 1641 | 1642 | // Relative velocity along the normal 1643 | float contactVelocity = MathDot(radiusV, manifold->normal); 1644 | 1645 | // Do not resolve if velocities are separating 1646 | if (contactVelocity > 0.0f) return; 1647 | 1648 | float raCrossN = MathCrossVector2(radiusA, manifold->normal); 1649 | float rbCrossN = MathCrossVector2(radiusB, manifold->normal); 1650 | 1651 | float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia; 1652 | 1653 | // Calculate impulse scalar value 1654 | float impulse = -(1.0f + manifold->restitution)*contactVelocity; 1655 | impulse /= inverseMassSum; 1656 | impulse /= (float)manifold->contactsCount; 1657 | 1658 | // Apply impulse to each physics body 1659 | Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse }; 1660 | 1661 | if (bodyA->enabled) 1662 | { 1663 | bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x); 1664 | bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y); 1665 | if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, VECTOR2( -impulseV.x, -impulseV.y )); 1666 | } 1667 | 1668 | if (bodyB->enabled) 1669 | { 1670 | bodyB->velocity.x += bodyB->inverseMass*(impulseV.x); 1671 | bodyB->velocity.y += bodyB->inverseMass*(impulseV.y); 1672 | if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, impulseV); 1673 | } 1674 | 1675 | // Apply friction impulse to each physics body 1676 | radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; 1677 | radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; 1678 | 1679 | Vector2 tangent = { radiusV.x - (manifold->normal.x*MathDot(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathDot(radiusV, manifold->normal)) }; 1680 | MathNormalize(&tangent); 1681 | 1682 | // Calculate impulse tangent magnitude 1683 | float impulseTangent = -MathDot(radiusV, tangent); 1684 | impulseTangent /= inverseMassSum; 1685 | impulseTangent /= (float)manifold->contactsCount; 1686 | 1687 | float absImpulseTangent = fabs(impulseTangent); 1688 | 1689 | // Don't apply tiny friction impulses 1690 | if (absImpulseTangent <= PHYSAC_EPSILON) return; 1691 | 1692 | // Apply coulumb's law 1693 | Vector2 tangentImpulse = { 0.0f, 0.0f }; 1694 | if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = VECTOR2( tangent.x*impulseTangent, tangent.y*impulseTangent ); 1695 | else tangentImpulse = VECTOR2( tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction ); 1696 | 1697 | // Apply friction impulse 1698 | if (bodyA->enabled) 1699 | { 1700 | bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x); 1701 | bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y); 1702 | 1703 | if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, VECTOR2( -tangentImpulse.x, -tangentImpulse.y )); 1704 | } 1705 | 1706 | if (bodyB->enabled) 1707 | { 1708 | bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x); 1709 | bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y); 1710 | 1711 | if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, tangentImpulse); 1712 | } 1713 | } 1714 | } 1715 | 1716 | // Integrates physics velocity into position and forces 1717 | static void IntegratePhysicsVelocity(PhysicsBody body) 1718 | { 1719 | if ((body == NULL) ||!body->enabled) return; 1720 | 1721 | body->position.x += body->velocity.x*deltaTime; 1722 | body->position.y += body->velocity.y*deltaTime; 1723 | 1724 | if (!body->freezeOrient) body->orient += body->angularVelocity*deltaTime; 1725 | Mat2Set(&body->shape.transform, body->orient); 1726 | 1727 | IntegratePhysicsForces(body); 1728 | } 1729 | 1730 | // Corrects physics bodies positions based on manifolds collision information 1731 | static void CorrectPhysicsPositions(PhysicsManifold manifold) 1732 | { 1733 | PhysicsBody bodyA = manifold->bodyA; 1734 | PhysicsBody bodyB = manifold->bodyB; 1735 | 1736 | if ((bodyA == NULL) || (bodyB == NULL)) return; 1737 | 1738 | Vector2 correction = { 0.0f, 0.0f }; 1739 | correction.x = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; 1740 | correction.y = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; 1741 | 1742 | if (bodyA->enabled) 1743 | { 1744 | bodyA->position.x -= correction.x*bodyA->inverseMass; 1745 | bodyA->position.y -= correction.y*bodyA->inverseMass; 1746 | } 1747 | 1748 | if (bodyB->enabled) 1749 | { 1750 | bodyB->position.x += correction.x*bodyB->inverseMass; 1751 | bodyB->position.y += correction.y*bodyB->inverseMass; 1752 | } 1753 | } 1754 | 1755 | // Returns the extreme point along a direction within a polygon 1756 | static Vector2 GetSupport(PhysicsShape shape, Vector2 dir) 1757 | { 1758 | float bestProjection = -PHYSAC_FLT_MAX; 1759 | Vector2 bestVertex = { 0.0f, 0.0f }; 1760 | PolygonData data = shape.vertexData; 1761 | 1762 | for (int i = 0; i < data.vertexCount; i++) 1763 | { 1764 | Vector2 vertex = data.positions[i]; 1765 | float projection = MathDot(vertex, dir); 1766 | 1767 | if (projection > bestProjection) 1768 | { 1769 | bestVertex = vertex; 1770 | bestProjection = projection; 1771 | } 1772 | } 1773 | 1774 | return bestVertex; 1775 | } 1776 | 1777 | // Finds polygon shapes axis least penetration 1778 | static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB) 1779 | { 1780 | float bestDistance = -PHYSAC_FLT_MAX; 1781 | int bestIndex = 0; 1782 | 1783 | PolygonData dataA = shapeA.vertexData; 1784 | //PolygonData dataB = shapeB.vertexData; 1785 | 1786 | for (int i = 0; i < dataA.vertexCount; i++) 1787 | { 1788 | // Retrieve a face normal from A shape 1789 | Vector2 normal = dataA.normals[i]; 1790 | Vector2 transNormal = Mat2MultiplyVector2(shapeA.transform, normal); 1791 | 1792 | // Transform face normal into B shape's model space 1793 | Mat2 buT = Mat2Transpose(shapeB.transform); 1794 | normal = Mat2MultiplyVector2(buT, transNormal); 1795 | 1796 | // Retrieve support point from B shape along -n 1797 | Vector2 support = GetSupport(shapeB, VECTOR2( -normal.x, -normal.y )); 1798 | 1799 | // Retrieve vertex on face from A shape, transform into B shape's model space 1800 | Vector2 vertex = dataA.positions[i]; 1801 | vertex = Mat2MultiplyVector2(shapeA.transform, vertex); 1802 | vertex = Vector2Add(vertex, shapeA.body->position); 1803 | vertex = Vector2Subtract(vertex, shapeB.body->position); 1804 | vertex = Mat2MultiplyVector2(buT, vertex); 1805 | 1806 | // Compute penetration distance in B shape's model space 1807 | float distance = MathDot(normal, Vector2Subtract(support, vertex)); 1808 | 1809 | // Store greatest distance 1810 | if (distance > bestDistance) 1811 | { 1812 | bestDistance = distance; 1813 | bestIndex = i; 1814 | } 1815 | } 1816 | 1817 | *faceIndex = bestIndex; 1818 | return bestDistance; 1819 | } 1820 | 1821 | // Finds two polygon shapes incident face 1822 | static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index) 1823 | { 1824 | PolygonData refData = ref.vertexData; 1825 | PolygonData incData = inc.vertexData; 1826 | 1827 | Vector2 referenceNormal = refData.normals[index]; 1828 | 1829 | // Calculate normal in incident's frame of reference 1830 | referenceNormal = Mat2MultiplyVector2(ref.transform, referenceNormal); // To world space 1831 | referenceNormal = Mat2MultiplyVector2(Mat2Transpose(inc.transform), referenceNormal); // To incident's model space 1832 | 1833 | // Find most anti-normal face on polygon 1834 | int incidentFace = 0; 1835 | float minDot = PHYSAC_FLT_MAX; 1836 | 1837 | for (int i = 0; i < incData.vertexCount; i++) 1838 | { 1839 | float dot = MathDot(referenceNormal, incData.normals[i]); 1840 | 1841 | if (dot < minDot) 1842 | { 1843 | minDot = dot; 1844 | incidentFace = i; 1845 | } 1846 | } 1847 | 1848 | // Assign face vertices for incident face 1849 | *v0 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); 1850 | *v0 = Vector2Add(*v0, inc.body->position); 1851 | incidentFace = (((incidentFace + 1) < incData.vertexCount) ? (incidentFace + 1) : 0); 1852 | *v1 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); 1853 | *v1 = Vector2Add(*v1, inc.body->position); 1854 | } 1855 | 1856 | // Calculates clipping based on a normal and two faces 1857 | static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) 1858 | { 1859 | int sp = 0; 1860 | Vector2 out[2] = { *faceA, *faceB }; 1861 | 1862 | // Retrieve distances from each endpoint to the line 1863 | float distanceA = MathDot(normal, *faceA) - clip; 1864 | float distanceB = MathDot(normal, *faceB) - clip; 1865 | 1866 | // If negative (behind plane) 1867 | if (distanceA <= 0.0f) out[sp++] = *faceA; 1868 | if (distanceB <= 0.0f) out[sp++] = *faceB; 1869 | 1870 | // If the points are on different sides of the plane 1871 | if ((distanceA*distanceB) < 0.0f) 1872 | { 1873 | // Push intersection point 1874 | float alpha = distanceA/(distanceA - distanceB); 1875 | out[sp] = *faceA; 1876 | Vector2 delta = Vector2Subtract(*faceB, *faceA); 1877 | delta.x *= alpha; 1878 | delta.y *= alpha; 1879 | out[sp] = Vector2Add(out[sp], delta); 1880 | sp++; 1881 | } 1882 | 1883 | // Assign the new converted values 1884 | *faceA = out[0]; 1885 | *faceB = out[1]; 1886 | 1887 | return sp; 1888 | } 1889 | 1890 | // Check if values are between bias range 1891 | static bool BiasGreaterThan(float valueA, float valueB) 1892 | { 1893 | return (valueA >= (valueB*0.95f + valueA*0.01f)); 1894 | } 1895 | 1896 | // Returns the barycenter of a triangle given by 3 points 1897 | static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) 1898 | { 1899 | Vector2 result = { 0.0f, 0.0f }; 1900 | 1901 | result.x = (v1.x + v2.x + v3.x)/3; 1902 | result.y = (v1.y + v2.y + v3.y)/3; 1903 | 1904 | return result; 1905 | } 1906 | 1907 | // Initializes hi-resolution MONOTONIC timer 1908 | static void InitTimer(void) 1909 | { 1910 | srand(time(NULL)); // Initialize random seed 1911 | 1912 | #if defined(_WIN32) 1913 | QueryPerformanceFrequency((unsigned long long int *) &frequency); 1914 | #endif 1915 | 1916 | #if defined(__linux__) 1917 | struct timespec now; 1918 | if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) frequency = 1000000000; 1919 | #endif 1920 | 1921 | #if defined(__APPLE__) 1922 | mach_timebase_info_data_t timebase; 1923 | mach_timebase_info(&timebase); 1924 | frequency = (timebase.denom*1e9)/timebase.numer; 1925 | #endif 1926 | 1927 | baseTime = GetTimeCount(); // Get MONOTONIC clock time offset 1928 | startTime = GetCurrentTime(); // Get current time 1929 | } 1930 | 1931 | // Get hi-res MONOTONIC time measure in seconds 1932 | static uint64_t GetTimeCount(void) 1933 | { 1934 | uint64_t value = 0; 1935 | 1936 | #if defined(_WIN32) 1937 | QueryPerformanceCounter((unsigned long long int *) &value); 1938 | #endif 1939 | 1940 | #if defined(__linux__) 1941 | struct timespec now; 1942 | clock_gettime(CLOCK_MONOTONIC, &now); 1943 | value = (uint64_t)now.tv_sec*(uint64_t)1000000000 + (uint64_t)now.tv_nsec; 1944 | #endif 1945 | 1946 | #if defined(__APPLE__) 1947 | value = mach_absolute_time(); 1948 | #endif 1949 | 1950 | return value; 1951 | } 1952 | 1953 | // Get current time in milliseconds 1954 | static double GetCurrentTime(void) 1955 | { 1956 | return (double)(GetTimeCount() - baseTime)/frequency*1000; 1957 | } 1958 | 1959 | // Returns the cross product of a vector and a value 1960 | static inline Vector2 MathCross(float value, Vector2 vector) 1961 | { 1962 | return VECTOR2( -value*vector.y, value*vector.x ); 1963 | } 1964 | 1965 | // Returns the cross product of two vectors 1966 | static inline float MathCrossVector2(Vector2 v1, Vector2 v2) 1967 | { 1968 | return (v1.x*v2.y - v1.y*v2.x); 1969 | } 1970 | 1971 | // Returns the len square root of a vector 1972 | static inline float MathLenSqr(Vector2 vector) 1973 | { 1974 | return (vector.x*vector.x + vector.y*vector.y); 1975 | } 1976 | 1977 | // Returns the dot product of two vectors 1978 | static inline float MathDot(Vector2 v1, Vector2 v2) 1979 | { 1980 | return (v1.x*v2.x + v1.y*v2.y); 1981 | } 1982 | 1983 | // Returns the square root of distance between two vectors 1984 | static inline float DistSqr(Vector2 v1, Vector2 v2) 1985 | { 1986 | Vector2 dir = Vector2Subtract(v1, v2); 1987 | return MathDot(dir, dir); 1988 | } 1989 | 1990 | // Returns the normalized values of a vector 1991 | static void MathNormalize(Vector2 *vector) 1992 | { 1993 | float length, ilength; 1994 | 1995 | Vector2 aux = *vector; 1996 | length = sqrtf(aux.x*aux.x + aux.y*aux.y); 1997 | 1998 | if (length == 0) length = 1.0f; 1999 | 2000 | ilength = 1.0f/length; 2001 | 2002 | vector->x *= ilength; 2003 | vector->y *= ilength; 2004 | } 2005 | 2006 | #if defined(PHYSAC_STANDALONE) 2007 | // Returns the sum of two given vectors 2008 | static inline Vector2 Vector2Add(Vector2 v1, Vector2 v2) 2009 | { 2010 | return VECTOR2( v1.x + v2.x, v1.y + v2.y ); 2011 | } 2012 | 2013 | // Returns the subtract of two given vectors 2014 | static inline Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) 2015 | { 2016 | return VECTOR2( v1.x - v2.x, v1.y - v2.y ); 2017 | } 2018 | #endif 2019 | 2020 | // Creates a matrix 2x2 from a given radians value 2021 | static Mat2 Mat2Radians(float radians) 2022 | { 2023 | float c = cosf(radians); 2024 | float s = sinf(radians); 2025 | 2026 | return MAT2( c, -s, s, c ); 2027 | } 2028 | 2029 | // Set values from radians to a created matrix 2x2 2030 | static void Mat2Set(Mat2 *matrix, float radians) 2031 | { 2032 | float cos = cosf(radians); 2033 | float sin = sinf(radians); 2034 | 2035 | matrix->m00 = cos; 2036 | matrix->m01 = -sin; 2037 | matrix->m10 = sin; 2038 | matrix->m11 = cos; 2039 | } 2040 | 2041 | // Returns the transpose of a given matrix 2x2 2042 | static inline Mat2 Mat2Transpose(Mat2 matrix) 2043 | { 2044 | return MAT2( matrix.m00, matrix.m10, matrix.m01, matrix.m11 ); 2045 | } 2046 | 2047 | // Multiplies a vector by a matrix 2x2 2048 | static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector) 2049 | { 2050 | return VECTOR2( matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y ); 2051 | } 2052 | 2053 | #endif // PHYSAC_IMPLEMENTATION 2054 | --------------------------------------------------------------------------------