├── .clang-format ├── .gitattributes ├── .github ├── actions │ ├── build │ │ └── action.yml │ └── deps │ │ └── action.yml └── workflows │ ├── android_builds.yml │ ├── ios_builds.yml │ ├── linux_builds.yml │ ├── macos_builds.yml │ ├── runner.yml │ ├── static_checks.yml │ └── windows_builds.yml ├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── README.md ├── SConstruct ├── demo ├── icon.svg ├── icon.svg.import ├── physics_server_box2d.gdextension ├── project.godot └── world.tscn ├── scripts ├── clang-format.sh └── clang-tidy.sh └── src ├── b2_user_settings.h ├── bodies ├── box2d_area.cpp ├── box2d_area.h ├── box2d_body.cpp ├── box2d_body.h ├── box2d_collision_object.cpp ├── box2d_collision_object.h ├── box2d_direct_body_state.cpp └── box2d_direct_body_state.h ├── box2d_type_conversions.cpp ├── box2d_type_conversions.h ├── joints ├── box2d_joint.cpp └── box2d_joint.h ├── register_types.cpp ├── register_types.h ├── servers ├── physics_server_box2d.cpp └── physics_server_box2d.h ├── shapes ├── box2d_shape.cpp ├── box2d_shape.h ├── box2d_shape_capsule.cpp ├── box2d_shape_capsule.h ├── box2d_shape_circle.cpp ├── box2d_shape_circle.h ├── box2d_shape_concave_polygon.cpp ├── box2d_shape_concave_polygon.h ├── box2d_shape_convex_polygon.cpp ├── box2d_shape_convex_polygon.h ├── box2d_shape_rectangle.cpp ├── box2d_shape_rectangle.h ├── box2d_shape_segment.cpp ├── box2d_shape_segment.h ├── box2d_shape_separation_ray.cpp ├── box2d_shape_separation_ray.h ├── box2d_shape_world_boundary.cpp └── box2d_shape_world_boundary.h └── spaces ├── box2d_direct_space_state.cpp ├── box2d_direct_space_state.h ├── box2d_query_callback.cpp ├── box2d_query_callback.h ├── box2d_ray_cast_callback.cpp ├── box2d_ray_cast_callback.h ├── box2d_space.cpp ├── box2d_space.h ├── box2d_space_contact_filter.cpp ├── box2d_space_contact_filter.h ├── box2d_space_contact_listener.cpp └── box2d_space_contact_listener.h /.clang-format: -------------------------------------------------------------------------------- 1 | # Commented out parameters are those with the same value as base LLVM style. 2 | # We can uncomment them if we want to change their value, or enforce the 3 | # chosen value in case the base style changes (last sync: Clang 14.0). 4 | --- 5 | ### General config, applies to all languages ### 6 | BasedOnStyle: LLVM 7 | AccessModifierOffset: -4 8 | AlignAfterOpenBracket: DontAlign 9 | # AlignArrayOfStructures: None 10 | # AlignConsecutiveMacros: None 11 | # AlignConsecutiveAssignments: None 12 | # AlignConsecutiveBitFields: None 13 | # AlignConsecutiveDeclarations: None 14 | # AlignEscapedNewlines: Right 15 | AlignOperands: DontAlign 16 | AlignTrailingComments: false 17 | # AllowAllArgumentsOnNextLine: true 18 | AllowAllParametersOfDeclarationOnNextLine: false 19 | # AllowShortEnumsOnASingleLine: true 20 | # AllowShortBlocksOnASingleLine: Never 21 | # AllowShortCaseLabelsOnASingleLine: false 22 | # AllowShortFunctionsOnASingleLine: All 23 | # AllowShortLambdasOnASingleLine: All 24 | # AllowShortIfStatementsOnASingleLine: Never 25 | # AllowShortLoopsOnASingleLine: false 26 | # AlwaysBreakAfterDefinitionReturnType: None 27 | # AlwaysBreakAfterReturnType: None 28 | # AlwaysBreakBeforeMultilineStrings: false 29 | # AlwaysBreakTemplateDeclarations: MultiLine 30 | # AttributeMacros: 31 | # - __capability 32 | # BinPackArguments: true 33 | # BinPackParameters: true 34 | # BraceWrapping: 35 | # AfterCaseLabel: false 36 | # AfterClass: false 37 | # AfterControlStatement: Never 38 | # AfterEnum: false 39 | # AfterFunction: false 40 | # AfterNamespace: false 41 | # AfterObjCDeclaration: false 42 | # AfterStruct: false 43 | # AfterUnion: false 44 | # AfterExternBlock: false 45 | # BeforeCatch: false 46 | # BeforeElse: false 47 | # BeforeLambdaBody: false 48 | # BeforeWhile: false 49 | # IndentBraces: false 50 | # SplitEmptyFunction: true 51 | # SplitEmptyRecord: true 52 | # SplitEmptyNamespace: true 53 | # BreakBeforeBinaryOperators: None 54 | # BreakBeforeConceptDeclarations: true 55 | # BreakBeforeBraces: Attach 56 | # BreakBeforeInheritanceComma: false 57 | # BreakInheritanceList: BeforeColon 58 | # BreakBeforeTernaryOperators: true 59 | # BreakConstructorInitializersBeforeComma: false 60 | BreakConstructorInitializers: AfterColon 61 | # BreakStringLiterals: true 62 | ColumnLimit: 0 63 | # CommentPragmas: '^ IWYU pragma:' 64 | # QualifierAlignment: Leave 65 | # CompactNamespaces: false 66 | ConstructorInitializerIndentWidth: 8 67 | ContinuationIndentWidth: 8 68 | Cpp11BracedListStyle: false 69 | # DeriveLineEnding: true 70 | # DerivePointerAlignment: false 71 | # DisableFormat: false 72 | # EmptyLineAfterAccessModifier: Never 73 | # EmptyLineBeforeAccessModifier: LogicalBlock 74 | # ExperimentalAutoDetectBinPacking: false 75 | # PackConstructorInitializers: BinPack 76 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 77 | # AllowAllConstructorInitializersOnNextLine: true 78 | # FixNamespaceComments: true 79 | # ForEachMacros: 80 | # - foreach 81 | # - Q_FOREACH 82 | # - BOOST_FOREACH 83 | # IfMacros: 84 | # - KJ_IF_MAYBE 85 | # IncludeBlocks: Preserve 86 | IncludeCategories: 87 | - Regex: '".*"' 88 | Priority: 1 89 | - Regex: '^<.*\.h>' 90 | Priority: 2 91 | - Regex: '^<.*' 92 | Priority: 3 93 | # IncludeIsMainRegex: '(Test)?$' 94 | # IncludeIsMainSourceRegex: '' 95 | # IndentAccessModifiers: false 96 | IndentCaseLabels: true 97 | # IndentCaseBlocks: false 98 | # IndentGotoLabels: true 99 | # IndentPPDirectives: None 100 | # IndentExternBlock: AfterExternBlock 101 | # IndentRequires: false 102 | IndentWidth: 4 103 | # IndentWrappedFunctionNames: false 104 | # InsertTrailingCommas: None 105 | # JavaScriptQuotes: Leave 106 | # JavaScriptWrapImports: true 107 | KeepEmptyLinesAtTheStartOfBlocks: false 108 | # LambdaBodyIndentation: Signature 109 | # MacroBlockBegin: '' 110 | # MacroBlockEnd: '' 111 | # MaxEmptyLinesToKeep: 1 112 | # NamespaceIndentation: None 113 | # PenaltyBreakAssignment: 2 114 | # PenaltyBreakBeforeFirstCallParameter: 19 115 | # PenaltyBreakComment: 300 116 | # PenaltyBreakFirstLessLess: 120 117 | # PenaltyBreakOpenParenthesis: 0 118 | # PenaltyBreakString: 1000 119 | # PenaltyBreakTemplateDeclaration: 10 120 | # PenaltyExcessCharacter: 1000000 121 | # PenaltyReturnTypeOnItsOwnLine: 60 122 | # PenaltyIndentedWhitespace: 0 123 | # PointerAlignment: Right 124 | # PPIndentWidth: -1 125 | # ReferenceAlignment: Pointer 126 | # ReflowComments: true 127 | # RemoveBracesLLVM: false 128 | # SeparateDefinitionBlocks: Leave 129 | # ShortNamespaceLines: 1 130 | # SortIncludes: CaseSensitive 131 | # SortJavaStaticImport: Before 132 | # SortUsingDeclarations: true 133 | # SpaceAfterCStyleCast: false 134 | # SpaceAfterLogicalNot: false 135 | # SpaceAfterTemplateKeyword: true 136 | # SpaceBeforeAssignmentOperators: true 137 | # SpaceBeforeCaseColon: false 138 | # SpaceBeforeCpp11BracedList: false 139 | # SpaceBeforeCtorInitializerColon: true 140 | # SpaceBeforeInheritanceColon: true 141 | # SpaceBeforeParens: ControlStatements 142 | # SpaceBeforeParensOptions: 143 | # AfterControlStatements: true 144 | # AfterForeachMacros: true 145 | # AfterFunctionDefinitionName: false 146 | # AfterFunctionDeclarationName: false 147 | # AfterIfMacros: true 148 | # AfterOverloadedOperator: false 149 | # BeforeNonEmptyParentheses: false 150 | # SpaceAroundPointerQualifiers: Default 151 | # SpaceBeforeRangeBasedForLoopColon: true 152 | # SpaceInEmptyBlock: false 153 | # SpaceInEmptyParentheses: false 154 | # SpacesBeforeTrailingComments: 1 155 | # SpacesInAngles: Never 156 | # SpacesInConditionalStatement: false 157 | # SpacesInContainerLiterals: true 158 | # SpacesInCStyleCastParentheses: false 159 | ## Godot TODO: We'll want to use a min of 1, but we need to see how to fix 160 | ## our comment capitalization at the same time. 161 | SpacesInLineCommentPrefix: 162 | Minimum: 0 163 | Maximum: -1 164 | # SpacesInParentheses: false 165 | # SpacesInSquareBrackets: false 166 | # SpaceBeforeSquareBrackets: false 167 | # BitFieldColonSpacing: Both 168 | # StatementAttributeLikeMacros: 169 | # - Q_EMIT 170 | # StatementMacros: 171 | # - Q_UNUSED 172 | # - QT_REQUIRE_VERSION 173 | TabWidth: 4 174 | # UseCRLF: false 175 | UseTab: Always 176 | # WhitespaceSensitiveMacros: 177 | # - STRINGIZE 178 | # - PP_STRINGIZE 179 | # - BOOST_PP_STRINGIZE 180 | # - NS_SWIFT_NAME 181 | # - CF_SWIFT_NAME 182 | --- 183 | ### C++ specific config ### 184 | Language: Cpp 185 | Standard: c++17 186 | --- 187 | ### ObjC specific config ### 188 | Language: ObjC 189 | # ObjCBinPackProtocolList: Auto 190 | ObjCBlockIndentWidth: 4 191 | # ObjCBreakBeforeNestedBlockParam: true 192 | # ObjCSpaceAfterProperty: false 193 | # ObjCSpaceBeforeProtocolList: true 194 | --- 195 | ### Java specific config ### 196 | Language: Java 197 | # BreakAfterJavaFieldAnnotations: false 198 | JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] 199 | ... 200 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/actions/build/action.yml: -------------------------------------------------------------------------------- 1 | name: Physics Server Box2D Build 2 | description: Build Godot Cpp and the Physics Server 2D Extension. Also uploads the artifacts. 3 | 4 | inputs: 5 | platform: 6 | required: true 7 | description: Target platform. 8 | arch: 9 | default: '' 10 | description: Target architecture. 11 | target: 12 | required: true 13 | description: Build target (editor, template_release, template_debug). 14 | sconsflags: 15 | required: true 16 | description: Extra flags 17 | 18 | runs: 19 | using: composite 20 | steps: 21 | - name: Cache .scons_cache 22 | uses: actions/cache@v3 23 | with: 24 | path: | 25 | ${{ github.workspace }}/.scons-cache/ 26 | ${{ github.workspace }}/godot-cpp/.scons-cache/ 27 | key: ${{ inputs.platform }}_${{ inputs.arch }}_${{ inputs.target }}_cache 28 | - name: Setup python and scons 29 | uses: ./.github/actions/deps 30 | - name: Lint 31 | shell: sh 32 | run: 33 | ./scripts/clang-format.sh 34 | ./scripts/clang-tidy.sh 35 | - name: Build Godot Cpp 36 | shell: sh 37 | env: 38 | SCONS_CACHE: .scons-cache 39 | SCONS_CACHE_DIR: .scons-cache 40 | run: | 41 | cd godot-cpp && scons target=${{ inputs.target }} platform=${{ inputs.platform }} arch=${{ inputs.arch }} generate_bindings=yes ${{ inputs.sconsflags }} 42 | - name: Build Physics Server Box2D 43 | shell: sh 44 | env: 45 | SCONS_CACHE: .scons-cache 46 | SCONS_CACHE_DIR: .scons-cache 47 | run: | 48 | scons target=${{ inputs.target }} platform=${{ inputs.platform }} arch=${{ inputs.arch }} generate_bindings=no ${{ inputs.sconsflags }} 49 | - name: Upload Artifact 50 | uses: actions/upload-artifact@v3 51 | with: 52 | name: Physics Server Box2D - ${{ matrix.os }} 53 | path: | 54 | ${{ github.workspace }}/demo/bin/ 55 | ${{ github.workspace }}/demo/physics_server_box2d.gdextension 56 | retention-days: 14 57 | -------------------------------------------------------------------------------- /.github/actions/deps/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup python and scons 2 | description: Setup python, install the pip version of scons. 3 | 4 | inputs: 5 | python-version: 6 | description: The python version to use. 7 | default: "3.x" 8 | python-arch: 9 | description: The python architecture. 10 | default: "x64" 11 | 12 | runs: 13 | using: "composite" 14 | steps: 15 | # Use python 3.x release (works cross platform) 16 | - name: Set up Python 3.x 17 | uses: actions/setup-python@v4 18 | with: 19 | # Semantic version range syntax or exact version of a Python version 20 | python-version: ${{ inputs.python-version }} 21 | # Optional - x64 or x86 architecture, defaults to x64 22 | architecture: ${{ inputs.python-arch }} 23 | 24 | - name: Setup scons 25 | shell: bash 26 | run: | 27 | python -c "import sys; print(sys.version)" 28 | python -m pip install scons==4.4.0 29 | scons --version 30 | 31 | - name: Setup clang-format 32 | shell: bash 33 | run: | 34 | python -m pip install clang-format 35 | -------------------------------------------------------------------------------- /.github/workflows/android_builds.yml: -------------------------------------------------------------------------------- 1 | name: 🤖 Android Builds 2 | on: 3 | workflow_call: 4 | 5 | # Global Settings 6 | env: 7 | SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no 8 | 9 | jobs: 10 | android: 11 | runs-on: "ubuntu-20.04" 12 | name: Android Build ${{ matrix.target }} ${{ matrix.arch }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | target: [template_debug, template_release] 17 | arch: [arm64, x86_64] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | - name: Set up Java 11 24 | uses: actions/setup-java@v3 25 | with: 26 | distribution: temurin 27 | java-version: 11 28 | 29 | - name: Build ${{ matrix.target }} ${{ matrix.arch }} 30 | uses: ./.github/actions/build 31 | with: 32 | sconsflags: ${{ env.SCONSFLAGS }} 33 | arch: ${{ matrix.arch }} 34 | platform: android 35 | target: ${{ matrix.target }} 36 | -------------------------------------------------------------------------------- /.github/workflows/ios_builds.yml: -------------------------------------------------------------------------------- 1 | name: 🍏 iOS Builds 2 | on: 3 | workflow_call: 4 | 5 | # Global Settings 6 | env: 7 | SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no 8 | 9 | jobs: 10 | ios: 11 | runs-on: "macos-latest" 12 | name: iOS Build ${{ matrix.target }} ${{ matrix.arch }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | target: [template_debug, template_release] 17 | arch: [arm64] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | - name: Build ${{ matrix.target }} ${{ matrix.arch }} 24 | uses: ./.github/actions/build 25 | with: 26 | sconsflags: ${{ env.SCONSFLAGS }} 27 | arch: ${{ matrix.arch }} 28 | platform: ios 29 | target: ${{ matrix.target }} 30 | -------------------------------------------------------------------------------- /.github/workflows/linux_builds.yml: -------------------------------------------------------------------------------- 1 | name: 🐧 Linux Builds 2 | on: 3 | workflow_call: 4 | 5 | # Global Settings 6 | env: 7 | SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no 8 | 9 | jobs: 10 | ios: 11 | runs-on: "ubuntu-20.04" 12 | name: Linux Build ${{ matrix.target }} ${{ matrix.arch }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | target: [template_debug, template_release] 17 | arch: [x86_64] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | 24 | - name: Build ${{ matrix.target }} ${{ matrix.arch }} 25 | uses: ./.github/actions/build 26 | with: 27 | sconsflags: ${{ env.SCONSFLAGS }} 28 | arch: ${{ matrix.arch }} 29 | platform: linux 30 | target: ${{ matrix.target }} 31 | -------------------------------------------------------------------------------- /.github/workflows/macos_builds.yml: -------------------------------------------------------------------------------- 1 | name: 🍎 macOS Builds 2 | on: 3 | workflow_call: 4 | 5 | # Global Settings 6 | env: 7 | SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no 8 | 9 | jobs: 10 | ios: 11 | runs-on: "macos-latest" 12 | name: macOS Build ${{ matrix.target }} ${{ matrix.arch }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | target: [template_debug, template_release] 17 | arch: [universal] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | 24 | - name: Build ${{ matrix.target }} ${{ matrix.arch }} 25 | uses: ./.github/actions/build 26 | with: 27 | sconsflags: ${{ env.SCONSFLAGS }} 28 | arch: ${{ matrix.arch }} 29 | platform: macos 30 | target: ${{ matrix.target }} 31 | -------------------------------------------------------------------------------- /.github/workflows/runner.yml: -------------------------------------------------------------------------------- 1 | name: 🔗 Builds 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | static-checks: 6 | name: 📊 Static checks 7 | uses: ./.github/workflows/static_checks.yml 8 | 9 | android-build: 10 | name: 🤖 Android 11 | needs: static-checks 12 | uses: ./.github/workflows/android_builds.yml 13 | 14 | ios-build: 15 | name: 🍏 iOS 16 | needs: static-checks 17 | uses: ./.github/workflows/ios_builds.yml 18 | 19 | linux-build: 20 | name: 🐧 Linux 21 | needs: static-checks 22 | uses: ./.github/workflows/linux_builds.yml 23 | 24 | macos-build: 25 | name: 🍎 macOS 26 | needs: static-checks 27 | uses: ./.github/workflows/macos_builds.yml 28 | 29 | windows-build: 30 | name: 🏁 Windows 31 | needs: static-checks 32 | uses: ./.github/workflows/windows_builds.yml 33 | -------------------------------------------------------------------------------- /.github/workflows/static_checks.yml: -------------------------------------------------------------------------------- 1 | name: 📊 Static Checks 2 | on: 3 | workflow_call: 4 | 5 | jobs: 6 | static-checks: 7 | name: Code style 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | with: 12 | submodules: true 13 | - name: Clang Format 14 | shell: sh 15 | run: ./scripts/clang-format.sh 16 | -------------------------------------------------------------------------------- /.github/workflows/windows_builds.yml: -------------------------------------------------------------------------------- 1 | name: 🏁 Windows Builds 2 | on: 3 | workflow_call: 4 | 5 | # Global Settings 6 | env: 7 | SCONSFLAGS: verbose=yes warnings=extra werror=yes 8 | 9 | jobs: 10 | build-windows: 11 | runs-on: "windows-latest" 12 | name: Windows Build ${{ matrix.target }} ${{ matrix.arch }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | target: [template_debug, template_release] 17 | arch: [x86_32, x86_64] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | - name: Setup MSVC problem matcher 24 | uses: ammaraskar/msvc-problem-matcher@master 25 | 26 | - name: Build ${{ matrix.target }} ${{ matrix.arch }} 27 | uses: ./.github/actions/build 28 | with: 29 | sconsflags: ${{ env.SCONSFLAGS }} 30 | arch: ${{ matrix.arch }} 31 | platform: windows 32 | target: ${{ matrix.target }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | demo/.godot/ 3 | 4 | # Binaries 5 | *.os 6 | *.dblite 7 | 8 | # Generated directories with binaries 9 | demo/bin/ 10 | 11 | .DS_Store 12 | .vscode 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "godot-cpp"] 2 | path = godot-cpp 3 | url = https://github.com/godotengine/godot-cpp 4 | [submodule "box2d"] 5 | path = box2d 6 | url = https://github.com/erincatto/box2d 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present Ricardo Buring and Dragos Daian 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 | # PhysicsServerBox2D 2 | 3 | An unofficial [**Box2D**](https://github.com/erincatto/box2d) physics server for [**Godot Engine**](https://github.com/godotengine/godot) 4.0, implemented as a GDExtension. 4 | 5 | The goal of the project is to be a drop-in solution for 2D physics in Godot 4.0. In your Godot project you can load the GDExtension, change the (advanced) project setting `physics/2d/physics_engine` to `Box2D`, and it will work with Godot's original 2D physics nodes such as `RigidBody2D` and `StaticBody2D`. 6 | 7 | ## Current state 8 | 9 | ⚠ This project is a work in progress, still in a very early stage. ⚠ 10 | 11 | Runtime errors of the form `Required virtual method ... must be overridden before calling` reflect this unfinished state, and they hint at which functionality is still missing. 12 | 13 | ## Building from source 14 | 15 | 1. Clone the git repository https://github.com/rburing/physics_server_box2d, including its `box2d` and `godot-cpp` submodules. 16 | 17 | 2. Open a terminal application and change its working directory to the `physics_server_box2d` git repository. 18 | 19 | 3. Compile `godot-cpp` for the desired `target` (`template_debug` or `template_release`): 20 | 21 | cd godot-cpp 22 | scons target=template_debug generate_bindings=yes 23 | 24 | 4. Compile the GDExtension for the same `target` as above: 25 | 26 | cd .. 27 | scons target=template_debug generate_bindings=no 28 | 29 | *Note*: The `template_debug` target can also be loaded in the Godot editor. 30 | 31 | ## Demo 32 | 33 | The Godot project in the `demo` subdirectory is an example of how to load the GDExtension. 34 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | env = SConscript("godot-cpp/SConstruct") 6 | 7 | box2d_folder = "box2d/" 8 | box2d_include = [ 9 | "include/", 10 | "src/" 11 | ] 12 | box2d_src = [ 13 | "collision/b2_broad_phase.cpp", 14 | "collision/b2_chain_shape.cpp", 15 | "collision/b2_circle_shape.cpp", 16 | "collision/b2_collide_circle.cpp", 17 | "collision/b2_collide_edge.cpp", 18 | "collision/b2_collide_polygon.cpp", 19 | "collision/b2_collision.cpp", 20 | "collision/b2_distance.cpp", 21 | "collision/b2_dynamic_tree.cpp", 22 | "collision/b2_edge_shape.cpp", 23 | "collision/b2_polygon_shape.cpp", 24 | "collision/b2_time_of_impact.cpp", 25 | "common/b2_block_allocator.cpp", 26 | "common/b2_draw.cpp", 27 | "common/b2_math.cpp", 28 | "common/b2_settings.cpp", 29 | "common/b2_stack_allocator.cpp", 30 | "common/b2_timer.cpp", 31 | "dynamics/b2_body.cpp", 32 | "dynamics/b2_chain_circle_contact.cpp", 33 | "dynamics/b2_chain_polygon_contact.cpp", 34 | "dynamics/b2_circle_contact.cpp", 35 | "dynamics/b2_contact.cpp", 36 | "dynamics/b2_contact_manager.cpp", 37 | "dynamics/b2_contact_solver.cpp", 38 | "dynamics/b2_distance_joint.cpp", 39 | "dynamics/b2_edge_circle_contact.cpp", 40 | "dynamics/b2_edge_polygon_contact.cpp", 41 | "dynamics/b2_fixture.cpp", 42 | "dynamics/b2_friction_joint.cpp", 43 | "dynamics/b2_gear_joint.cpp", 44 | "dynamics/b2_island.cpp", 45 | "dynamics/b2_joint.cpp", 46 | "dynamics/b2_motor_joint.cpp", 47 | "dynamics/b2_mouse_joint.cpp", 48 | "dynamics/b2_polygon_circle_contact.cpp", 49 | "dynamics/b2_polygon_contact.cpp", 50 | "dynamics/b2_prismatic_joint.cpp", 51 | "dynamics/b2_pulley_joint.cpp", 52 | "dynamics/b2_revolute_joint.cpp", 53 | "dynamics/b2_weld_joint.cpp", 54 | "dynamics/b2_wheel_joint.cpp", 55 | "dynamics/b2_world.cpp", 56 | "dynamics/b2_world_callbacks.cpp", 57 | "rope/b2_rope.cpp", 58 | ] 59 | 60 | env.Prepend(CPPPATH=[box2d_folder + folder for folder in box2d_include]) 61 | 62 | # For the reference: 63 | # - CCFLAGS are compilation flags shared between C and C++ 64 | # - CFLAGS are for C-specific compilation flags 65 | # - CXXFLAGS are for C++-specific compilation flags 66 | # - CPPFLAGS are for pre-processor flags 67 | # - CPPDEFINES are for pre-processor defines 68 | # - LINKFLAGS are for linking flags 69 | 70 | # Make Box2D include "b2_user_settings.h" 71 | env.Append(CPPDEFINES="B2_USER_SETTINGS") 72 | 73 | # tweak this if you want to use different folders, or more folders, to store your source code in. 74 | env.Append(CPPPATH=["src/"]) 75 | sources = [Glob("src/*.cpp"),Glob("src/bodies/*.cpp"),Glob("src/joints/*.cpp"),Glob("src/servers/*.cpp"),Glob("src/shapes/*.cpp"),Glob("src/spaces/*.cpp")] 76 | sources.extend([box2d_folder + 'src/' + box2d_src_file for box2d_src_file in box2d_src]) 77 | 78 | if env["platform"] == "macos": 79 | library = env.SharedLibrary( 80 | "demo/bin/libphysics_server_box2d.{}.{}.framework/libphysics_server_box2d.{}.{}".format( 81 | env["platform"], env["target"], env["platform"], env["target"] 82 | ), 83 | source=sources, 84 | ) 85 | else: 86 | library = env.SharedLibrary( 87 | "demo/bin/libphysics_server_box2d{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), 88 | source=sources, 89 | ) 90 | 91 | Default(library) 92 | -------------------------------------------------------------------------------- /demo/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d3vm5kkva7oiy" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /demo/physics_server_box2d.gdextension: -------------------------------------------------------------------------------- 1 | [configuration] 2 | 3 | entry_symbol = "physics_server_box2d_library_init" 4 | 5 | [libraries] 6 | 7 | macos.debug = "res://bin/libphysics_server_box2d.macos.template_debug.framework" 8 | macos.release = "res://bin/libphysics_server_box2d.macos.template_release.framework" 9 | ios.debug = "res://bin/libphysics_server_box2d.ios.template_debug.arm64.dylib" 10 | ios.release = "res://bin/libphysics_server_box2d.ios.template_release.arm64.dylib" 11 | windows.debug.x86_32 = "res://bin/libphysics_server_box2d.windows.template_debug.x86_32.dll" 12 | windows.release.x86_32 = "res://bin/libphysics_server_box2d.windows.template_release.x86_32.dll" 13 | windows.debug.x86_64 = "res://bin/libphysics_server_box2d.windows.template_debug.x86_64.dll" 14 | windows.release.x86_64 = "res://bin/libphysics_server_box2d.windows.template_release.x86_64.dll" 15 | linux.debug.x86_64 = "res://bin/libphysics_server_box2d.linux.template_debug.x86_64.so" 16 | linux.release.x86_64 = "res://bin/libphysics_server_box2d.linux.template_release.x86_64.so" 17 | linux.debug.arm64 = "res://bin/libphysics_server_box2d.linux.template_debug.arm64.so" 18 | linux.release.arm64 = "res://bin/libphysics_server_box2d.linux.template_release.arm64.so" 19 | linux.debug.rv64 = "res://bin/libphysics_server_box2d.linux.template_debug.rv64.so" 20 | linux.release.rv64 = "res://bin/libphysics_server_box2d.linux.template_release.rv64.so" 21 | android.debug.x86_64 = "res://bin/libphysics_server_box2d.android.template_debug.x86_64.so" 22 | android.release.x86_64 = "res://bin/libphysics_server_box2d.android.template_release.x86_64.so" 23 | android.debug.arm64 = "res://bin/libphysics_server_box2d.android.template_debug.arm64.so" 24 | android.release.arm64 = "res://bin/libphysics_server_box2d.android.template_release.arm64.so" 25 | -------------------------------------------------------------------------------- /demo/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="PhysicsServerBox2D demo" 14 | run/main_scene="res://world.tscn" 15 | config/features=PackedStringArray("4.0") 16 | config/icon="res://icon.svg" 17 | 18 | [physics] 19 | 20 | 2d/physics_engine="Box2D" 21 | -------------------------------------------------------------------------------- /demo/world.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bkd3jvy83di6t"] 2 | 3 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_0osea"] 4 | size = Vector2(500, 20) 5 | 6 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_ruwi5"] 7 | 8 | [node name="World" type="Node2D"] 9 | 10 | [node name="StaticBody2D" type="StaticBody2D" parent="."] 11 | 12 | [node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D"] 13 | shape = SubResource("RectangleShape2D_0osea") 14 | 15 | [node name="RigidBody2D" type="RigidBody2D" parent="."] 16 | position = Vector2(0, -62) 17 | 18 | [node name="CollisionShape2D" type="CollisionShape2D" parent="RigidBody2D"] 19 | shape = SubResource("RectangleShape2D_ruwi5") 20 | 21 | [node name="Camera2D" type="Camera2D" parent="RigidBody2D"] 22 | -------------------------------------------------------------------------------- /scripts/clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This is based on Godot's clang-format.sh 3 | 4 | # This script runs clang-format on all relevant files in the repo. 5 | # This is the primary script responsible for fixing style violations. 6 | 7 | set -uo pipefail 8 | 9 | # Loop through all code files tracked by Git. 10 | files=$(git ls-files -- '*.h' '*.cpp') 11 | 12 | 13 | if [ ! -z "$files" ]; then 14 | clang-format --Wno-error=unknown -i $files 15 | fi 16 | 17 | diff=$(git diff --color) 18 | 19 | # If no diff has been generated all is OK, clean up, and exit. 20 | if [ -z "$diff" ] ; then 21 | printf "\e[1;32m*** Files in this commit comply with the clang-format style rules.\e[0m\n" 22 | exit 0 23 | fi 24 | 25 | # A diff has been created, notify the user, clean up, and exit. 26 | printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" 27 | # Perl commands replace trailing spaces with `·` and tabs with ``. 28 | printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="" x length($2); sprintf("$1$tabs$3")/ge' 29 | 30 | printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\e[0m\n" 31 | exit 1 32 | -------------------------------------------------------------------------------- /scripts/clang-tidy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This is based on Godot's clang-tidy.sh 3 | 4 | # This script runs clang-tidy on all relevant files in the repo. 5 | # This is more thorough than clang-format and thus slower; it should only be run manually. 6 | 7 | set -uo pipefail 8 | 9 | # Loops through all code files tracked by Git. 10 | 11 | git ls-files -- '*.h' '*.cpp' | 12 | while read -r f; do 13 | # Run clang-tidy. 14 | clang-tidy --quiet --fix "$f" &> /dev/null 15 | 16 | # Run clang-format. This also fixes the output of clang-tidy. 17 | clang-format --Wno-error=unknown -i "$f" 18 | done 19 | 20 | diff=$(git diff --color) 21 | 22 | # If no diff has been generated all is OK, clean up, and exit. 23 | if [ -z "$diff" ] ; then 24 | printf "\e[1;32m*** Files in this commit comply with the clang-tidy style rules.\e[0m\n" 25 | exit 0 26 | fi 27 | 28 | # A diff has been created, notify the user, clean up, and exit. 29 | printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" 30 | # Perl commands replace trailing spaces with `·` and tabs with ``. 31 | printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="" x length($2); sprintf("$1$tabs$3")/ge' 32 | 33 | printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\e[0m\n" 34 | exit 1 35 | -------------------------------------------------------------------------------- /src/b2_user_settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | // Tunable Constants 9 | 10 | /// You can use this to change the length scale used by your game. 11 | /// For example for inches you could use 39.4. 12 | #define b2_lengthUnitsPerMeter 1.0f 13 | 14 | /// The maximum number of vertices on a convex polygon. You cannot increase 15 | /// this too much because b2BlockAllocator has a maximum object size. 16 | #define b2_maxPolygonVertices 8 17 | 18 | // User data 19 | 20 | class Box2DCollisionObject; 21 | 22 | struct B2_API b2BodyUserData { 23 | b2BodyUserData() { 24 | collision_object = nullptr; 25 | } 26 | 27 | Box2DCollisionObject *collision_object; 28 | }; 29 | 30 | struct B2_API b2FixtureUserData { 31 | b2FixtureUserData() { 32 | shape_idx = -1; 33 | box2d_fixture_idx = 0; 34 | } 35 | 36 | int shape_idx; 37 | int box2d_fixture_idx; 38 | }; 39 | 40 | /// You can define this to inject whatever data you want in b2Joint 41 | struct B2_API b2JointUserData { 42 | b2JointUserData() { 43 | pointer = 0; 44 | } 45 | 46 | /// For legacy compatibility 47 | uintptr_t pointer; 48 | }; 49 | 50 | // Memory Allocation using Godot's functions 51 | 52 | inline void *b2Alloc(int32 size) { 53 | return memalloc(size); 54 | } 55 | 56 | inline void b2Free(void *mem) { 57 | memfree(mem); 58 | } 59 | 60 | /// Default logging function 61 | B2_API void b2Log_Default(const char *string, va_list args); 62 | 63 | /// Implement this to use your own logging. 64 | inline void b2Log(const char *string, ...) { 65 | va_list args; 66 | va_start(args, string); 67 | b2Log_Default(string, args); 68 | va_end(args); 69 | } 70 | -------------------------------------------------------------------------------- /src/bodies/box2d_area.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_area.h" 2 | #include "box2d_body.h" 3 | 4 | // Physics Server 5 | void Box2DArea::set_monitorable(bool p_monitorable) { 6 | monitorable = p_monitorable; 7 | } 8 | bool Box2DArea::get_monitorable() { 9 | return monitorable; 10 | } 11 | bool Box2DArea::get_monitoring() { 12 | return area_monitor_callback.is_valid(); 13 | } 14 | void Box2DArea::set_monitor_callback(const Callable &p_callback) { 15 | monitor_callback = p_callback; 16 | } 17 | void Box2DArea::set_area_monitor_callback(const Callable &p_callback) { 18 | area_monitor_callback = p_callback; 19 | } 20 | void Box2DArea::call_area_monitor(Box2DArea *area, PhysicsServer2D::AreaBodyStatus status, const RID &p_area, ObjectID p_instance, int area_shape_idx, int self_shape_idx) { 21 | if (get_monitoring() && area->monitorable) { 22 | area_monitor_callback.callv(Array::make(status, p_area, p_instance, area_shape_idx, self_shape_idx)); 23 | } 24 | } 25 | void Box2DArea::call_monitor(Box2DCollisionObject *body, PhysicsServer2D::AreaBodyStatus status, const RID &p_body, ObjectID p_instance, int32_t area_shape_idx, int32_t self_shape_idx) { 26 | if (monitor_callback.is_valid()) { 27 | monitor_callback.callv(Array::make(status, p_body, p_instance, area_shape_idx, self_shape_idx)); 28 | if (status == PhysicsServer2D::AreaBodyStatus::AREA_BODY_ADDED) { 29 | add_body(body); 30 | } else { 31 | remove_body(body); 32 | } 33 | } 34 | } 35 | 36 | void Box2DArea::set_transform(const Transform2D &p_transform) { 37 | // TODO: add to moved list? 38 | 39 | _set_transform(p_transform); 40 | // _set_inv_transform(p_transform.affine_inverse()); 41 | } 42 | 43 | void Box2DArea::set_space(Box2DSpace *p_space) { 44 | // TODO: remove from monitor query list, remove from moved list? 45 | 46 | //monitored_bodies.clear(); 47 | //monitored_areas.clear(); 48 | 49 | _set_space(p_space); 50 | } 51 | 52 | void Box2DArea::set_priority(real_t p_priority) { 53 | if (collision.priority == p_priority) { 54 | return; 55 | } 56 | collision.priority = p_priority; 57 | for (Box2DCollisionObject *body : bodies) { 58 | body->sort_areas(); 59 | body->recalculate_total_gravity(); 60 | body->recalculate_total_angular_damp(); 61 | body->recalculate_total_linear_damp(); 62 | } 63 | } 64 | 65 | void Box2DArea::set_gravity_override_mode(PhysicsServer2D::AreaSpaceOverrideMode p_value) { 66 | if (gravity.override_mode == p_value) { 67 | return; 68 | } 69 | gravity.override_mode = p_value; 70 | for (Box2DCollisionObject *body : bodies) { 71 | body->recalculate_total_gravity(); 72 | } 73 | } 74 | void Box2DArea::set_gravity(real_t p_value) { 75 | if (gravity.gravity == godot_to_box2d(p_value)) { 76 | return; 77 | } 78 | gravity.gravity = godot_to_box2d(p_value); 79 | for (Box2DCollisionObject *body : bodies) { 80 | body->recalculate_total_gravity(); 81 | } 82 | } 83 | void Box2DArea::set_gravity_vector(Vector2 p_value) { 84 | if (gravity.vector == b2Vec2(p_value.x, p_value.y)) { 85 | return; 86 | } 87 | gravity.vector = b2Vec2(p_value.x, p_value.y); 88 | for (Box2DCollisionObject *body : bodies) { 89 | body->recalculate_total_gravity(); 90 | } 91 | } 92 | void Box2DArea::set_gravity_is_point(bool p_value) { 93 | if (gravity.is_point == p_value) { 94 | return; 95 | } 96 | gravity.is_point = p_value; 97 | for (Box2DCollisionObject *body : bodies) { 98 | body->recalculate_total_gravity(); 99 | } 100 | } 101 | void Box2DArea::set_gravity_point_unit_distance(double p_value) { 102 | if (gravity.point_unit_distance == p_value) { 103 | return; 104 | } 105 | gravity.point_unit_distance = p_value; 106 | for (Box2DCollisionObject *body : bodies) { 107 | body->recalculate_total_gravity(); 108 | } 109 | } 110 | void Box2DArea::set_linear_damp_override_mode(PhysicsServer2D::AreaSpaceOverrideMode p_value) { 111 | if (linear_damp_override_mode == p_value) { 112 | return; 113 | } 114 | linear_damp_override_mode = p_value; 115 | for (Box2DCollisionObject *body : bodies) { 116 | body->recalculate_total_linear_damp(); 117 | } 118 | } 119 | void Box2DArea::set_angular_damp_override_mode(PhysicsServer2D::AreaSpaceOverrideMode p_value) { 120 | if (angular_damp_override_mode == p_value) { 121 | return; 122 | } 123 | angular_damp_override_mode = p_value; 124 | for (Box2DCollisionObject *body : bodies) { 125 | body->recalculate_total_angular_damp(); 126 | } 127 | } 128 | 129 | void Box2DArea::set_linear_damp(real_t p_linear_damp) { 130 | if (damping.linear_damp == p_linear_damp) { 131 | return; 132 | } 133 | damping.linear_damp = p_linear_damp; 134 | for (Box2DCollisionObject *body : bodies) { 135 | body->recalculate_total_linear_damp(); 136 | } 137 | } 138 | void Box2DArea::set_angular_damp(real_t p_angular_damp) { 139 | if (damping.angular_damp == p_angular_damp) { 140 | return; 141 | } 142 | damping.angular_damp = p_angular_damp; 143 | for (Box2DCollisionObject *body : bodies) { 144 | body->recalculate_total_angular_damp(); 145 | } 146 | } 147 | 148 | PhysicsServer2D::AreaSpaceOverrideMode Box2DArea::get_gravity_override_mode() const { 149 | return gravity.override_mode; 150 | } 151 | double Box2DArea::get_gravity() const { 152 | return box2d_to_godot(gravity.gravity); 153 | } 154 | 155 | b2Vec2 Box2DArea::get_b2_gravity(Transform2D transform) const { 156 | if (!gravity.is_point) { 157 | return gravity.gravity * gravity.vector; 158 | } 159 | 160 | const Vector2 point = get_transform().xform(box2d_to_godot(gravity.vector)); 161 | const Vector2 to_point = point - transform.get_origin(); 162 | const float to_point_dist_sq = std::max(to_point.length_squared(), CMP_EPSILON); 163 | const Vector2 to_point_dir = to_point / Math::sqrt(to_point_dist_sq); 164 | 165 | const float gravity_dist_sq = gravity.point_unit_distance * gravity.point_unit_distance; 166 | 167 | return godot_to_box2d((to_point_dir * (get_gravity() * gravity_dist_sq / to_point_dist_sq))); 168 | } 169 | Vector2 Box2DArea::get_gravity_vector() const { 170 | return Vector2(gravity.vector.x, gravity.vector.y); 171 | } 172 | bool Box2DArea::get_gravity_is_point() const { 173 | return gravity.is_point; 174 | } 175 | double Box2DArea::get_gravity_point_unit_distance() const { 176 | return gravity.point_unit_distance; 177 | } 178 | PhysicsServer2D::AreaSpaceOverrideMode Box2DArea::get_linear_damp_override_mode() const { 179 | return linear_damp_override_mode; 180 | } 181 | PhysicsServer2D::AreaSpaceOverrideMode Box2DArea::get_angular_damp_override_mode() const { 182 | return angular_damp_override_mode; 183 | } 184 | void Box2DArea::add_body(Box2DCollisionObject *p_body) { 185 | bodies.append(p_body); 186 | p_body->add_area(this); 187 | } 188 | void Box2DArea::remove_body(Box2DCollisionObject *p_body) { 189 | bodies.erase(p_body); 190 | p_body->remove_area(this); 191 | } 192 | 193 | Box2DArea::Box2DArea() : 194 | Box2DCollisionObject(TYPE_AREA) { 195 | damping.linear_damp = 0.1; 196 | damping.angular_damp = 1; 197 | // areas are sensors and dynamic bodies, but don't move. 198 | // b2_staticBody don't collide with b2_staticBody or b2_kinematicBody 199 | // b2_kinematicBody don't collide with b2_staticBody or b2_kinematicBody 200 | // and areas have to be able to intersect with both kinematic and static bodies 201 | body_def->type = b2_dynamicBody; 202 | //_set_static(true); //areas are not active by default 203 | } 204 | 205 | Box2DArea::~Box2DArea() { 206 | for (Box2DCollisionObject *body : bodies) { 207 | if (body) { 208 | body->remove_area(this); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/bodies/box2d_area.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../spaces/box2d_space.h" 10 | #include "box2d_collision_object.h" 11 | 12 | using namespace godot; 13 | 14 | class Box2DBody; 15 | 16 | class Box2DArea : public Box2DCollisionObject { 17 | bool monitorable = false; 18 | Callable monitor_callback; 19 | Callable area_monitor_callback; 20 | struct Gravity { 21 | PhysicsServer2D::AreaSpaceOverrideMode override_mode = PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_DISABLED; 22 | real_t gravity = 9.8; 23 | b2Vec2 vector = b2Vec2(0, 1); 24 | bool is_point = false; 25 | real_t point_unit_distance = 0; 26 | }; 27 | Gravity gravity; 28 | PhysicsServer2D::AreaSpaceOverrideMode linear_damp_override_mode = PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_DISABLED; 29 | PhysicsServer2D::AreaSpaceOverrideMode angular_damp_override_mode = PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_DISABLED; 30 | Vector bodies; 31 | 32 | void update_bodies(); 33 | 34 | public: 35 | virtual void set_linear_damp(real_t p_linear_damp) override; 36 | virtual void set_angular_damp(real_t p_angular_damp) override; 37 | virtual void set_priority(real_t p_priority) override; 38 | // Physics Server 39 | void set_monitorable(bool monitorable); 40 | bool get_monitorable(); 41 | bool get_monitoring(); 42 | void set_monitor_callback(const Callable &callback); 43 | void set_area_monitor_callback(const Callable &callback); 44 | void call_area_monitor(Box2DArea *area, PhysicsServer2D::AreaBodyStatus status, const RID &p_area, ObjectID p_instance, int area_shape_idx, int self_shape_idx); 45 | void call_monitor(Box2DCollisionObject *body, PhysicsServer2D::AreaBodyStatus status, const RID &p_body, ObjectID p_instance, int32_t area_shape_idx, int32_t self_shape_idx); 46 | 47 | virtual void set_transform(const Transform2D &p_transform) override; 48 | 49 | virtual void set_space(Box2DSpace *p_space) override; 50 | 51 | void set_gravity_override_mode(PhysicsServer2D::AreaSpaceOverrideMode p_value); 52 | void set_gravity(real_t p_value); 53 | void set_gravity_vector(Vector2 p_value); 54 | void set_gravity_is_point(bool p_value); 55 | void set_gravity_point_unit_distance(double p_value); 56 | void set_linear_damp_override_mode(PhysicsServer2D::AreaSpaceOverrideMode p_value); 57 | void set_angular_damp_override_mode(PhysicsServer2D::AreaSpaceOverrideMode p_value); 58 | 59 | PhysicsServer2D::AreaSpaceOverrideMode get_gravity_override_mode() const; 60 | double get_gravity() const; 61 | b2Vec2 get_b2_gravity(Transform2D transform) const; 62 | Vector2 get_gravity_vector() const; 63 | bool get_gravity_is_point() const; 64 | double get_gravity_point_unit_distance() const; 65 | PhysicsServer2D::AreaSpaceOverrideMode get_linear_damp_override_mode() const; 66 | PhysicsServer2D::AreaSpaceOverrideMode get_angular_damp_override_mode() const; 67 | 68 | void add_body(Box2DCollisionObject *p_body); 69 | void remove_body(Box2DCollisionObject *p_body); 70 | 71 | Box2DArea(); 72 | ~Box2DArea(); 73 | }; 74 | -------------------------------------------------------------------------------- /src/bodies/box2d_body.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_body.h" 2 | #include "../box2d_type_conversions.h" 3 | #include "box2d_direct_body_state.h" 4 | 5 | bool Box2DBody::is_active() const { return active; } 6 | 7 | // Physics Server 8 | 9 | void Box2DBody::set_max_contacts_reported(int32 p_max_contacts_reported) { 10 | max_contacts_reported = p_max_contacts_reported; 11 | } 12 | 13 | int32 Box2DBody::get_max_contacts_reported() { 14 | return max_contacts_reported; 15 | } 16 | 17 | void Box2DBody::wakeup() { 18 | if ((!get_space()) || mode == PhysicsServer2D::BODY_MODE_STATIC || mode == PhysicsServer2D::BODY_MODE_KINEMATIC) { 19 | return; 20 | } 21 | set_active(true); 22 | } 23 | 24 | void Box2DBody::set_state_sync_callback(const Callable &p_callable) { 25 | body_state_callback = p_callable; 26 | } 27 | 28 | Box2DDirectBodyState *Box2DBody::get_direct_state() { 29 | if (!direct_state) { 30 | direct_state = memnew(Box2DDirectBodyState); 31 | direct_state->body = this; 32 | } 33 | return direct_state; 34 | } 35 | 36 | void Box2DBody::set_active(bool p_active) { 37 | if (active == p_active) { 38 | return; 39 | } 40 | 41 | active = p_active; 42 | 43 | if (active) { 44 | if (mode == PhysicsServer2D::BODY_MODE_STATIC) { 45 | // Static bodies can't be active. 46 | active = false; 47 | } else if (get_space()) { 48 | set_sleep_state(true); 49 | get_space()->body_add_to_active_list(&active_list); 50 | } 51 | } else if (get_space()) { 52 | set_sleep_state(false); 53 | get_space()->body_remove_from_active_list(&active_list); 54 | } 55 | } 56 | 57 | void Box2DBody::set_mode(PhysicsServer2D::BodyMode p_mode) { 58 | if (mode == p_mode) { 59 | return; 60 | } 61 | mode = p_mode; 62 | switch (p_mode) { 63 | case PhysicsServer2D::BODY_MODE_STATIC: { 64 | // TODO: other stuff 65 | body_def->type = b2_staticBody; 66 | body_def->fixedRotation = false; 67 | set_active(false); 68 | } break; 69 | case PhysicsServer2D::BODY_MODE_KINEMATIC: { 70 | // TODO: other stuff 71 | body_def->type = b2_kinematicBody; 72 | body_def->fixedRotation = false; 73 | set_active(true); // TODO: consider contacts 74 | } break; 75 | case PhysicsServer2D::BODY_MODE_RIGID: { 76 | body_def->type = b2_dynamicBody; 77 | body_def->fixedRotation = false; 78 | set_active(true); 79 | } break; 80 | case PhysicsServer2D::BODY_MODE_RIGID_LINEAR: { 81 | // TODO: (inverse) mass calculation? 82 | //_set_static(false); 83 | body_def->type = b2_dynamicBody; 84 | body_def->fixedRotation = true; 85 | set_active(true); 86 | } break; 87 | } 88 | if (body) { 89 | body->SetType(body_def->type); 90 | body->SetFixedRotation(body_def->fixedRotation); 91 | body->SetMassData(&mass_data); 92 | } 93 | } 94 | 95 | PhysicsServer2D::BodyMode Box2DBody::get_mode() const { 96 | return mode; 97 | } 98 | 99 | void Box2DBody::set_state(PhysicsServer2D::BodyState p_state, const Variant &p_variant) { 100 | switch (p_state) { 101 | case PhysicsServer2D::BODY_STATE_TRANSFORM: { 102 | if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC) { 103 | // TODO 104 | } else if (mode == PhysicsServer2D::BODY_MODE_STATIC) { 105 | _set_transform(p_variant); 106 | //_set_inv_transform(get_transform().affine_inverse()); 107 | //wakeup_neighbours(); 108 | } else { // rigid body 109 | Transform2D t = p_variant; 110 | t.orthonormalize(); 111 | new_transform = get_transform(); // used as old to compute motion 112 | if (t == new_transform) { 113 | break; 114 | } 115 | _set_transform(t); 116 | //_set_inv_transform(get_transform().inverse()); 117 | //_update_transform_dependent(); 118 | } 119 | wakeup(); 120 | } break; 121 | case PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY: { 122 | Vector2 linear_velocity = p_variant; 123 | set_linear_velocity(linear_velocity); 124 | wakeup(); 125 | } break; 126 | case PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY: { 127 | float angular_velocity = godot_to_box2d(variant_to_number(p_variant)); 128 | set_angular_velocity(angular_velocity); 129 | wakeup(); 130 | } break; 131 | case PhysicsServer2D::BODY_STATE_SLEEPING: { 132 | if (mode == PhysicsServer2D::BODY_MODE_STATIC || mode == PhysicsServer2D::BODY_MODE_KINEMATIC) { 133 | break; 134 | } 135 | bool do_sleep = p_variant; 136 | if (do_sleep) { 137 | set_linear_velocity(Vector2()); 138 | set_angular_velocity(0); 139 | set_active(false); 140 | } else { 141 | if (mode != PhysicsServer2D::BODY_MODE_STATIC) { 142 | set_active(true); 143 | } 144 | } 145 | } break; 146 | case PhysicsServer2D::BODY_STATE_CAN_SLEEP: { 147 | can_sleep = p_variant; 148 | if (body) { 149 | body->SetSleepingAllowed(can_sleep); 150 | } 151 | if (mode >= PhysicsServer2D::BODY_MODE_RIGID && !active && !can_sleep) { 152 | set_active(true); 153 | } 154 | } break; 155 | } 156 | } 157 | 158 | Variant Box2DBody::get_state(PhysicsServer2D::BodyState p_state) const { 159 | switch (p_state) { 160 | case PhysicsServer2D::BODY_STATE_TRANSFORM: { 161 | return get_transform(); 162 | } break; 163 | case PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY: { 164 | return get_linear_velocity(); 165 | } break; 166 | case PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY: { 167 | return get_angular_velocity(); 168 | } break; 169 | case PhysicsServer2D::BODY_STATE_SLEEPING: { 170 | return !is_active(); 171 | } 172 | case PhysicsServer2D::BODY_STATE_CAN_SLEEP: { 173 | return can_sleep; 174 | } 175 | } 176 | return Variant(); 177 | } 178 | 179 | void Box2DBody::set_space(Box2DSpace *p_space) { 180 | if (get_space()) { 181 | // TODO: clean up more 182 | if (active_list.in_list()) { 183 | get_space()->body_remove_from_active_list(&active_list); 184 | } 185 | if (direct_state_query_list.in_list()) { 186 | get_space()->body_remove_from_state_query_list(&direct_state_query_list); 187 | } 188 | } 189 | 190 | _set_space(p_space); 191 | 192 | if (get_space()) { 193 | // TODO: do more 194 | if (body) { 195 | body->SetAwake(active); 196 | } 197 | if (active) { 198 | get_space()->body_add_to_active_list(&active_list); 199 | } 200 | } 201 | } 202 | 203 | void Box2DBody::after_step() { 204 | if (body_state_callback.is_valid()) { 205 | get_space()->body_add_to_state_query_list(&direct_state_query_list); 206 | } 207 | } 208 | 209 | void Box2DBody::call_queries() { 210 | Variant direct_state = get_direct_state(); 211 | if (body_state_callback.is_valid()) { 212 | body_state_callback.callv(Array::make(direct_state)); 213 | } 214 | } 215 | 216 | void Box2DBody::set_continuous_collision_detection_mode(PhysicsServer2D::CCDMode p_mode) { 217 | if (collision_mode == p_mode) { 218 | return; 219 | } 220 | collision_mode = p_mode; 221 | switch (collision_mode) { 222 | case PhysicsServer2D::CCD_MODE_DISABLED: { 223 | body_def->bullet = false; 224 | } break; 225 | case PhysicsServer2D::CCD_MODE_CAST_RAY: 226 | case PhysicsServer2D::CCD_MODE_CAST_SHAPE: 227 | body_def->bullet = true; 228 | break; 229 | } 230 | if (body) { 231 | body->SetBullet(body_def->bullet); 232 | } 233 | } 234 | PhysicsServer2D::CCDMode Box2DBody::get_continuous_collision_detection_mode() const { 235 | return collision_mode; 236 | } 237 | 238 | void Box2DBody::add_joint(Box2DJoint *p_joint) { 239 | joints.insert(p_joint); 240 | } 241 | void Box2DBody::remove_joint(Box2DJoint *p_joint) { 242 | joints.erase(p_joint); 243 | } 244 | 245 | HashSet Box2DBody::get_joints() { 246 | return joints; 247 | } 248 | 249 | Box2DBody::Box2DBody() : 250 | Box2DCollisionObject(TYPE_BODY), 251 | active_list(this), 252 | direct_state_query_list(this) { 253 | } 254 | 255 | Box2DBody::~Box2DBody() { 256 | if (direct_state) { 257 | memdelete(direct_state); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/bodies/box2d_body.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../joints/box2d_joint.h" 10 | #include "../spaces/box2d_space.h" 11 | #include "box2d_collision_object.h" 12 | 13 | using namespace godot; 14 | 15 | class Box2DDirectBodyState; 16 | 17 | class Box2DBody : public Box2DCollisionObject { 18 | PhysicsServer2D::BodyMode mode = PhysicsServer2D::BODY_MODE_RIGID; 19 | 20 | SelfList active_list; 21 | SelfList direct_state_query_list; 22 | 23 | bool active = true; 24 | bool can_sleep = true; 25 | PhysicsServer2D::CCDMode collision_mode = PhysicsServer2D::CCD_MODE_DISABLED; 26 | 27 | Transform2D new_transform; 28 | 29 | Callable body_state_callback; 30 | 31 | Box2DDirectBodyState *direct_state = nullptr; 32 | HashSet joints; 33 | int32 max_contacts_reported = 0; 34 | 35 | public: 36 | // Physics Server 37 | void set_max_contacts_reported(int32 p_max_contacts_reported); 38 | 39 | int32 get_max_contacts_reported(); 40 | 41 | void set_space(Box2DSpace *p_space) override; 42 | 43 | void set_state_sync_callback(const Callable &p_callable); 44 | 45 | Box2DDirectBodyState *get_direct_state(); 46 | 47 | void set_active(bool p_active); 48 | bool is_active() const; 49 | 50 | void wakeup(); 51 | 52 | void set_mode(PhysicsServer2D::BodyMode p_mode); 53 | PhysicsServer2D::BodyMode get_mode() const; 54 | 55 | void set_state(PhysicsServer2D::BodyState p_state, const Variant &p_variant); 56 | Variant get_state(PhysicsServer2D::BodyState p_state) const; 57 | 58 | void set_continuous_collision_detection_mode(PhysicsServer2D::CCDMode mode); 59 | PhysicsServer2D::CCDMode get_continuous_collision_detection_mode() const; 60 | 61 | void add_joint(Box2DJoint *p_joint); 62 | void remove_joint(Box2DJoint *p_joint); 63 | 64 | virtual HashSet get_joints() override; 65 | 66 | void after_step(); 67 | void call_queries(); 68 | 69 | Box2DBody(); 70 | ~Box2DBody(); 71 | }; 72 | -------------------------------------------------------------------------------- /src/bodies/box2d_collision_object.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_collision_object.h" 2 | 3 | #include "../b2_user_settings.h" 4 | 5 | #include "../box2d_type_conversions.h" 6 | #include "../spaces/box2d_direct_space_state.h" 7 | #include "box2d_area.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | // Mass 15 | 16 | void Box2DCollisionObject::reset_mass_properties() { 17 | mass_data.mass = 1; 18 | mass_data.I = 0; 19 | mass_data.center = b2Vec2(); 20 | if (body) { 21 | body->SetMassData(&mass_data); 22 | } 23 | } 24 | 25 | void Box2DCollisionObject::set_mass(real_t p_mass) { 26 | if (mass_data.mass == p_mass) { 27 | return; 28 | } 29 | mass_data.mass = p_mass; 30 | if (body) { 31 | body->SetMassData(&mass_data); 32 | } 33 | } 34 | double Box2DCollisionObject::get_mass() const { 35 | return mass_data.mass; // no need to convert 36 | } 37 | void Box2DCollisionObject::set_inertia(real_t p_inertia) { 38 | if (mass_data.I == p_inertia) { 39 | return; 40 | } 41 | mass_data.I = p_inertia; 42 | if (body) { 43 | body->SetMassData(&mass_data); 44 | } 45 | } 46 | double Box2DCollisionObject::get_inertia() const { 47 | return mass_data.I; // no need to convert 48 | } 49 | void Box2DCollisionObject::set_center_of_mass(Vector2 p_center_of_mass) { 50 | if (godot_to_box2d(p_center_of_mass) == mass_data.center) { 51 | return; 52 | } 53 | godot_to_box2d(p_center_of_mass, mass_data.center); 54 | if (body) { 55 | body->SetMassData(&mass_data); 56 | } 57 | } 58 | Vector2 Box2DCollisionObject::get_center_of_mass() const { 59 | if (body) { 60 | return box2d_to_godot(mass_data.center + body->GetPosition()); 61 | } else { 62 | return box2d_to_godot(mass_data.center + body_def->position); 63 | } 64 | } 65 | 66 | // Damping 67 | 68 | void Box2DCollisionObject::set_linear_damp_mode(PhysicsServer2D::BodyDampMode p_linear_damp) { 69 | if (damping.linear_damp_mode == p_linear_damp) { 70 | return; 71 | } 72 | damping.linear_damp_mode = p_linear_damp; 73 | recalculate_total_linear_damp(); 74 | } 75 | void Box2DCollisionObject::set_angular_damp_mode(PhysicsServer2D::BodyDampMode p_angular_damp) { 76 | if (damping.angular_damp_mode == p_angular_damp) { 77 | return; 78 | } 79 | damping.angular_damp_mode = p_angular_damp; 80 | recalculate_total_angular_damp(); 81 | } 82 | 83 | void Box2DCollisionObject::set_linear_damp(real_t p_linear_damp) { 84 | if (damping.linear_damp == p_linear_damp) { 85 | return; 86 | } 87 | damping.linear_damp = p_linear_damp; 88 | recalculate_total_linear_damp(); 89 | } 90 | void Box2DCollisionObject::set_angular_damp(real_t p_angular_damp) { 91 | if (damping.angular_damp == p_angular_damp) { 92 | return; 93 | } 94 | damping.angular_damp = p_angular_damp; 95 | recalculate_total_angular_damp(); 96 | } 97 | PhysicsServer2D::BodyDampMode Box2DCollisionObject::get_linear_damp_mode() const { 98 | return damping.linear_damp_mode; 99 | } 100 | PhysicsServer2D::BodyDampMode Box2DCollisionObject::get_angular_damp_mode() const { 101 | return damping.angular_damp_mode; 102 | } 103 | double Box2DCollisionObject::get_linear_damp() const { 104 | return damping.linear_damp; 105 | } 106 | double Box2DCollisionObject::get_angular_damp() const { 107 | return damping.angular_damp; 108 | } 109 | 110 | void Box2DCollisionObject::recalculate_total_linear_damp() { 111 | total_linear_damp = damping.linear_damp; 112 | if (get_linear_damp_mode() == PhysicsServer2D::BodyDampMode::BODY_DAMP_MODE_REPLACE) { 113 | body_def->linearDamping = total_linear_damp; 114 | if (body) { 115 | body->SetLinearDamping(body_def->linearDamping); 116 | } 117 | // replace linear damp with body one 118 | return; 119 | } 120 | bool keep_computing = true; 121 | for (Box2DArea *area : areas) { 122 | if (!keep_computing) { 123 | break; 124 | } 125 | real_t linear_damp = area->damping.linear_damp; 126 | switch (area->get_linear_damp_override_mode()) { 127 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_COMBINE: { 128 | total_linear_damp += linear_damp; 129 | } break; 130 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: { 131 | total_linear_damp += linear_damp; 132 | keep_computing = false; 133 | } break; 134 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_REPLACE: { 135 | total_linear_damp = linear_damp; 136 | keep_computing = false; 137 | } break; 138 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: { 139 | total_linear_damp = linear_damp; 140 | } break; 141 | default: { 142 | } 143 | } 144 | } 145 | body_def->linearDamping = total_linear_damp; 146 | if (body) { 147 | body->SetLinearDamping(body_def->linearDamping); 148 | } 149 | } 150 | 151 | void Box2DCollisionObject::recalculate_total_angular_damp() { 152 | total_angular_damp = damping.angular_damp; 153 | if (get_angular_damp_mode() == PhysicsServer2D::BodyDampMode::BODY_DAMP_MODE_REPLACE) { 154 | body_def->angularDamping = total_angular_damp; 155 | if (body) { 156 | body->SetAngularDamping(body_def->angularDamping); 157 | } 158 | // replace angular damp with body one 159 | return; 160 | } 161 | // compute angular damp from areas 162 | bool keep_computing = true; 163 | for (Box2DArea *area : areas) { 164 | if (!keep_computing) { 165 | break; 166 | } 167 | real_t angular_damp = area->damping.angular_damp; 168 | switch (area->get_angular_damp_override_mode()) { 169 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_COMBINE: { 170 | total_angular_damp += angular_damp; 171 | } break; 172 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: { 173 | total_angular_damp += angular_damp; 174 | keep_computing = false; 175 | } break; 176 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_REPLACE: { 177 | total_angular_damp = angular_damp; 178 | keep_computing = false; 179 | } break; 180 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: { 181 | total_angular_damp = angular_damp; 182 | } break; 183 | default: { 184 | } 185 | } 186 | } 187 | body_def->angularDamping = total_angular_damp; 188 | if (body) { 189 | body->SetAngularDamping(body_def->angularDamping); 190 | } 191 | } 192 | 193 | // Collision 194 | 195 | void Box2DCollisionObject::set_priority(real_t p_priority) { 196 | collision.priority = p_priority; 197 | } 198 | 199 | double Box2DCollisionObject::get_priority() const { 200 | return collision.priority; 201 | } 202 | 203 | // Physics Material 204 | 205 | void Box2DCollisionObject::set_bounce(real_t p_bounce) { 206 | if (physics_material.bounce == p_bounce) { 207 | return; 208 | } 209 | physics_material.bounce = p_bounce; 210 | if (body) { 211 | _update_shapes(); 212 | } 213 | } 214 | void Box2DCollisionObject::set_friction(real_t p_friction) { 215 | if (physics_material.friction == p_friction) { 216 | return; 217 | } 218 | physics_material.friction = p_friction; 219 | if (body) { 220 | _update_shapes(); 221 | } 222 | } 223 | 224 | double Box2DCollisionObject::get_bounce() const { 225 | return physics_material.bounce; 226 | } 227 | double Box2DCollisionObject::get_friction() const { 228 | return physics_material.friction; 229 | } 230 | 231 | // Direct Body API 232 | 233 | Vector2 Box2DCollisionObject::get_total_gravity() const { 234 | return Vector2(total_gravity.x, total_gravity.y); 235 | } 236 | 237 | double Box2DCollisionObject::get_total_linear_damp() const { 238 | return box2d_to_godot(body_def->linearDamping); 239 | } 240 | 241 | double Box2DCollisionObject::get_total_angular_damp() const { 242 | return box2d_to_godot(body_def->linearDamping); 243 | } 244 | 245 | Vector2 Box2DCollisionObject::get_center_of_mass_local() const { 246 | return box2d_to_godot(mass_data.center); 247 | } 248 | 249 | double Box2DCollisionObject::get_inverse_mass() const { 250 | if (mass_data.mass <= 0) { 251 | return 0; 252 | } 253 | return 1.0 / mass_data.mass; 254 | } 255 | double Box2DCollisionObject::get_inverse_inertia() const { 256 | if (mass_data.I <= 0) { 257 | return 0; 258 | } 259 | return 1.0 / mass_data.I; 260 | } 261 | void Box2DCollisionObject::set_linear_velocity(const Vector2 &p_linear_velocity) { 262 | b2Vec2 box2d_linear_velocity = godot_to_box2d(p_linear_velocity); 263 | body_def->linearVelocity = box2d_linear_velocity; 264 | if (body) { 265 | body->SetLinearVelocity(box2d_linear_velocity); 266 | } 267 | } 268 | 269 | Vector2 Box2DCollisionObject::get_linear_velocity() const { 270 | if (body) { 271 | return box2d_to_godot(body->GetLinearVelocity()); 272 | } 273 | return Vector2(); 274 | } 275 | void Box2DCollisionObject::set_angular_velocity(double p_velocity) { 276 | float angularVelocity = godot_to_box2d(p_velocity); 277 | body_def->angularVelocity = angularVelocity; 278 | if (body) { 279 | body->SetAngularVelocity(angularVelocity); 280 | } 281 | } 282 | double Box2DCollisionObject::get_angular_velocity() const { 283 | if (body) { 284 | return box2d_to_godot(body->GetAngularVelocity()); 285 | } 286 | return 0; 287 | } 288 | void Box2DCollisionObject::set_transform(const Transform2D &transform) { 289 | _set_transform(transform); 290 | } 291 | Transform2D Box2DCollisionObject::get_transform() const { 292 | if (body) { 293 | return Transform2D(body->GetAngle(), box2d_to_godot(body->GetPosition())); 294 | } else { 295 | return Transform2D(body_def->angle, box2d_to_godot(body_def->position)); 296 | } 297 | } 298 | Vector2 Box2DCollisionObject::get_velocity_at_local_position(const Vector2 &p_local_position) const { 299 | if (body) { 300 | b2Vec2 velocity = body->GetLinearVelocityFromLocalPoint(godot_to_box2d(p_local_position)); 301 | return box2d_to_godot(velocity); 302 | } 303 | return Vector2(); 304 | } 305 | void Box2DCollisionObject::apply_central_impulse(const Vector2 &impulse) { 306 | if (body) { 307 | body->ApplyLinearImpulseToCenter(body->GetMass() * godot_to_box2d(impulse), true); 308 | } 309 | } 310 | void Box2DCollisionObject::apply_impulse(const Vector2 &impulse, const Vector2 &position) { 311 | if (body) { 312 | body->ApplyLinearImpulse(body->GetMass() * godot_to_box2d(impulse), godot_to_box2d(position), true); 313 | } 314 | } 315 | void Box2DCollisionObject::apply_torque_impulse(double impulse) { 316 | if (body) { 317 | body->ApplyTorque(body->GetMass() * godot_to_box2d(impulse), true); 318 | } 319 | } 320 | void Box2DCollisionObject::apply_central_force(const Vector2 &force) { 321 | if (body) { 322 | body->ApplyForceToCenter(body->GetMass() * godot_to_box2d(force), true); 323 | } 324 | } 325 | void Box2DCollisionObject::apply_force(const Vector2 &force, const Vector2 &position) { 326 | if (body) { 327 | body->ApplyForce(body->GetMass() * godot_to_box2d(force), godot_to_box2d(position), true); 328 | } 329 | } 330 | void Box2DCollisionObject::apply_torque(double torque) { 331 | if (body) { 332 | body->ApplyTorque(body->GetMass() * godot_to_box2d(torque), true); 333 | } 334 | } 335 | 336 | // Constant Forces 337 | 338 | void Box2DCollisionObject::add_constant_central_force(const Vector2 &force) { 339 | constant_forces.constant_force += godot_to_box2d(force); 340 | // TODO set position to center 341 | //constant_force_position = position; 342 | } 343 | void Box2DCollisionObject::add_constant_force(const Vector2 &force, const Vector2 &position) { 344 | constant_forces.constant_force += godot_to_box2d(force); 345 | constant_forces.constant_force_position = godot_to_box2d(position); 346 | } 347 | void Box2DCollisionObject::add_constant_torque(double torque) { 348 | constant_forces.constant_torque += godot_to_box2d(torque); 349 | } 350 | void Box2DCollisionObject::set_constant_force(const Vector2 &force) { 351 | constant_forces.constant_force = godot_to_box2d(force); 352 | } 353 | Vector2 Box2DCollisionObject::get_constant_force() const { 354 | return box2d_to_godot(constant_forces.constant_force); 355 | } 356 | void Box2DCollisionObject::set_constant_torque(double torque) { 357 | constant_forces.constant_torque = godot_to_box2d(torque); 358 | } 359 | double Box2DCollisionObject::get_constant_torque() const { 360 | return box2d_to_godot(constant_forces.constant_torque); 361 | } 362 | void Box2DCollisionObject::set_sleep_state(bool enabled) { 363 | if (body) { 364 | body->SetAwake(!enabled); 365 | } 366 | } 367 | bool Box2DCollisionObject::is_sleeping() const { 368 | return !body->IsAwake(); 369 | } 370 | Box2DCollisionObject::ContactEdgeData Box2DCollisionObject::_get_contact_edge_data(int32_t contact_idx) const { 371 | if (!body) { 372 | return ContactEdgeData(); 373 | } 374 | b2ContactEdge *contacts = body->GetContactList(); 375 | int32 contacts_count = 0; 376 | b2WorldManifold worldManifold; 377 | while (contacts) { 378 | contacts_count += contacts->contact->GetManifold()->pointCount; 379 | if (contacts_count > contact_idx) { 380 | return ContactEdgeData{ contacts, contacts_count - contact_idx - 1 }; 381 | } 382 | contacts = contacts->next; 383 | } 384 | return ContactEdgeData(); 385 | } 386 | 387 | int32_t Box2DCollisionObject::get_contact_count() const { 388 | if (!body) { 389 | return 0; 390 | } 391 | b2ContactEdge *contacts = body->GetContactList(); 392 | int32 contacts_count = 0; 393 | while (contacts) { 394 | contacts_count += contacts->contact->GetManifold()->pointCount; 395 | contacts = contacts->next; 396 | } 397 | return contacts_count; 398 | } 399 | Vector2 Box2DCollisionObject::get_contact_local_position(int32_t contact_idx) const { 400 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 401 | if (!data.edge) { 402 | return Vector2(); 403 | } 404 | b2WorldManifold worldManifold; 405 | data.edge->contact->GetWorldManifold(&worldManifold); 406 | return box2d_to_godot(worldManifold.points[data.point_idx]); 407 | } 408 | Vector2 Box2DCollisionObject::get_contact_local_normal(int32_t contact_idx) const { 409 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 410 | if (!data.edge) { 411 | return Vector2(); 412 | } 413 | b2Vec2 normal = data.edge->contact->GetManifold()->localNormal; 414 | return Vector2(normal.x, normal.y); 415 | } 416 | int32_t Box2DCollisionObject::get_contact_local_shape(int32_t contact_idx) const { 417 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 418 | if (!data.edge) { 419 | return -1; 420 | } 421 | return data.edge->contact->GetFixtureA()->GetUserData().shape_idx; 422 | } 423 | RID Box2DCollisionObject::get_contact_collider(int32_t contact_idx) const { 424 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 425 | if (!data.edge) { 426 | return RID(); 427 | } 428 | b2BodyUserData *user_data = (b2BodyUserData *)&data.edge->other->GetUserData(); 429 | return user_data->collision_object->get_self(); 430 | } 431 | Vector2 Box2DCollisionObject::get_contact_collider_position(int32_t contact_idx) const { 432 | return get_contact_local_position(contact_idx); 433 | } 434 | uint64_t Box2DCollisionObject::get_contact_collider_id(int32_t contact_idx) const { 435 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 436 | if (!data.edge) { 437 | return 0; 438 | } 439 | b2BodyUserData *user_data = (b2BodyUserData *)&data.edge->other->GetUserData(); 440 | return user_data->collision_object->get_object_instance_id(); 441 | } 442 | Object *Box2DCollisionObject::get_contact_collider_object(int32_t contact_idx) const { 443 | ObjectID id = ObjectID(get_contact_collider_id(contact_idx)); 444 | return ObjectDB::get_instance(id); 445 | } 446 | int32_t Box2DCollisionObject::get_contact_collider_shape(int32_t contact_idx) const { 447 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 448 | if (!data.edge) { 449 | return -1; 450 | } 451 | return data.edge->contact->GetFixtureB()->GetUserData().shape_idx; 452 | } 453 | Vector2 Box2DCollisionObject::get_contact_collider_velocity_at_position(int32_t contact_idx) const { 454 | ContactEdgeData data = _get_contact_edge_data(contact_idx); 455 | if (!data.edge) { 456 | return Vector2(); 457 | } 458 | b2WorldManifold worldManifold; 459 | data.edge->contact->GetWorldManifold(&worldManifold); 460 | b2Vec2 world_point = worldManifold.points[data.point_idx]; 461 | return box2d_to_godot(data.edge->other->GetLinearVelocityFromWorldPoint(world_point)); 462 | } 463 | Vector2 Box2DCollisionObject::get_contact_impulse(int32_t contact_idx) const { 464 | return get_contact_local_normal(contact_idx) * get_step(); 465 | } 466 | double Box2DCollisionObject::get_step() const { 467 | Box2DSpace *space = get_space(); 468 | if (!space) { 469 | return 0; 470 | } 471 | return space->get_step(); 472 | } 473 | void Box2DCollisionObject::integrate_forces() { 474 | } 475 | 476 | PhysicsDirectSpaceState2D *Box2DCollisionObject::get_space_state() { 477 | if (!direct_space) { 478 | direct_space = memnew(Box2DDirectSpaceState); 479 | direct_space->space = space; 480 | } 481 | return direct_space; 482 | } 483 | 484 | // Physics Server 485 | 486 | void Box2DCollisionObject::set_collision_layer(uint32_t layer) { 487 | filter.categoryBits = layer; 488 | if (body) { 489 | _update_shapes(); 490 | } 491 | } 492 | 493 | uint32_t Box2DCollisionObject::get_collision_layer() const { 494 | return filter.categoryBits; 495 | } 496 | void Box2DCollisionObject::set_collision_mask(uint32_t layer) { 497 | filter.maskBits = layer; 498 | if (body) { 499 | _update_shapes(); 500 | } 501 | } 502 | 503 | uint32_t Box2DCollisionObject::get_collision_mask() const { 504 | return filter.maskBits; 505 | } 506 | 507 | void Box2DCollisionObject::set_pickable(bool p_pickable) { 508 | collision.pickable = p_pickable; 509 | if (body) { 510 | _update_shapes(); 511 | } 512 | } 513 | 514 | void Box2DCollisionObject::set_object_instance_id(const ObjectID &p_instance_id) { 515 | object_instance_id = p_instance_id; 516 | } 517 | ObjectID Box2DCollisionObject::get_object_instance_id() const { return object_instance_id; } 518 | 519 | Object *Box2DCollisionObject::get_object() const { 520 | ObjectID id = ObjectID(object_instance_id); 521 | return ObjectDB::get_instance(id); 522 | } 523 | Object *Box2DCollisionObject::get_object_unsafe() const { 524 | return reinterpret_cast(internal::gde_interface->object_get_instance_from_id(object_instance_id)); 525 | ; 526 | } 527 | 528 | void Box2DCollisionObject::set_canvas_instance_id(const ObjectID &p_instance_id) { 529 | canvas_instance_id = p_instance_id; 530 | } 531 | ObjectID Box2DCollisionObject::get_canvas_instance_id() const { return canvas_instance_id; } 532 | 533 | Box2DSpace *Box2DCollisionObject::get_space() const { return space; } 534 | 535 | void Box2DCollisionObject::add_shape(Box2DShape *p_shape, const Transform2D &p_transform, bool p_disabled) { 536 | Shape s; 537 | s.shape = p_shape; 538 | s.xform = p_transform; 539 | s.disabled = p_disabled; 540 | shapes.push_back(s); 541 | 542 | // TODO (queue) update 543 | } 544 | 545 | void Box2DCollisionObject::set_shape(int p_index, Box2DShape *p_shape) { 546 | ERR_FAIL_INDEX(p_index, shapes.size()); 547 | //shapes[p_index].shape->remove_owner(this); 548 | shapes.write[p_index].shape = p_shape; 549 | 550 | // TODO: (queue) update 551 | } 552 | 553 | void Box2DCollisionObject::set_shape_transform(int p_index, const Transform2D &p_transform) { 554 | ERR_FAIL_INDEX(p_index, shapes.size()); 555 | 556 | shapes.write[p_index].xform = p_transform; 557 | 558 | // TODO: (queue) update 559 | } 560 | 561 | void Box2DCollisionObject::set_shape_disabled(int p_index, bool p_disabled) { 562 | ERR_FAIL_INDEX(p_index, shapes.size()); 563 | 564 | Shape &shape = shapes.write[p_index]; 565 | if (shape.disabled == p_disabled) { 566 | return; 567 | } 568 | 569 | shape.disabled = p_disabled; 570 | 571 | if (!space) { 572 | return; 573 | } 574 | 575 | for (int j = 0; j < shape.fixtures.size(); j++) { 576 | if (body) { 577 | body->DestroyFixture(shape.fixtures[j]); 578 | } 579 | shape.fixtures.write[j] = nullptr; 580 | } 581 | shape.fixtures.clear(); 582 | 583 | // TODO: (queue) update 584 | } 585 | 586 | void Box2DCollisionObject::set_shape_as_one_way_collision(int p_index, bool enable) { 587 | ERR_FAIL_INDEX(p_index, shapes.size()); 588 | 589 | Shape &shape = shapes.write[p_index]; 590 | if (shape.one_way_collision == enable) { 591 | return; 592 | } 593 | 594 | shape.one_way_collision = enable; 595 | 596 | _update_shapes(); 597 | } 598 | 599 | void Box2DCollisionObject::remove_shape(Box2DShape *p_shape) { 600 | //remove a shape, all the times it appears 601 | for (int i = 0; i < shapes.size(); i++) { 602 | if (shapes[i].shape == p_shape) { 603 | remove_shape(i); 604 | i--; 605 | } 606 | } 607 | } 608 | 609 | void Box2DCollisionObject::recreate_shapes() { 610 | // todo make it work with one shape 611 | _clear_fixtures(); 612 | _update_shapes(); 613 | } 614 | 615 | void Box2DCollisionObject::remove_shape(int p_index) { 616 | //remove anything from shape to be erased to end, so subindices don't change 617 | ERR_FAIL_INDEX(p_index, shapes.size()); 618 | for (int i = p_index; i < shapes.size(); i++) { 619 | Shape &shape = shapes.write[i]; 620 | for (int j = 0; j < shape.fixtures.size(); j++) { 621 | // should never get here with a null owner 622 | if (body) { 623 | body->DestroyFixture(shape.fixtures[j]); 624 | } 625 | shape.fixtures.write[j] = nullptr; 626 | } 627 | shape.fixtures.clear(); 628 | } 629 | shapes.remove_at(p_index); 630 | 631 | // TODO: (queue) update 632 | } 633 | 634 | void Box2DCollisionObject::_clear_fixtures() { 635 | for (int i = 0; i < shapes.size(); i++) { 636 | Shape &shape = shapes.write[i]; 637 | for (int j = 0; j < shape.fixtures.size(); j++) { 638 | if (body) { 639 | body->DestroyFixture(shape.fixtures[j]); 640 | } 641 | shape.fixtures.write[j] = nullptr; 642 | } 643 | shape.fixtures.clear(); 644 | } 645 | } 646 | 647 | void Box2DCollisionObject::_set_space(Box2DSpace *p_space) { 648 | if (space) { 649 | // NOTE: Remember the transform by copying it from the b2Body to the b2BodyDef. 650 | if (body) { 651 | body_def->position = body->GetPosition(); 652 | body_def->angle = body->GetAngle(); 653 | } 654 | 655 | _clear_fixtures(); 656 | space->remove_object(this); 657 | } 658 | space = p_space; 659 | if (space) { 660 | space->add_object(this); 661 | _update_shapes(); 662 | } 663 | } 664 | 665 | int Box2DCollisionObject::get_shape_count() const { return shapes.size(); } 666 | Box2DShape *Box2DCollisionObject::get_shape(int p_index) const { 667 | CRASH_BAD_INDEX(p_index, shapes.size()); 668 | return shapes[p_index].shape; 669 | } 670 | 671 | const Transform2D &Box2DCollisionObject::get_shape_transform(int p_index) const { 672 | CRASH_BAD_INDEX(p_index, shapes.size()); 673 | return shapes[p_index].xform; 674 | } 675 | 676 | void Box2DCollisionObject::set_gravity_scale(real_t p_gravity_scale) { 677 | gravity_scale = p_gravity_scale; // no need to convert 678 | } 679 | 680 | real_t Box2DCollisionObject::get_gravity_scale() { 681 | return gravity_scale; // no need to convert 682 | } 683 | 684 | void Box2DCollisionObject::add_collision_exception(Box2DCollisionObject *excepted_body) { 685 | collision_exception.insert(excepted_body); 686 | } 687 | void Box2DCollisionObject::remove_collision_exception(Box2DCollisionObject *excepted_body) { 688 | collision_exception.erase(excepted_body); 689 | } 690 | 691 | bool Box2DCollisionObject::is_body_collision_excepted(Box2DCollisionObject *excepted_body) { 692 | return collision_exception.has(excepted_body); 693 | } 694 | TypedArray Box2DCollisionObject::get_collision_exception() { 695 | TypedArray array; 696 | for (Box2DCollisionObject *E : collision_exception) { 697 | array.append(E->get_self()); 698 | } 699 | return array; 700 | } 701 | // MISC 702 | 703 | void Box2DCollisionObject::_update_shapes() { 704 | if (!space || !body) { 705 | return; 706 | } 707 | 708 | for (int i = 0; i < shapes.size(); i++) { 709 | Shape &s = shapes.write[i]; 710 | if (s.disabled) { 711 | continue; 712 | } 713 | 714 | //not quite correct, should compute the next matrix.. 715 | //Transform2D xform = transform * s.xform; 716 | bool is_static = body->GetType() == b2_staticBody; 717 | if (s.fixtures.is_empty()) { 718 | int box2d_shape_count = s.shape->get_b2Shape_count(is_static); 719 | s.fixtures.resize(box2d_shape_count); 720 | for (int j = 0; j < box2d_shape_count; j++) { 721 | b2FixtureDef fixture_def; 722 | fixture_def.shape = s.shape->get_transformed_b2Shape(j, s.xform, s.one_way_collision, is_static); 723 | if (fixture_def.shape == nullptr) { 724 | ERR_PRINT("Shape " + itos(j) + " disabled."); 725 | s.disabled = true; 726 | break; 727 | } 728 | fixture_def.density = 1.0f; 729 | fixture_def.filter = filter; 730 | fixture_def.friction = physics_material.friction; 731 | fixture_def.restitution = physics_material.bounce; 732 | fixture_def.isSensor = type == Type::TYPE_AREA; 733 | fixture_def.userData.shape_idx = i; 734 | fixture_def.userData.box2d_fixture_idx = j; 735 | s.fixtures.write[j] = body->CreateFixture(&fixture_def); 736 | } 737 | } else { 738 | int box2d_shape_count = s.shape->get_b2Shape_count(is_static); 739 | for (int j = 0; j < box2d_shape_count; j++) { 740 | b2Fixture *fixture = s.fixtures[j]; 741 | fixture->SetFilterData(filter); 742 | fixture->SetFriction(physics_material.friction); 743 | fixture->SetRestitution(physics_material.bounce); 744 | } 745 | } 746 | 747 | //space->get_broadphase()->move(s.bpid, shape_aabb); 748 | } 749 | } 750 | void Box2DCollisionObject::before_step() { 751 | if (body) { 752 | // custom gravity 753 | body->ApplyForceToCenter(body->GetMass() * gravity_scale * total_gravity, false); 754 | if (constant_forces.constant_force != b2Vec2_zero) { 755 | // constant force 756 | body->ApplyForce(body->GetMass() * constant_forces.constant_force, constant_forces.constant_force_position + body->GetPosition(), true); 757 | } 758 | if (constant_forces.constant_torque != 0) { 759 | // constant torque 760 | body->ApplyTorque(body->GetMass() * constant_forces.constant_torque, true); 761 | } 762 | } 763 | } 764 | 765 | Box2DCollisionObject::Type Box2DCollisionObject::get_type() const { return type; } 766 | 767 | void Box2DCollisionObject::set_self(const RID &p_self) { self = p_self; } 768 | RID Box2DCollisionObject::get_self() const { return self; } 769 | 770 | void Box2DCollisionObject::add_area(Box2DArea *p_area) { 771 | areas.append(p_area); 772 | areas.sort(); 773 | recalculate_total_gravity(); 774 | recalculate_total_angular_damp(); 775 | recalculate_total_linear_damp(); 776 | } 777 | void Box2DCollisionObject::sort_areas() { 778 | areas.sort(); 779 | } 780 | void Box2DCollisionObject::recalculate_total_gravity() { 781 | total_gravity = b2Vec2_zero; 782 | // compute gravity from other areas 783 | bool keep_computing = true; 784 | for (Box2DArea *area : areas) { 785 | if (!keep_computing) { 786 | break; 787 | } 788 | b2Vec2 area_gravity = area->get_b2_gravity(get_transform()); 789 | 790 | switch (area->get_gravity_override_mode()) { 791 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_COMBINE: { 792 | total_gravity += area_gravity; 793 | } break; 794 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: { 795 | total_gravity += area_gravity; 796 | keep_computing = false; 797 | } break; 798 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_REPLACE: { 799 | total_gravity = area_gravity; 800 | keep_computing = false; 801 | } break; 802 | case PhysicsServer2D::AreaSpaceOverrideMode::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: { 803 | total_gravity = area_gravity; 804 | } break; 805 | default: { 806 | } 807 | } 808 | } 809 | } 810 | void Box2DCollisionObject::remove_area(Box2DArea *p_area) { 811 | areas.erase(p_area); 812 | areas.sort(); 813 | recalculate_total_gravity(); 814 | recalculate_total_angular_damp(); 815 | recalculate_total_linear_damp(); 816 | } 817 | 818 | b2BodyDef *Box2DCollisionObject::get_b2BodyDef() { return body_def; } 819 | void Box2DCollisionObject::set_b2BodyDef(b2BodyDef *p_body_def) { body_def = p_body_def; } 820 | b2Body *Box2DCollisionObject::get_b2Body() { return body; } 821 | void Box2DCollisionObject::set_b2Body(b2Body *p_body) { 822 | body = p_body; 823 | // set additional properties here 824 | if (body) { 825 | body->SetMassData(&mass_data); 826 | body->SetAwake(true); 827 | //recreate_shapes(); 828 | } 829 | } 830 | 831 | void Box2DCollisionObject::_set_transform(const Transform2D &p_transform, bool p_update_shapes) { 832 | if (body) { 833 | Vector2 pos = p_transform.get_origin(); 834 | b2Vec2 box2d_pos; 835 | godot_to_box2d(pos, box2d_pos); 836 | body->SetTransform(box2d_pos, p_transform.get_rotation()); 837 | } else { 838 | godot_to_box2d(p_transform.get_origin(), body_def->position); 839 | body_def->angle = p_transform.get_rotation(); 840 | } 841 | if (p_update_shapes) { 842 | _update_shapes(); 843 | } 844 | } 845 | 846 | Box2DCollisionObject::Box2DCollisionObject(Type p_type) { 847 | type = p_type; 848 | body_def = memnew(b2BodyDef); 849 | body_def->userData.collision_object = this; 850 | reset_mass_properties(); 851 | } 852 | 853 | Box2DCollisionObject::~Box2DCollisionObject() { 854 | for (Box2DArea *area : areas) { 855 | if (area) { 856 | area->remove_body(this); 857 | } 858 | } 859 | memdelete(body_def); 860 | } 861 | -------------------------------------------------------------------------------- /src/bodies/box2d_collision_object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "../box2d_type_conversions.h" 13 | #include "../shapes/box2d_shape.h" 14 | #include "../spaces/box2d_space.h" 15 | 16 | using namespace godot; 17 | 18 | class Box2DDirectSpaceState; 19 | class Box2DJoint; 20 | class Box2DArea; 21 | 22 | class Box2DCollisionObject { 23 | public: 24 | enum Type { 25 | TYPE_AREA, 26 | TYPE_BODY 27 | }; 28 | bool operator<(const Box2DCollisionObject &other) const { 29 | return collision.priority < other.collision.priority; 30 | } 31 | 32 | protected: 33 | Type type; 34 | RID self; 35 | ObjectID object_instance_id; 36 | ObjectID canvas_instance_id; 37 | 38 | b2Body *body = nullptr; 39 | b2BodyDef *body_def = nullptr; 40 | Box2DSpace *space = nullptr; 41 | HashSet collision_exception; 42 | Vector areas; 43 | 44 | struct Shape { 45 | Transform2D xform; 46 | Box2DShape *shape = nullptr; 47 | Vector fixtures; 48 | bool disabled = false; 49 | bool one_way_collision = false; 50 | }; 51 | Vector shapes; 52 | 53 | struct Collision { 54 | real_t priority = 1; 55 | bool pickable = false; 56 | }; 57 | b2Filter filter; 58 | Collision collision; 59 | struct ConstantForces { 60 | b2Vec2 constant_force = b2Vec2_zero; 61 | b2Vec2 constant_force_position = b2Vec2_zero; 62 | real_t constant_torque = 0; 63 | }; 64 | ConstantForces constant_forces; 65 | struct PhysicsMaterial { 66 | real_t friction = 1; 67 | real_t bounce = 0; 68 | }; 69 | PhysicsMaterial physics_material; 70 | struct Damping { 71 | real_t linear_damp = 0; 72 | real_t angular_damp = 0; 73 | PhysicsServer2D::BodyDampMode linear_damp_mode = PhysicsServer2D::BodyDampMode::BODY_DAMP_MODE_COMBINE; 74 | PhysicsServer2D::BodyDampMode angular_damp_mode = PhysicsServer2D::BodyDampMode::BODY_DAMP_MODE_COMBINE; 75 | }; 76 | Damping damping; 77 | real_t total_linear_damp = 0.1; 78 | real_t total_angular_damp = 1; 79 | b2Vec2 total_gravity = b2Vec2(0, -9.8); 80 | 81 | void _clear_fixtures(); 82 | void _update_shapes(); 83 | Box2DDirectSpaceState *direct_space = nullptr; 84 | 85 | b2MassData mass_data; 86 | real_t gravity_scale = 1; 87 | struct ContactEdgeData { 88 | b2ContactEdge *edge; 89 | int32_t point_idx; 90 | }; 91 | 92 | ContactEdgeData _get_contact_edge_data(int32_t contact_idx) const; 93 | 94 | protected: 95 | void _set_transform(const Transform2D &p_transform, bool p_update_shapes = true); 96 | 97 | void _set_space(Box2DSpace *p_space); 98 | 99 | Box2DCollisionObject(Type p_type); 100 | 101 | public: 102 | void recreate_shapes(); 103 | void set_linear_damp_mode(PhysicsServer2D::BodyDampMode p_linear_damp); 104 | virtual void set_linear_damp(real_t p_linear_damp); 105 | void set_angular_damp_mode(PhysicsServer2D::BodyDampMode p_linear_damp); 106 | virtual void set_angular_damp(real_t p_angular_damp); 107 | virtual void set_priority(real_t p_priority); 108 | void set_bounce(real_t p_bounce); 109 | void set_friction(real_t p_friction); 110 | void set_mass(real_t p_mass); 111 | void set_inertia(real_t p_inertia); 112 | void set_center_of_mass(Vector2 p_center_of_mass); 113 | 114 | PhysicsServer2D::BodyDampMode get_linear_damp_mode() const; 115 | double get_linear_damp() const; 116 | PhysicsServer2D::BodyDampMode get_angular_damp_mode() const; 117 | double get_angular_damp() const; 118 | double get_priority() const; 119 | double get_bounce() const; 120 | double get_friction() const; 121 | double get_mass() const; 122 | double get_inertia() const; 123 | void reset_mass_properties(); 124 | 125 | // Direct Body API 126 | virtual Vector2 get_total_gravity() const; 127 | virtual double get_total_linear_damp() const; 128 | virtual double get_total_angular_damp() const; 129 | virtual Vector2 get_center_of_mass() const; 130 | virtual Vector2 get_center_of_mass_local() const; 131 | virtual double get_inverse_mass() const; 132 | virtual double get_inverse_inertia() const; 133 | virtual void set_linear_velocity(const Vector2 &velocity); 134 | virtual Vector2 get_linear_velocity() const; 135 | virtual void set_angular_velocity(double velocity); 136 | virtual double get_angular_velocity() const; 137 | virtual void set_transform(const Transform2D &transform); 138 | virtual Transform2D get_transform() const; 139 | virtual Vector2 get_velocity_at_local_position(const Vector2 &local_position) const; 140 | virtual void apply_central_impulse(const Vector2 &impulse); 141 | virtual void apply_impulse(const Vector2 &impulse, const Vector2 &position); 142 | virtual void apply_torque_impulse(double impulse); 143 | virtual void apply_central_force(const Vector2 &force); 144 | virtual void apply_force(const Vector2 &force, const Vector2 &position); 145 | virtual void apply_torque(double torque); 146 | virtual void add_constant_central_force(const Vector2 &force); 147 | virtual void add_constant_force(const Vector2 &force, const Vector2 &position); 148 | virtual void add_constant_torque(double torque); 149 | virtual void set_constant_force(const Vector2 &force); 150 | virtual Vector2 get_constant_force() const; 151 | virtual void set_constant_torque(double torque); 152 | virtual double get_constant_torque() const; 153 | virtual void set_sleep_state(bool enabled); 154 | virtual bool is_sleeping() const; 155 | virtual int32_t get_contact_count() const; 156 | virtual Vector2 get_contact_local_position(int32_t contact_idx) const; 157 | virtual Vector2 get_contact_local_normal(int32_t contact_idx) const; 158 | virtual int32_t get_contact_local_shape(int32_t contact_idx) const; 159 | virtual RID get_contact_collider(int32_t contact_idx) const; 160 | virtual Vector2 get_contact_collider_position(int32_t contact_idx) const; 161 | virtual uint64_t get_contact_collider_id(int32_t contact_idx) const; 162 | virtual Object *get_contact_collider_object(int32_t contact_idx) const; 163 | virtual int32_t get_contact_collider_shape(int32_t contact_idx) const; 164 | virtual Vector2 get_contact_collider_velocity_at_position(int32_t contact_idx) const; 165 | virtual Vector2 get_contact_impulse(int32_t contact_idx) const; 166 | virtual double get_step() const; 167 | virtual void integrate_forces(); 168 | virtual PhysicsDirectSpaceState2D *get_space_state(); 169 | 170 | // Physics Server 171 | 172 | virtual void set_object_instance_id(const ObjectID &p_instance_id); 173 | virtual ObjectID get_object_instance_id() const; 174 | virtual void set_canvas_instance_id(const ObjectID &p_instance_id); 175 | virtual ObjectID get_canvas_instance_id() const; 176 | virtual void set_collision_layer(uint32_t layer); 177 | virtual uint32_t get_collision_layer() const; 178 | virtual void set_collision_mask(uint32_t layer); 179 | virtual uint32_t get_collision_mask() const; 180 | virtual void set_pickable(bool pickable); 181 | virtual Box2DSpace *get_space() const; 182 | virtual void add_shape(Box2DShape *p_shape, const Transform2D &p_transform = Transform2D(), bool p_disabled = false); 183 | virtual void set_shape(int p_index, Box2DShape *p_shape); 184 | virtual void set_shape_transform(int p_index, const Transform2D &p_transform); 185 | virtual void set_shape_disabled(int p_index, bool p_disabled); 186 | virtual void set_shape_as_one_way_collision(int p_index, bool enable); 187 | virtual int get_shape_count() const; 188 | virtual Box2DShape *get_shape(int p_index) const; 189 | virtual const Transform2D &get_shape_transform(int p_index) const; 190 | virtual void remove_shape(Box2DShape *p_shape); 191 | virtual void remove_shape(int p_index); 192 | void set_gravity_scale(real_t p_gravity_scale); 193 | real_t get_gravity_scale(); 194 | void add_collision_exception(Box2DCollisionObject *excepted_body); 195 | void remove_collision_exception(Box2DCollisionObject *excepted_body); 196 | bool is_body_collision_excepted(Box2DCollisionObject *excepted_body); 197 | 198 | TypedArray get_collision_exception(); 199 | 200 | virtual void set_space(Box2DSpace *p_space) = 0; 201 | 202 | // MISC 203 | void sort_areas(); 204 | void recalculate_total_gravity(); 205 | void recalculate_total_linear_damp(); 206 | void recalculate_total_angular_damp(); 207 | 208 | Object *get_object() const; 209 | Object *get_object_unsafe() const; 210 | Type get_type() const; 211 | void set_self(const RID &p_self); 212 | RID get_self() const; 213 | virtual void add_area(Box2DArea *p_area); 214 | virtual void remove_area(Box2DArea *p_area); 215 | 216 | b2BodyDef *get_b2BodyDef(); 217 | void set_b2BodyDef(b2BodyDef *p_body_def); 218 | b2Body *get_b2Body(); 219 | virtual void set_b2Body(b2Body *p_body); 220 | virtual HashSet get_joints() { return HashSet(); } 221 | 222 | void before_step(); 223 | Box2DCollisionObject(); 224 | virtual ~Box2DCollisionObject(); 225 | }; 226 | -------------------------------------------------------------------------------- /src/bodies/box2d_direct_body_state.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_direct_body_state.h" 2 | 3 | Vector2 Box2DDirectBodyState::_get_total_gravity() const { 4 | ERR_FAIL_NULL_V(body, Vector2()); 5 | return body->get_total_gravity(); 6 | } 7 | 8 | double Box2DDirectBodyState::_get_total_linear_damp() const { 9 | ERR_FAIL_NULL_V(body, 0); 10 | return body->get_total_linear_damp(); 11 | } 12 | 13 | double Box2DDirectBodyState::_get_total_angular_damp() const { 14 | ERR_FAIL_NULL_V(body, 0); 15 | return body->get_total_angular_damp(); 16 | } 17 | 18 | Vector2 Box2DDirectBodyState::_get_center_of_mass() const { 19 | ERR_FAIL_NULL_V(body, Vector2()); 20 | return body->get_center_of_mass(); 21 | } 22 | 23 | Vector2 Box2DDirectBodyState::_get_center_of_mass_local() const { 24 | ERR_FAIL_NULL_V(body, Vector2()); 25 | return body->get_center_of_mass_local(); 26 | } 27 | 28 | double Box2DDirectBodyState::_get_inverse_mass() const { 29 | ERR_FAIL_NULL_V(body, 0); 30 | return body->get_inverse_mass(); 31 | } 32 | double Box2DDirectBodyState::_get_inverse_inertia() const { 33 | ERR_FAIL_NULL_V(body, 0); 34 | return body->get_inverse_inertia(); 35 | } 36 | void Box2DDirectBodyState::_set_linear_velocity(const Vector2 &velocity) { 37 | ERR_FAIL_NULL(body); 38 | body->set_linear_velocity(velocity); 39 | } 40 | Vector2 Box2DDirectBodyState::_get_linear_velocity() const { 41 | ERR_FAIL_NULL_V(body, Vector2()); 42 | return body->get_linear_velocity(); 43 | } 44 | void Box2DDirectBodyState::_set_angular_velocity(double velocity) { 45 | ERR_FAIL_NULL(body); 46 | body->set_angular_velocity(velocity); 47 | } 48 | double Box2DDirectBodyState::_get_angular_velocity() const { 49 | ERR_FAIL_NULL_V(body, 0); 50 | return body->get_angular_velocity(); 51 | } 52 | void Box2DDirectBodyState::_set_transform(const Transform2D &transform) { 53 | ERR_FAIL_NULL(body); 54 | body->set_transform(transform); 55 | } 56 | Transform2D Box2DDirectBodyState::_get_transform() const { 57 | ERR_FAIL_NULL_V(body, Transform2D()); 58 | return body->get_transform(); 59 | } 60 | Vector2 Box2DDirectBodyState::_get_velocity_at_local_position(const Vector2 &local_position) const { 61 | ERR_FAIL_NULL_V(body, Vector2()); 62 | return body->get_velocity_at_local_position(local_position); 63 | } 64 | void Box2DDirectBodyState::_apply_central_impulse(const Vector2 &impulse) { 65 | ERR_FAIL_NULL(body); 66 | body->apply_central_impulse(impulse); 67 | } 68 | void Box2DDirectBodyState::_apply_impulse(const Vector2 &impulse, const Vector2 &position) { 69 | ERR_FAIL_NULL(body); 70 | body->apply_impulse(impulse, position); 71 | } 72 | void Box2DDirectBodyState::_apply_torque_impulse(double impulse) { 73 | ERR_FAIL_NULL(body); 74 | body->apply_torque_impulse(impulse); 75 | } 76 | void Box2DDirectBodyState::_apply_central_force(const Vector2 &force) { 77 | ERR_FAIL_NULL(body); 78 | body->apply_central_force(force); 79 | } 80 | void Box2DDirectBodyState::_apply_force(const Vector2 &force, const Vector2 &position) { 81 | ERR_FAIL_NULL(body); 82 | body->apply_force(force, position); 83 | } 84 | void Box2DDirectBodyState::_apply_torque(double torque) { 85 | ERR_FAIL_NULL(body); 86 | body->apply_torque(torque); 87 | } 88 | void Box2DDirectBodyState::_add_constant_central_force(const Vector2 &force) { 89 | ERR_FAIL_NULL(body); 90 | body->add_constant_central_force(force); 91 | } 92 | void Box2DDirectBodyState::_add_constant_force(const Vector2 &force, const Vector2 &position) { 93 | ERR_FAIL_NULL(body); 94 | body->add_constant_force(force, position); 95 | } 96 | void Box2DDirectBodyState::_add_constant_torque(double torque) { 97 | ERR_FAIL_NULL(body); 98 | body->add_constant_torque(torque); 99 | } 100 | void Box2DDirectBodyState::_set_constant_force(const Vector2 &force) { 101 | ERR_FAIL_NULL(body); 102 | body->set_constant_force(force); 103 | } 104 | Vector2 Box2DDirectBodyState::_get_constant_force() const { 105 | ERR_FAIL_NULL_V(body, Vector2()); 106 | return body->get_constant_force(); 107 | } 108 | void Box2DDirectBodyState::_set_constant_torque(double torque) { 109 | ERR_FAIL_NULL(body); 110 | body->set_constant_torque(torque); 111 | } 112 | double Box2DDirectBodyState::_get_constant_torque() const { 113 | ERR_FAIL_NULL_V(body, 0); 114 | return body->get_constant_torque(); 115 | } 116 | void Box2DDirectBodyState::_set_sleep_state(bool enabled) { 117 | ERR_FAIL_NULL(body); 118 | body->set_sleep_state(enabled); 119 | } 120 | bool Box2DDirectBodyState::_is_sleeping() const { 121 | ERR_FAIL_NULL_V(body, false); 122 | return body->is_sleeping(); 123 | } 124 | int32_t Box2DDirectBodyState::_get_contact_count() const { 125 | ERR_FAIL_NULL_V(body, 0); 126 | return body->get_contact_count(); 127 | } 128 | Vector2 Box2DDirectBodyState::_get_contact_local_position(int32_t contact_idx) const { 129 | ERR_FAIL_NULL_V(body, Vector2()); 130 | return body->get_contact_local_position(contact_idx); 131 | } 132 | Vector2 Box2DDirectBodyState::_get_contact_local_normal(int32_t contact_idx) const { 133 | ERR_FAIL_NULL_V(body, Vector2()); 134 | return body->get_contact_local_normal(contact_idx); 135 | } 136 | int32_t Box2DDirectBodyState::_get_contact_local_shape(int32_t contact_idx) const { 137 | ERR_FAIL_NULL_V(body, 0); 138 | return body->get_contact_local_shape(contact_idx); 139 | } 140 | RID Box2DDirectBodyState::_get_contact_collider(int32_t contact_idx) const { 141 | ERR_FAIL_NULL_V(body, RID()); 142 | return body->get_contact_collider(contact_idx); 143 | } 144 | Vector2 Box2DDirectBodyState::_get_contact_collider_position(int32_t contact_idx) const { 145 | ERR_FAIL_NULL_V(body, Vector2()); 146 | return body->get_contact_collider_position(contact_idx); 147 | } 148 | uint64_t Box2DDirectBodyState::_get_contact_collider_id(int32_t contact_idx) const { 149 | ERR_FAIL_NULL_V(body, 0); 150 | return body->get_contact_collider_id(contact_idx); 151 | } 152 | Object *Box2DDirectBodyState::_get_contact_collider_object(int32_t contact_idx) const { 153 | ERR_FAIL_NULL_V(body, nullptr); 154 | return body->get_contact_collider_object(contact_idx); 155 | } 156 | int32_t Box2DDirectBodyState::_get_contact_collider_shape(int32_t contact_idx) const { 157 | ERR_FAIL_NULL_V(body, 0); 158 | return body->get_contact_collider_shape(contact_idx); 159 | } 160 | Vector2 Box2DDirectBodyState::_get_contact_collider_velocity_at_position(int32_t contact_idx) const { 161 | ERR_FAIL_NULL_V(body, Vector2()); 162 | return body->get_contact_collider_velocity_at_position(contact_idx); 163 | } 164 | Vector2 Box2DDirectBodyState::_get_contact_impulse(int32_t contact_idx) const { 165 | ERR_FAIL_NULL_V(body, Vector2()); 166 | return body->get_contact_impulse(contact_idx); 167 | } 168 | double Box2DDirectBodyState::_get_step() const { 169 | ERR_FAIL_NULL_V(body, 0); 170 | return body->get_step(); 171 | } 172 | void Box2DDirectBodyState::_integrate_forces() { 173 | ERR_FAIL_NULL(body); 174 | body->integrate_forces(); 175 | } 176 | PhysicsDirectSpaceState2D *Box2DDirectBodyState::_get_space_state() { 177 | ERR_FAIL_NULL_V(body, nullptr); 178 | return body->get_space_state(); 179 | } 180 | -------------------------------------------------------------------------------- /src/bodies/box2d_direct_body_state.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "box2d_body.h" 7 | 8 | using namespace godot; 9 | 10 | class Box2DDirectBodyState : public PhysicsDirectBodyState2DExtension { 11 | GDCLASS(Box2DDirectBodyState, PhysicsDirectBodyState2DExtension); 12 | 13 | protected: 14 | static void _bind_methods() {} 15 | 16 | public: 17 | Box2DBody *body = nullptr; 18 | 19 | virtual Vector2 _get_total_gravity() const override; 20 | virtual double _get_total_linear_damp() const override; 21 | virtual double _get_total_angular_damp() const override; 22 | virtual Vector2 _get_center_of_mass() const override; 23 | virtual Vector2 _get_center_of_mass_local() const override; 24 | virtual double _get_inverse_mass() const override; 25 | virtual double _get_inverse_inertia() const override; 26 | virtual void _set_linear_velocity(const Vector2 &velocity) override; 27 | virtual Vector2 _get_linear_velocity() const override; 28 | virtual void _set_angular_velocity(double velocity) override; 29 | virtual double _get_angular_velocity() const override; 30 | virtual void _set_transform(const Transform2D &transform) override; 31 | virtual Transform2D _get_transform() const override; 32 | virtual Vector2 _get_velocity_at_local_position(const Vector2 &local_position) const override; 33 | virtual void _apply_central_impulse(const Vector2 &impulse) override; 34 | virtual void _apply_impulse(const Vector2 &impulse, const Vector2 &position) override; 35 | virtual void _apply_torque_impulse(double impulse) override; 36 | virtual void _apply_central_force(const Vector2 &force) override; 37 | virtual void _apply_force(const Vector2 &force, const Vector2 &position) override; 38 | virtual void _apply_torque(double torque) override; 39 | virtual void _add_constant_central_force(const Vector2 &force) override; 40 | virtual void _add_constant_force(const Vector2 &force, const Vector2 &position) override; 41 | virtual void _add_constant_torque(double torque) override; 42 | virtual void _set_constant_force(const Vector2 &force) override; 43 | virtual Vector2 _get_constant_force() const override; 44 | virtual void _set_constant_torque(double torque) override; 45 | virtual double _get_constant_torque() const override; 46 | virtual void _set_sleep_state(bool enabled) override; 47 | virtual bool _is_sleeping() const override; 48 | virtual int32_t _get_contact_count() const override; 49 | virtual Vector2 _get_contact_local_position(int32_t contact_idx) const override; 50 | virtual Vector2 _get_contact_local_normal(int32_t contact_idx) const override; 51 | virtual int32_t _get_contact_local_shape(int32_t contact_idx) const override; 52 | virtual RID _get_contact_collider(int32_t contact_idx) const override; 53 | virtual Vector2 _get_contact_collider_position(int32_t contact_idx) const override; 54 | virtual uint64_t _get_contact_collider_id(int32_t contact_idx) const override; 55 | virtual Object *_get_contact_collider_object(int32_t contact_idx) const override; 56 | virtual int32_t _get_contact_collider_shape(int32_t contact_idx) const override; 57 | virtual Vector2 _get_contact_collider_velocity_at_position(int32_t contact_idx) const override; 58 | virtual Vector2 _get_contact_impulse(int32_t contact_idx) const override; 59 | virtual double _get_step() const override; 60 | virtual void _integrate_forces() override; 61 | virtual PhysicsDirectSpaceState2D *_get_space_state() override; 62 | 63 | ~Box2DDirectBodyState() override = default; 64 | }; 65 | -------------------------------------------------------------------------------- /src/box2d_type_conversions.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_type_conversions.h" 2 | 3 | void godot_to_box2d(const float &p_value, float &r_box2d_value) { 4 | r_box2d_value = godot_to_box2d(p_value); 5 | } 6 | 7 | void godot_to_box2d(const double &p_value, float &r_box2d_value) { 8 | r_box2d_value = godot_to_box2d(p_value); 9 | } 10 | 11 | void godot_to_box2d(const Vector2 &p_vector, b2Vec2 &r_box2d_vector) { 12 | r_box2d_vector.x = p_vector.x * G_TO_B_FACTOR; 13 | r_box2d_vector.y = p_vector.y * G_TO_B_FACTOR; 14 | } 15 | 16 | float godot_to_box2d(const float &p_value) { 17 | return p_value * G_TO_B_FACTOR; 18 | } 19 | 20 | float godot_to_box2d(const double &p_value) { 21 | return (float)(p_value * G_TO_B_FACTOR); 22 | } 23 | 24 | b2Vec2 godot_to_box2d(const Vector2 &p_vector) { 25 | return b2Vec2(p_vector.x * G_TO_B_FACTOR, p_vector.y * G_TO_B_FACTOR); 26 | } 27 | 28 | void box2d_to_godot(const float &p_box2d_value, float &r_value) { 29 | r_value = box2d_to_godot(p_box2d_value); 30 | } 31 | 32 | void box2d_to_godot(const float &p_box2d_value, double &r_value) { 33 | r_value = box2d_to_godot_d(p_box2d_value); 34 | } 35 | 36 | void box2d_to_godot(const b2Vec2 &p_box2d_vector, Vector2 &r_vector) { 37 | r_vector.x = B_TO_G_FACTOR * p_box2d_vector.x; 38 | r_vector.y = B_TO_G_FACTOR * p_box2d_vector.y; 39 | } 40 | 41 | float box2d_to_godot(const float &p_box2d_value) { 42 | return B_TO_G_FACTOR * p_box2d_value; 43 | } 44 | double box2d_to_godot_d(const float &p_box2d_value) { 45 | return (double)(B_TO_G_FACTOR * p_box2d_value); 46 | } 47 | Vector2 box2d_to_godot(const b2Vec2 &p_box2d_vector) { 48 | return Vector2(B_TO_G_FACTOR * p_box2d_vector.x, B_TO_G_FACTOR * p_box2d_vector.y); 49 | } 50 | 51 | float variant_to_number(Variant p_variant) { 52 | if (p_variant.get_type() == Variant::INT) { 53 | return (int)p_variant; 54 | } 55 | return p_variant; 56 | } 57 | -------------------------------------------------------------------------------- /src/box2d_type_conversions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace godot; 9 | 10 | #define B_TO_G_FACTOR 50.0 11 | #define G_TO_B_FACTOR 1.0 / 50.0 12 | #define GODOT_LINEAR_SLOP b2_linearSlop *B_TO_G_FACTOR 13 | 14 | void godot_to_box2d(const float &p_value, float &r_box2d_value); 15 | void godot_to_box2d(const double &p_value, float &r_box2d_value); 16 | void godot_to_box2d(const Vector2 &p_vector, b2Vec2 &r_box2d_vector); 17 | 18 | float godot_to_box2d(const float &p_value); 19 | float godot_to_box2d(const double &p_value); 20 | b2Vec2 godot_to_box2d(const Vector2 &p_vector); 21 | 22 | void box2d_to_godot(const float &p_box2d_value, float &r_value); 23 | void box2d_to_godot(const float &p_box2d_value, double &r_value); 24 | void box2d_to_godot(const b2Vec2 &p_box2d_vector, Vector2 &r_vector); 25 | 26 | float box2d_to_godot(const float &p_box2d_value); 27 | double box2d_to_godot_d(const float &p_box2d_value); 28 | Vector2 box2d_to_godot(const b2Vec2 &p_box2d_vector); 29 | 30 | float variant_to_number(Variant p_variant); 31 | -------------------------------------------------------------------------------- /src/joints/box2d_joint.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_joint.h" 2 | #include "bodies/box2d_body.h" 3 | #include "box2d_type_conversions.h" 4 | #include "spaces/box2d_space.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | void Box2DJoint::clear() { 16 | configured = false; 17 | common.body_a = nullptr; 18 | common.body_b = nullptr; 19 | if (space) { 20 | space->remove_joint(this); 21 | } 22 | } 23 | 24 | void Box2DJoint::_recreate_joint() { 25 | if (space) { 26 | space->create_joint(this); 27 | } 28 | } 29 | 30 | void Box2DJoint::set_bias(real_t p_data) { 31 | common.bias = p_data; 32 | } 33 | void Box2DJoint::set_max_bias(real_t p_data) { 34 | common.max_bias = p_data; 35 | } 36 | void Box2DJoint::set_max_force(real_t p_data) { 37 | common.max_force = p_data; 38 | } 39 | real_t Box2DJoint::get_bias() { 40 | return common.bias; 41 | } 42 | real_t Box2DJoint::get_max_bias() { 43 | return common.max_bias; 44 | } 45 | real_t Box2DJoint::get_max_force() { 46 | return common.max_force; 47 | } 48 | 49 | void Box2DJoint::set_disable_collisions(bool p_disable_collisions) { 50 | if (joint_def->collideConnected == !p_disable_collisions) { 51 | return; 52 | } 53 | joint_def->collideConnected = !p_disable_collisions; 54 | _recreate_joint(); 55 | } 56 | 57 | bool Box2DJoint::get_disable_collisions() { 58 | return !joint_def->collideConnected; 59 | } 60 | 61 | void Box2DJoint::make_pin(const Vector2 &p_anchor, Box2DBody *p_body_a, Box2DBody *p_body_b) { 62 | type = PhysicsServer2D::JointType::JOINT_TYPE_PIN; 63 | common.anchor_a = godot_to_box2d(p_anchor); 64 | b2RevoluteJointDef *revolute_joint_def = memnew(b2RevoluteJointDef); 65 | revolute_joint_def->collideConnected = joint_def->collideConnected; 66 | memdelete(joint_def); 67 | joint_def = revolute_joint_def; 68 | common.body_a = p_body_a; 69 | common.body_b = p_body_b; 70 | // body_a and body_b are set when joint is created 71 | configured = true; 72 | } 73 | 74 | void Box2DJoint::make_groove(const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, Box2DBody *p_body_a, Box2DBody *p_body_b) { 75 | type = PhysicsServer2D::JointType::JOINT_TYPE_GROOVE; 76 | groove.lower_translation = godot_to_box2d((p_a_groove2 - p_b_anchor).length()); 77 | groove.upper_translation = godot_to_box2d((p_a_groove1 - p_b_anchor).length()); 78 | Vector2 axis = (p_a_groove1 - p_a_groove2).normalized(); 79 | groove.axis = b2Vec2(axis.x, axis.y); 80 | common.anchor_b = godot_to_box2d(p_b_anchor); 81 | b2PrismaticJointDef *prismatic_joint_def = memnew(b2PrismaticJointDef); 82 | prismatic_joint_def->collideConnected = joint_def->collideConnected; 83 | memdelete(joint_def); 84 | joint_def = prismatic_joint_def; 85 | common.body_a = p_body_a; 86 | common.body_b = p_body_b; 87 | // body_a and body_b are set when joint is created 88 | configured = true; 89 | } 90 | 91 | void Box2DJoint::make_damped_spring(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, Box2DBody *p_body_a, Box2DBody *p_body_b) { 92 | type = PhysicsServer2D::JointType::JOINT_TYPE_DAMPED_SPRING; 93 | common.anchor_a = godot_to_box2d(p_anchor_a); 94 | common.anchor_b = godot_to_box2d(p_anchor_b); 95 | b2DistanceJointDef *distance_joint_def = memnew(b2DistanceJointDef); 96 | distance_joint_def->collideConnected = joint_def->collideConnected; 97 | memdelete(joint_def); 98 | joint_def = distance_joint_def; 99 | common.body_a = p_body_a; 100 | common.body_b = p_body_b; 101 | // body_a and body_b are set when joint is created 102 | configured = true; 103 | } 104 | 105 | void Box2DJoint::set_pin_softness(real_t p_softness) { 106 | pin.softness = p_softness; // unused 107 | } 108 | 109 | real_t Box2DJoint::get_pin_softness() { 110 | return pin.softness; 111 | } 112 | 113 | void Box2DJoint::set_damped_spring_rest_length(real_t p_damped_spring_rest_length) { 114 | if (damped_spring.rest_length == godot_to_box2d(p_damped_spring_rest_length)) { 115 | return; 116 | } 117 | damped_spring.rest_length = godot_to_box2d(p_damped_spring_rest_length); 118 | if (joint && (b2DistanceJoint *)joint) { 119 | b2DistanceJoint *distance_joint = (b2DistanceJoint *)joint; 120 | distance_joint->SetLength(damped_spring.rest_length); 121 | } 122 | } 123 | real_t Box2DJoint::get_damped_spring_rest_length() { 124 | return box2d_to_godot(damped_spring.rest_length); 125 | } 126 | 127 | void Box2DJoint::set_damped_spring_stiffness(real_t p_damped_spring_stiffness) { 128 | if (damped_spring.stiffness == godot_to_box2d(p_damped_spring_stiffness)) { 129 | return; 130 | } 131 | damped_spring.stiffness = godot_to_box2d(p_damped_spring_stiffness); 132 | if (joint && (b2DistanceJoint *)joint && common.body_a && common.body_b) { 133 | b2DistanceJoint *distance_joint = (b2DistanceJoint *)joint; 134 | float stiffness = 0; 135 | float damping = 0; 136 | b2LinearStiffness(stiffness, damping, damped_spring.stiffness, damped_spring.damping, common.body_a->get_b2Body(), common.body_b->get_b2Body()); 137 | distance_joint->SetStiffness(stiffness); 138 | } 139 | } 140 | real_t Box2DJoint::get_damped_spring_stiffness() { 141 | return box2d_to_godot(damped_spring.stiffness); 142 | } 143 | 144 | void Box2DJoint::set_damped_spring_damping(real_t p_damped_spring_damping) { 145 | if (damped_spring.damping == godot_to_box2d(p_damped_spring_damping)) { 146 | return; 147 | } 148 | damped_spring.damping = godot_to_box2d(p_damped_spring_damping); 149 | if (joint && (b2DistanceJoint *)joint && common.body_a && common.body_b) { 150 | b2DistanceJoint *distance_joint = (b2DistanceJoint *)joint; 151 | float stiffness = 0; 152 | float damping = 0; 153 | b2LinearStiffness(stiffness, damping, damped_spring.stiffness, damped_spring.damping, common.body_a->get_b2Body(), common.body_b->get_b2Body()); 154 | distance_joint->SetStiffness(damping); 155 | } 156 | } 157 | real_t Box2DJoint::get_damped_spring_damping() { 158 | return box2d_to_godot(damped_spring.damping); 159 | } 160 | 161 | Box2DBody *Box2DJoint::get_body_a() { 162 | return common.body_a; 163 | } 164 | Box2DBody *Box2DJoint::get_body_b() { 165 | return common.body_b; 166 | } 167 | 168 | b2JointDef *Box2DJoint::get_b2JointDef() { 169 | switch (type) { 170 | case PhysicsServer2D::JointType::JOINT_TYPE_PIN: { 171 | b2RevoluteJointDef *revolute_joint_def = (b2RevoluteJointDef *)joint_def; 172 | revolute_joint_def->enableMotor = true; 173 | revolute_joint_def->Initialize(common.body_a->get_b2Body(), common.body_b->get_b2Body(), common.anchor_a); 174 | } break; 175 | case PhysicsServer2D::JointType::JOINT_TYPE_DAMPED_SPRING: { 176 | b2DistanceJointDef *distance_joint_def = (b2DistanceJointDef *)joint_def; 177 | b2LinearStiffness(distance_joint_def->stiffness, distance_joint_def->damping, damped_spring.stiffness, damped_spring.damping, common.body_a->get_b2Body(), common.body_b->get_b2Body()); 178 | distance_joint_def->Initialize(common.body_a->get_b2Body(), common.body_b->get_b2Body(), common.anchor_a, common.anchor_b); 179 | 180 | distance_joint_def->length = damped_spring.rest_length; 181 | distance_joint_def->minLength = 0; 182 | } break; 183 | case PhysicsServer2D::JointType::JOINT_TYPE_GROOVE: { 184 | b2PrismaticJointDef *prismatic_joint_def = (b2PrismaticJointDef *)joint_def; 185 | prismatic_joint_def->Initialize(common.body_a->get_b2Body(), common.body_b->get_b2Body(), common.anchor_b, groove.axis); 186 | prismatic_joint_def->lowerTranslation = -groove.lower_translation; 187 | prismatic_joint_def->upperTranslation = groove.upper_translation; 188 | prismatic_joint_def->enableLimit = true; 189 | } break; 190 | default: { 191 | ERR_PRINT("UNSUPPORTED"); 192 | } 193 | } 194 | return joint_def; 195 | } 196 | b2Joint *Box2DJoint::get_b2Joint() { 197 | return joint; 198 | } 199 | void Box2DJoint::set_b2Joint(b2Joint *p_joint) { 200 | joint = p_joint; 201 | } 202 | 203 | void Box2DJoint::set_space(Box2DSpace *p_space) { 204 | space = p_space; 205 | } 206 | 207 | Box2DJoint::Box2DJoint() { 208 | joint_def = memnew(b2JointDef); 209 | } 210 | 211 | Box2DJoint::~Box2DJoint() { 212 | memdelete(joint_def); 213 | } 214 | -------------------------------------------------------------------------------- /src/joints/box2d_joint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace godot; 13 | 14 | class Box2DSpace; 15 | class Box2DBody; 16 | 17 | class Box2DJoint { 18 | RID self; 19 | struct Pin { 20 | real_t softness = 0; 21 | }; 22 | struct DampedSpring { 23 | real_t rest_length = 0; 24 | real_t stiffness = 0; 25 | real_t damping = 0; 26 | }; 27 | struct Groove { 28 | real_t lower_translation = 0; 29 | real_t upper_translation = 0; 30 | b2Vec2 axis; 31 | }; 32 | struct CommonJoint { 33 | real_t bias = 0; 34 | real_t max_bias = 0; 35 | real_t max_force = 0; 36 | Box2DBody *body_a = nullptr; 37 | Box2DBody *body_b = nullptr; 38 | b2Vec2 anchor_a; 39 | b2Vec2 anchor_b; 40 | }; 41 | DampedSpring damped_spring; 42 | Pin pin; 43 | Groove groove; 44 | CommonJoint common; 45 | 46 | b2Joint *joint = nullptr; 47 | b2JointDef *joint_def = nullptr; 48 | 49 | Box2DSpace *space = nullptr; 50 | 51 | void _recreate_joint(); 52 | bool configured = false; 53 | PhysicsServer2D::JointType type; 54 | 55 | public: 56 | _FORCE_INLINE_ PhysicsServer2D::JointType get_type() const { return type; } 57 | 58 | _FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; } 59 | _FORCE_INLINE_ RID get_self() const { return self; } 60 | 61 | _FORCE_INLINE_ bool is_configured() const { return configured; } 62 | Box2DBody *get_body_a(); 63 | Box2DBody *get_body_b(); 64 | void set_bias(real_t p_data); 65 | void set_max_bias(real_t p_data); 66 | void set_max_force(real_t p_data); 67 | real_t get_bias(); 68 | real_t get_max_bias(); 69 | real_t get_max_force(); 70 | Variant get_data() const; 71 | void clear(); 72 | void set_disable_collisions(bool disable_collisions); 73 | bool get_disable_collisions(); 74 | void make_pin(const Vector2 &p_anchor, Box2DBody *p_body_a, Box2DBody *p_body_b); 75 | void make_groove(const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, Box2DBody *p_body_a, Box2DBody *p_body_b); 76 | void make_damped_spring(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, Box2DBody *p_body_a, Box2DBody *p_body_b); 77 | void set_pin_softness(real_t p_softness); 78 | real_t get_pin_softness(); 79 | void set_damped_spring_rest_length(real_t p_damped_spring_rest_length); 80 | real_t get_damped_spring_rest_length(); 81 | void set_damped_spring_stiffness(real_t p_damped_spring_stiffness); 82 | real_t get_damped_spring_stiffness(); 83 | void set_damped_spring_damping(real_t p_damped_spring_damping); 84 | real_t get_damped_spring_damping(); 85 | 86 | b2JointDef *get_b2JointDef(); 87 | b2Joint *get_b2Joint(); 88 | void set_b2Joint(b2Joint *p_joint); 89 | 90 | void set_space(Box2DSpace *p_space); 91 | 92 | Box2DJoint(); 93 | virtual ~Box2DJoint(); 94 | }; 95 | -------------------------------------------------------------------------------- /src/register_types.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "bodies/box2d_direct_body_state.h" 12 | #include "servers/physics_server_box2d.h" 13 | #include "spaces/box2d_direct_space_state.h" 14 | 15 | using namespace godot; 16 | 17 | static PhysicsServerBox2DFactory *box2d_factory = nullptr; 18 | 19 | void initialize_physics_server_box2d_module(ModuleInitializationLevel p_level) { 20 | if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) { 21 | return; 22 | } 23 | ClassDB::register_class(true); 24 | ClassDB::register_class(true); 25 | ClassDB::register_class(); 26 | ClassDB::register_class(); 27 | 28 | box2d_factory = memnew(PhysicsServerBox2DFactory()); 29 | PhysicsServer2DManager::get_singleton()->register_server("Box2D", Callable(box2d_factory, "create_box2d_callback")); 30 | } 31 | 32 | void uninitialize_physics_server_box2d_module(ModuleInitializationLevel p_level) { 33 | if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) { 34 | return; 35 | } 36 | memdelete(box2d_factory); 37 | } 38 | 39 | extern "C" { 40 | 41 | // Initialization. 42 | 43 | GDExtensionBool GDE_EXPORT physics_server_box2d_library_init(const GDExtensionInterface *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { 44 | godot::GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); 45 | 46 | init_obj.register_initializer(initialize_physics_server_box2d_module); 47 | init_obj.register_terminator(uninitialize_physics_server_box2d_module); 48 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SERVERS); 49 | 50 | return init_obj.init(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/register_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace godot; 5 | 6 | void initialize_physics_server_box2d_module(ModuleInitializationLevel p_level); 7 | void uninitialize_physics_server_box2d_module(ModuleInitializationLevel p_level); 8 | -------------------------------------------------------------------------------- /src/servers/physics_server_box2d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // We don't need windows.h in this plugin but many others do and it throws up on itself all the time 4 | // So best to include it and make sure CI warns us when we use something Microsoft took for their own goals.... 5 | #ifdef WIN32 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include "../bodies/box2d_area.h" 22 | #include "../bodies/box2d_body.h" 23 | #include "../joints/box2d_joint.h" 24 | #include "../shapes/box2d_shape.h" 25 | #include "../spaces/box2d_space.h" 26 | 27 | using namespace godot; 28 | 29 | class PhysicsServerBox2D : public PhysicsServer2DExtension { 30 | GDCLASS(PhysicsServerBox2D, PhysicsServer2DExtension); 31 | bool active = true; 32 | bool doing_sync = false; 33 | 34 | bool using_threads = false; 35 | 36 | bool flushing_queries = false; 37 | 38 | HashSet active_spaces; 39 | Box2DArea default_area; 40 | 41 | mutable RID_PtrOwner shape_owner; 42 | mutable RID_PtrOwner space_owner; 43 | mutable RID_PtrOwner area_owner; 44 | mutable RID_PtrOwner body_owner; 45 | mutable RID_PtrOwner joint_owner; 46 | 47 | RID _shape_create(ShapeType p_shape); 48 | 49 | protected: 50 | static void _bind_methods(){}; 51 | 52 | public: 53 | /* SHAPE API */ 54 | virtual RID _world_boundary_shape_create() override; 55 | virtual RID _separation_ray_shape_create() override; 56 | virtual RID _segment_shape_create() override; 57 | virtual RID _circle_shape_create() override; 58 | virtual RID _rectangle_shape_create() override; 59 | virtual RID _capsule_shape_create() override; 60 | virtual RID _convex_polygon_shape_create() override; 61 | virtual RID _concave_polygon_shape_create() override; 62 | virtual void _shape_set_data(const RID &shape, const Variant &data) override; 63 | virtual void _shape_set_custom_solver_bias(const RID &shape, double bias) override; 64 | virtual PhysicsServer2D::ShapeType _shape_get_type(const RID &shape) const override; 65 | virtual Variant _shape_get_data(const RID &shape) const override; 66 | virtual double _shape_get_custom_solver_bias(const RID &shape) const override; 67 | virtual bool _shape_collide(const RID &shape_A, const Transform2D &xform_A, const Vector2 &motion_A, const RID &shape_B, const Transform2D &xform_B, const Vector2 &motion_B, void *results, int32_t result_max, int32_t *result_count) override; 68 | 69 | /* SPACE API */ 70 | virtual RID _space_create() override; 71 | virtual void _space_set_active(const RID &space, bool active) override; 72 | virtual bool _space_is_active(const RID &space) const override; 73 | virtual void _space_set_param(const RID &space, PhysicsServer2D::SpaceParameter param, double value) override; 74 | virtual double _space_get_param(const RID &space, PhysicsServer2D::SpaceParameter param) const override; 75 | virtual PhysicsDirectSpaceState2D *_space_get_direct_state(const RID &space) override; 76 | virtual void _space_set_debug_contacts(const RID &space, int32_t max_contacts) override; 77 | virtual PackedVector2Array _space_get_contacts(const RID &space) const override; 78 | virtual int32_t _space_get_contact_count(const RID &space) const override; 79 | 80 | /* AREA API */ 81 | virtual RID _area_create() override; 82 | virtual void _area_set_space(const RID &area, const RID &space) override; 83 | virtual RID _area_get_space(const RID &area) const override; 84 | virtual void _area_add_shape(const RID &area, const RID &shape, const Transform2D &transform, bool disabled) override; 85 | virtual void _area_set_shape(const RID &area, int32_t shape_idx, const RID &shape) override; 86 | virtual void _area_set_shape_transform(const RID &area, int32_t shape_idx, const Transform2D &transform) override; 87 | virtual void _area_set_shape_disabled(const RID &area, int32_t shape_idx, bool disabled) override; 88 | virtual int32_t _area_get_shape_count(const RID &area) const override; 89 | virtual RID _area_get_shape(const RID &area, int32_t shape_idx) const override; 90 | virtual Transform2D _area_get_shape_transform(const RID &area, int32_t shape_idx) const override; 91 | virtual void _area_remove_shape(const RID &area, int32_t shape_idx) override; 92 | virtual void _area_clear_shapes(const RID &area) override; 93 | virtual void _area_attach_object_instance_id(const RID &area, uint64_t id) override; 94 | virtual uint64_t _area_get_object_instance_id(const RID &area) const override; 95 | virtual void _area_attach_canvas_instance_id(const RID &area, uint64_t id) override; 96 | virtual uint64_t _area_get_canvas_instance_id(const RID &area) const override; 97 | virtual void _area_set_param(const RID &area, PhysicsServer2D::AreaParameter param, const Variant &value) override; 98 | virtual void _area_set_transform(const RID &area, const Transform2D &transform) override; 99 | virtual Variant _area_get_param(const RID &area, PhysicsServer2D::AreaParameter param) const override; 100 | virtual Transform2D _area_get_transform(const RID &area) const override; 101 | virtual void _area_set_collision_layer(const RID &area, uint32_t layer) override; 102 | virtual uint32_t _area_get_collision_layer(const RID &area) const override; 103 | virtual void _area_set_collision_mask(const RID &area, uint32_t mask) override; 104 | virtual uint32_t _area_get_collision_mask(const RID &area) const override; 105 | virtual void _area_set_monitorable(const RID &area, bool monitorable) override; 106 | virtual void _area_set_pickable(const RID &area, bool pickable) override; 107 | virtual void _area_set_monitor_callback(const RID &area, const Callable &callback) override; 108 | virtual void _area_set_area_monitor_callback(const RID &area, const Callable &callback) override; 109 | 110 | /* BODY API */ 111 | virtual RID _body_create() override; 112 | virtual void _body_set_space(const RID &body, const RID &space) override; 113 | virtual RID _body_get_space(const RID &body) const override; 114 | virtual void _body_set_mode(const RID &body, PhysicsServer2D::BodyMode mode) override; 115 | virtual PhysicsServer2D::BodyMode _body_get_mode(const RID &body) const override; 116 | virtual void _body_add_shape(const RID &body, const RID &shape, const Transform2D &transform, bool disabled) override; 117 | virtual void _body_set_shape(const RID &body, int32_t shape_idx, const RID &shape) override; 118 | virtual void _body_set_shape_transform(const RID &body, int32_t shape_idx, const Transform2D &transform) override; 119 | virtual int32_t _body_get_shape_count(const RID &body) const override; 120 | virtual RID _body_get_shape(const RID &body, int32_t shape_idx) const override; 121 | virtual Transform2D _body_get_shape_transform(const RID &body, int32_t shape_idx) const override; 122 | virtual void _body_set_shape_disabled(const RID &body, int32_t shape_idx, bool disabled) override; 123 | virtual void _body_set_shape_as_one_way_collision(const RID &body, int32_t shape_idx, bool enable, double margin) override; 124 | virtual void _body_remove_shape(const RID &body, int32_t shape_idx) override; 125 | virtual void _body_clear_shapes(const RID &body) override; 126 | virtual void _body_attach_object_instance_id(const RID &body, uint64_t id) override; 127 | virtual uint64_t _body_get_object_instance_id(const RID &body) const override; 128 | virtual void _body_attach_canvas_instance_id(const RID &body, uint64_t id) override; 129 | virtual uint64_t _body_get_canvas_instance_id(const RID &body) const override; 130 | virtual void _body_set_continuous_collision_detection_mode(const RID &body, PhysicsServer2D::CCDMode mode) override; 131 | virtual PhysicsServer2D::CCDMode _body_get_continuous_collision_detection_mode(const RID &body) const override; 132 | virtual void _body_set_collision_layer(const RID &body, uint32_t layer) override; 133 | virtual uint32_t _body_get_collision_layer(const RID &body) const override; 134 | virtual void _body_set_collision_mask(const RID &body, uint32_t mask) override; 135 | virtual uint32_t _body_get_collision_mask(const RID &body) const override; 136 | virtual void _body_set_collision_priority(const RID &body, double priority) override; 137 | virtual double _body_get_collision_priority(const RID &body) const override; 138 | virtual void _body_set_param(const RID &body, PhysicsServer2D::BodyParameter param, const Variant &value) override; 139 | virtual Variant _body_get_param(const RID &body, PhysicsServer2D::BodyParameter param) const override; 140 | virtual void _body_reset_mass_properties(const RID &body) override; 141 | virtual void _body_set_state(const RID &body, PhysicsServer2D::BodyState state, const Variant &value) override; 142 | virtual Variant _body_get_state(const RID &body, PhysicsServer2D::BodyState state) const override; 143 | virtual void _body_apply_central_impulse(const RID &body, const Vector2 &impulse) override; 144 | virtual void _body_apply_torque_impulse(const RID &body, double impulse) override; 145 | virtual void _body_apply_impulse(const RID &body, const Vector2 &impulse, const Vector2 &position) override; 146 | virtual void _body_apply_central_force(const RID &body, const Vector2 &force) override; 147 | virtual void _body_apply_force(const RID &body, const Vector2 &force, const Vector2 &position) override; 148 | virtual void _body_apply_torque(const RID &body, double torque) override; 149 | virtual void _body_add_constant_central_force(const RID &body, const Vector2 &force) override; 150 | virtual void _body_add_constant_force(const RID &body, const Vector2 &force, const Vector2 &position) override; 151 | virtual void _body_add_constant_torque(const RID &body, double torque) override; 152 | virtual void _body_set_constant_force(const RID &body, const Vector2 &force) override; 153 | virtual Vector2 _body_get_constant_force(const RID &body) const override; 154 | virtual void _body_set_constant_torque(const RID &body, double torque) override; 155 | virtual double _body_get_constant_torque(const RID &body) const override; 156 | virtual void _body_set_axis_velocity(const RID &body, const Vector2 &axis_velocity) override; 157 | virtual void _body_add_collision_exception(const RID &body, const RID &excepted_body) override; 158 | virtual void _body_remove_collision_exception(const RID &body, const RID &excepted_body) override; 159 | virtual TypedArray _body_get_collision_exceptions(const RID &body) const override; 160 | virtual void _body_set_max_contacts_reported(const RID &body, int32_t amount) override; 161 | virtual int32_t _body_get_max_contacts_reported(const RID &body) const override; 162 | virtual void _body_set_contacts_reported_depth_threshold(const RID &body, double threshold) override; 163 | virtual double _body_get_contacts_reported_depth_threshold(const RID &body) const override; 164 | virtual void _body_set_omit_force_integration(const RID &body, bool enable) override; 165 | virtual bool _body_is_omitting_force_integration(const RID &body) const override; 166 | virtual void _body_set_state_sync_callback(const RID &body, const Callable &callable) override; 167 | virtual void _body_set_force_integration_callback(const RID &body, const Callable &callable, const Variant &userdata) override; 168 | virtual bool _body_collide_shape(const RID &body, int32_t body_shape, const RID &shape, const Transform2D &shape_xform, const Vector2 &motion, void *results, int32_t result_max, int32_t *result_count) override; 169 | virtual void _body_set_pickable(const RID &body, bool pickable) override; 170 | virtual PhysicsDirectBodyState2D *_body_get_direct_state(const RID &body) override; 171 | virtual bool _body_test_motion(const RID &body, const Transform2D &from, const Vector2 &motion, double margin, bool collide_separation_ray, bool recovery_as_collision, PhysicsServer2DExtensionMotionResult *result) const override; 172 | 173 | /* JOINT API */ 174 | virtual RID _joint_create() override; 175 | virtual void _joint_clear(const RID &joint) override; 176 | virtual void _joint_set_param(const RID &joint, PhysicsServer2D::JointParam param, double value) override; 177 | virtual double _joint_get_param(const RID &joint, PhysicsServer2D::JointParam param) const override; 178 | virtual void _joint_disable_collisions_between_bodies(const RID &joint, bool disable) override; 179 | virtual bool _joint_is_disabled_collisions_between_bodies(const RID &joint) const override; 180 | virtual void _joint_make_pin(const RID &joint, const Vector2 &anchor, const RID &body_a, const RID &body_b) override; 181 | virtual void _joint_make_groove(const RID &joint, const Vector2 &a_groove1, const Vector2 &a_groove2, const Vector2 &b_anchor, const RID &body_a, const RID &body_b) override; 182 | virtual void _joint_make_damped_spring(const RID &joint, const Vector2 &anchor_a, const Vector2 &anchor_b, const RID &body_a, const RID &body_b) override; 183 | virtual void _pin_joint_set_param(const RID &joint, PhysicsServer2D::PinJointParam param, double value) override; 184 | virtual double _pin_joint_get_param(const RID &joint, PhysicsServer2D::PinJointParam param) const override; 185 | virtual void _damped_spring_joint_set_param(const RID &joint, PhysicsServer2D::DampedSpringParam param, double value) override; 186 | virtual double _damped_spring_joint_get_param(const RID &joint, PhysicsServer2D::DampedSpringParam param) const override; 187 | virtual PhysicsServer2D::JointType _joint_get_type(const RID &joint) const override; 188 | 189 | /* MISC API */ 190 | virtual void _free_rid(const RID &rid) override; 191 | virtual void _set_active(bool active) override; 192 | virtual void _init() override; 193 | virtual void _step(double step) override; 194 | virtual void _sync() override; 195 | virtual void _flush_queries() override; 196 | virtual void _end_sync() override; 197 | virtual void _finish() override; 198 | virtual bool _is_flushing_queries() const override; 199 | virtual int32_t _get_process_info(PhysicsServer2D::ProcessInfo process_info) override; 200 | 201 | PhysicsServerBox2D(); 202 | ~PhysicsServerBox2D(); 203 | }; 204 | 205 | class PhysicsServerBox2DFactory : public Object { 206 | GDCLASS(PhysicsServerBox2DFactory, Object); 207 | 208 | protected: 209 | static void _bind_methods() { 210 | ClassDB::bind_method(D_METHOD("create_box2d_callback"), &PhysicsServerBox2DFactory::_create_box2d_callback); 211 | } 212 | 213 | public: 214 | PhysicsServer2D *_create_box2d_callback() { 215 | PhysicsServer2D *physics_server_2d = memnew(PhysicsServerBox2D()); 216 | return physics_server_2d; 217 | } 218 | }; 219 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape.h" 2 | #include "bodies/box2d_collision_object.h" 3 | #include "box2d_type_conversions.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace godot; 13 | 14 | class Box2DCollisionObject; 15 | 16 | class Box2DShape { 17 | RID self; 18 | 19 | protected: 20 | bool configured = false; 21 | PhysicsServer2D::ShapeType type; 22 | 23 | public: 24 | _FORCE_INLINE_ PhysicsServer2D::ShapeType get_type() const { return type; } 25 | 26 | _FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; } 27 | _FORCE_INLINE_ RID get_self() const { return self; } 28 | 29 | _FORCE_INLINE_ bool is_configured() const { return configured; } 30 | virtual void set_data(const Variant &p_data) = 0; 31 | virtual Variant get_data() const = 0; 32 | 33 | virtual int get_b2Shape_count(bool is_static) const = 0; 34 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) = 0; 35 | 36 | Box2DShape() { type = PhysicsServer2D::SHAPE_CUSTOM; } 37 | virtual ~Box2DShape(){}; 38 | }; 39 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_capsule.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_capsule.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | void Box2DShapeCapsule::set_data(const Variant &p_data) { 10 | ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY && p_data.get_type() != Variant::VECTOR2); 11 | if (p_data.get_type() == Variant::ARRAY) { 12 | Array arr = p_data; 13 | ERR_FAIL_COND(arr.size() != 2); 14 | height = arr[0]; 15 | radius = arr[1]; 16 | } else { 17 | Point2 p = p_data; 18 | radius = p.x; 19 | height = p.y; 20 | } 21 | if (radius < GODOT_LINEAR_SLOP) { 22 | radius = GODOT_LINEAR_SLOP; 23 | } 24 | if (height < GODOT_LINEAR_SLOP) { 25 | height = GODOT_LINEAR_SLOP; 26 | } 27 | configured = true; 28 | } 29 | 30 | Variant Box2DShapeCapsule::get_data() const { 31 | return Vector2(height, radius); 32 | } 33 | 34 | int Box2DShapeCapsule::get_b2Shape_count(bool is_static) const { 35 | // TODO: Better handle the degenerate case when the capsule is a sphere. 36 | return 3; 37 | } 38 | 39 | b2Shape *Box2DShapeCapsule::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 40 | ERR_FAIL_INDEX_V(p_index, 3, nullptr); 41 | if (p_index == 0 || p_index == 1) { 42 | b2CircleShape *shape = memnew(b2CircleShape); 43 | godot_to_box2d(radius, shape->m_radius); 44 | real_t circle_height = (height * 0.5 - radius) * (p_index == 0 ? 1.0 : -1.0); 45 | if (circle_height < b2_linearSlop) { 46 | circle_height = b2_linearSlop; 47 | } 48 | godot_to_box2d(p_transform.xform(Vector2(0, circle_height)), shape->m_p); 49 | return shape; 50 | } 51 | b2PolygonShape *shape = memnew(b2PolygonShape); 52 | Vector2 half_extents(radius, height * 0.5 - radius); 53 | if (half_extents.x < GODOT_LINEAR_SLOP) { 54 | half_extents.x = GODOT_LINEAR_SLOP; 55 | } 56 | if (half_extents.y < GODOT_LINEAR_SLOP) { 57 | half_extents.y = GODOT_LINEAR_SLOP; 58 | } 59 | b2Vec2 box2d_half_extents = godot_to_box2d(half_extents); 60 | b2Vec2 box2d_origin = godot_to_box2d(p_transform.get_origin()); 61 | shape->SetAsBox(box2d_half_extents.x, box2d_half_extents.y, box2d_origin, p_transform.get_rotation()); 62 | return shape; 63 | } 64 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_capsule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "box2d_shape.h" 4 | 5 | class Box2DShapeCapsule : public Box2DShape { 6 | real_t height = 0.0; 7 | real_t radius = 0.0; 8 | 9 | public: 10 | virtual void set_data(const Variant &p_data) override; 11 | virtual Variant get_data() const override; 12 | virtual int get_b2Shape_count(bool is_static) const override; 13 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 14 | 15 | Box2DShapeCapsule() { type = PhysicsServer2D::SHAPE_CAPSULE; } 16 | ~Box2DShapeCapsule() {} 17 | }; 18 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_circle.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_circle.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | void Box2DShapeCircle::set_data(const Variant &p_data) { 9 | ERR_FAIL_COND(p_data.get_type() != Variant::FLOAT && p_data.get_type() != Variant::INT); 10 | radius = variant_to_number(p_data); 11 | if (radius < GODOT_LINEAR_SLOP) { 12 | radius = GODOT_LINEAR_SLOP; 13 | } 14 | configured = true; 15 | } 16 | 17 | Variant Box2DShapeCircle::get_data() const { 18 | return radius; 19 | } 20 | 21 | b2Shape *Box2DShapeCircle::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 22 | ERR_FAIL_INDEX_V(p_index, 1, nullptr); 23 | b2CircleShape *shape = memnew(b2CircleShape); 24 | Vector2 scale = p_transform.get_scale(); 25 | if (scale.x != scale.y) { 26 | ERR_PRINT("Circles don't support non uniform scale."); 27 | } 28 | godot_to_box2d(radius * scale.x, shape->m_radius); 29 | if (shape->m_radius < b2_linearSlop) { 30 | shape->m_radius = b2_linearSlop; 31 | } 32 | godot_to_box2d(p_transform.get_origin(), shape->m_p); 33 | return shape; 34 | } 35 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_circle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape.h" 3 | 4 | using namespace godot; 5 | 6 | class Box2DShapeCircle : public Box2DShape { 7 | real_t radius = 0.0; 8 | 9 | public: 10 | virtual void set_data(const Variant &p_data) override; 11 | virtual Variant get_data() const override; 12 | virtual int get_b2Shape_count(bool is_static) const override { return 1; }; 13 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 14 | 15 | Box2DShapeCircle() { type = PhysicsServer2D::SHAPE_CIRCLE; } 16 | ~Box2DShapeCircle(){}; 17 | }; 18 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_concave_polygon.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_concave_polygon.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include "box2d_shape_convex_polygon.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | void Box2DShapeConcavePolygon::set_data(const Variant &p_data) { 12 | ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY); 13 | PackedVector2Array points_array = p_data; 14 | ERR_FAIL_COND(points_array.size() < 3); 15 | points.resize(points_array.size()); 16 | for (int i = 0; i < points_array.size(); i++) { 17 | points.write[i] = points_array[i]; 18 | } 19 | points = Box2DShapeConvexPolygon::remove_points_that_are_too_close(points); 20 | ERR_FAIL_COND(points.size() < 3); 21 | configured = true; 22 | } 23 | 24 | Variant Box2DShapeConcavePolygon::get_data() const { 25 | Array points_array; 26 | points_array.resize(points.size()); 27 | for (int i = 0; i < points.size(); i++) { 28 | points_array[i] = points[i]; 29 | } 30 | return points_array; 31 | } 32 | int Box2DShapeConcavePolygon::get_b2Shape_count(bool is_static) const { 33 | if (is_static) { 34 | return 1; 35 | } 36 | return points.size(); 37 | } 38 | 39 | b2Shape *Box2DShapeConcavePolygon::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 40 | // make a chain shape if it's static 41 | if (is_static) { 42 | ERR_FAIL_INDEX_V(p_index, 1, nullptr); 43 | b2ChainShape *shape = memnew(b2ChainShape); 44 | b2Vec2 *box2d_points = new b2Vec2[points.size()]; 45 | for (int i = 0; i < points.size(); i++) { 46 | godot_to_box2d(p_transform.xform(points[i]), box2d_points[i]); 47 | } 48 | int points_count = points.size(); 49 | points_count = Box2DShapeConvexPolygon::remove_bad_points(box2d_points, points_count); 50 | ERR_FAIL_COND_V(points_count < 3, nullptr); 51 | shape->CreateChain(box2d_points, points_count, box2d_points[points.size() - 1], box2d_points[0]); 52 | delete[] box2d_points; 53 | return shape; 54 | } 55 | ERR_FAIL_COND_V(p_index > points.size(), nullptr); 56 | // if not make multiple small squares the size of a line 57 | b2PolygonShape *shape = memnew(b2PolygonShape); 58 | b2Vec2 *box2d_points = new b2Vec2[4]; 59 | Vector2 a = points[p_index]; 60 | Vector2 b = points[(p_index + 1) % points.size()]; 61 | Vector2 dir = (a - b).normalized(); 62 | Vector2 right(dir.y, -dir.x); 63 | godot_to_box2d(p_transform.xform(a - right * 0.1), box2d_points[0]); 64 | godot_to_box2d(p_transform.xform(a + right * 0.1), box2d_points[1]); 65 | godot_to_box2d(p_transform.xform(b - right * 0.1), box2d_points[2]); 66 | godot_to_box2d(p_transform.xform(b + right * 0.1), box2d_points[3]); 67 | shape->Set(box2d_points, 4); 68 | delete[] box2d_points; 69 | return shape; 70 | } 71 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_concave_polygon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape.h" 3 | 4 | class Box2DShapeConcavePolygon : public Box2DShape { 5 | Vector points; 6 | 7 | public: 8 | virtual void set_data(const Variant &p_data) override; 9 | virtual Variant get_data() const override; 10 | virtual int get_b2Shape_count(bool is_static) const override; 11 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 12 | 13 | Box2DShapeConcavePolygon() { type = PhysicsServer2D::SHAPE_CONCAVE_POLYGON; } 14 | ~Box2DShapeConcavePolygon() {} 15 | }; 16 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_convex_polygon.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_convex_polygon.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | void Box2DShapeConvexPolygon::set_data(const Variant &p_data) { 10 | ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY); 11 | PackedVector2Array points_array = p_data; 12 | ERR_FAIL_COND(points_array.size() < 3); 13 | points.resize(points_array.size()); 14 | for (int i = 0; i < points_array.size(); i++) { 15 | points.write[i] = points_array[i]; 16 | } 17 | 18 | points = remove_points_that_are_too_close(points); 19 | ERR_FAIL_COND(points.size() < 3); 20 | polygons.clear(); 21 | int cut_polygon_size = 0; 22 | // Cut convex into small N<=8 gons 23 | for (int i = 0; i < points.size(); i += cut_polygon_size) { 24 | // check how big we should make the polygon with remaining points 25 | cut_polygon_size = std::min(b2_maxPolygonVertices, points.size() - i); 26 | int remaining_count = points.size() - (i + cut_polygon_size); 27 | // if after this we have less than 4 points remaining, make a smaller polygon 28 | if (remaining_count <= 4 && remaining_count != 0) { 29 | cut_polygon_size -= 4; 30 | } 31 | Vector cut_polygon; 32 | cut_polygon.resize(cut_polygon_size); 33 | for (int j = 0; j < cut_polygon_size; j++) { 34 | cut_polygon.write[j] = points[i + j]; 35 | } 36 | polygons.append(cut_polygon); 37 | } 38 | configured = true; 39 | } 40 | 41 | Variant Box2DShapeConvexPolygon::get_data() const { 42 | Array points_array; 43 | points_array.resize(points.size()); 44 | for (int i = 0; i < points.size(); i++) { 45 | points_array[i] = points[i]; 46 | } 47 | return points_array; 48 | } 49 | 50 | int Box2DShapeConvexPolygon::get_b2Shape_count(bool is_static) const { 51 | return polygons.size(); 52 | } 53 | 54 | Vector Box2DShapeConvexPolygon::remove_points_that_are_too_close(Vector points) { 55 | int32 tempCount = 0; 56 | Vector new_points; 57 | // remove points that are too close 58 | for (int32 i = 0; i < points.size(); ++i) { 59 | Vector2 v = points[i]; 60 | 61 | bool unique = true; 62 | for (int32 j = 0; j < tempCount; ++j) { 63 | if (v.distance_squared_to(new_points[j]) < ((0.5f * GODOT_LINEAR_SLOP) * (0.5f * GODOT_LINEAR_SLOP))) { 64 | unique = false; 65 | break; 66 | } 67 | } 68 | 69 | if (unique) { 70 | tempCount++; 71 | new_points.append(v); 72 | } 73 | } 74 | return new_points; 75 | } 76 | 77 | // based on https://github.com/briansemrau/godot_box2d/blob/5f55923fac81386e5735573e99d908d18efec6a1/scene/resources/box2d_shapes.cpp#L424 78 | int Box2DShapeConvexPolygon::remove_bad_points(b2Vec2 *vertices, int32 count) { 79 | int32 n = b2Min(count, b2_maxPolygonVertices); 80 | 81 | // Perform welding and copy vertices into local buffer. 82 | b2Vec2 ps[b2_maxPolygonVertices]; 83 | int32 tempCount = 0; 84 | for (int32 i = 0; i < n; ++i) { 85 | b2Vec2 v = vertices[i]; 86 | 87 | bool unique = true; 88 | for (int32 j = 0; j < tempCount; ++j) { 89 | if (b2DistanceSquared(v, ps[j]) < ((0.5f * b2_linearSlop) * (0.5f * b2_linearSlop))) { 90 | unique = false; 91 | break; 92 | } 93 | } 94 | 95 | if (unique) { 96 | ps[tempCount++] = v; 97 | } 98 | } 99 | 100 | n = tempCount; 101 | if (n < 3) { 102 | ERR_PRINT("Polygon has too few vertices after welding " + itos(n)); 103 | // Polygon has too few vertices 104 | return 0; 105 | } 106 | 107 | // Create the convex hull using the Gift wrapping algorithm 108 | // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm 109 | 110 | // Find the right most point on the hull 111 | int32 i0 = 0; 112 | float x0 = ps[0].x; 113 | for (int32 i = 1; i < n; ++i) { 114 | float x = ps[i].x; 115 | if (x > x0 || (x == x0 && ps[i].y < ps[i0].y)) { 116 | i0 = i; 117 | x0 = x; 118 | } 119 | } 120 | 121 | int32 hull[b2_maxPolygonVertices]; 122 | int32 m = 0; 123 | int32 ih = i0; 124 | 125 | for (;;) { 126 | if (m >= b2_maxPolygonVertices) { 127 | ERR_PRINT("Cannot compute convex hull."); 128 | // cannot compute convex hull 129 | return 0; 130 | } 131 | hull[m] = ih; 132 | 133 | int32 ie = 0; 134 | for (int32 j = 1; j < n; ++j) { 135 | if (ie == ih) { 136 | ie = j; 137 | continue; 138 | } 139 | 140 | b2Vec2 r = ps[ie] - ps[hull[m]]; 141 | b2Vec2 v = ps[j] - ps[hull[m]]; 142 | float c = b2Cross(r, v); 143 | if (c < 0.0f) { 144 | ie = j; 145 | } 146 | 147 | // Collinearity check 148 | if (c == 0.0f && v.LengthSquared() > r.LengthSquared()) { 149 | ie = j; 150 | } 151 | } 152 | 153 | ++m; 154 | ih = ie; 155 | 156 | if (ie == i0) { 157 | break; 158 | } 159 | } 160 | 161 | if (m < 3) { 162 | ERR_PRINT("Polygon is degenerate. Convex hull has point_count " + itos(m)); 163 | // Polygon is degenerate. 164 | return 0; 165 | } 166 | // Copy vertices. 167 | for (int32 i = 0; i < m; ++i) { 168 | vertices[i] = ps[hull[i]]; 169 | } 170 | return m; 171 | } 172 | 173 | b2Shape *Box2DShapeConvexPolygon::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 174 | ERR_FAIL_COND_V(p_index >= polygons.size(), nullptr); 175 | Vector polygon = polygons[p_index]; 176 | ERR_FAIL_COND_V(polygon.size() > b2_maxPolygonVertices, nullptr); 177 | ERR_FAIL_COND_V(polygon.size() < 3, nullptr); 178 | b2Vec2 b2_points[b2_maxPolygonVertices]; 179 | b2PolygonShape *polygon_shape = memnew(b2PolygonShape); 180 | for (int i = 0; i < polygon.size(); i++) { 181 | godot_to_box2d(p_transform.xform(polygon[i]), b2_points[i]); 182 | } 183 | int new_size = remove_bad_points(b2_points, polygon.size()); 184 | ERR_FAIL_COND_V(new_size < 3, nullptr); 185 | polygon_shape->Set(b2_points, new_size); 186 | return polygon_shape; 187 | } 188 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_convex_polygon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape.h" 3 | 4 | class Box2DShapeConvexPolygon : public Box2DShape { 5 | Vector points; 6 | Vector> polygons; 7 | 8 | public: 9 | static Vector remove_points_that_are_too_close(Vector points); 10 | static int remove_bad_points(b2Vec2 *vertices, int32 count); 11 | virtual void set_data(const Variant &p_data) override; 12 | virtual Variant get_data() const override; 13 | virtual int get_b2Shape_count(bool is_static) const override; 14 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 15 | 16 | Box2DShapeConvexPolygon() { type = PhysicsServer2D::SHAPE_CONVEX_POLYGON; } 17 | ~Box2DShapeConvexPolygon() {} 18 | }; 19 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_rectangle.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_rectangle.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | void Box2DShapeRectangle::set_data(const Variant &p_data) { 9 | ERR_FAIL_COND(p_data.get_type() != Variant::VECTOR2); 10 | half_extents = p_data; 11 | if (half_extents.x < GODOT_LINEAR_SLOP) { 12 | half_extents.x = GODOT_LINEAR_SLOP; 13 | } 14 | if (half_extents.y < GODOT_LINEAR_SLOP) { 15 | half_extents.y = GODOT_LINEAR_SLOP; 16 | } 17 | configured = true; 18 | } 19 | 20 | Variant Box2DShapeRectangle::get_data() const { 21 | return half_extents; 22 | } 23 | 24 | b2Shape *Box2DShapeRectangle::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 25 | ERR_FAIL_INDEX_V(p_index, 1, nullptr); 26 | b2PolygonShape *shape = memnew(b2PolygonShape); 27 | b2Vec2 box2d_half_extents = godot_to_box2d(half_extents); 28 | b2Vec2 *box2d_points = new b2Vec2[4]; 29 | godot_to_box2d(p_transform.xform(Vector2(-half_extents.x, -half_extents.y)), box2d_points[0]); 30 | godot_to_box2d(p_transform.xform(Vector2(-half_extents.x, half_extents.y)), box2d_points[1]); 31 | godot_to_box2d(p_transform.xform(Vector2(half_extents.x, half_extents.y)), box2d_points[2]); 32 | godot_to_box2d(p_transform.xform(Vector2(half_extents.x, -half_extents.y)), box2d_points[3]); 33 | shape->Set(box2d_points, 4); 34 | delete[] box2d_points; 35 | return shape; 36 | } 37 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_rectangle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape.h" 3 | 4 | class Box2DShapeRectangle : public Box2DShape { 5 | protected: 6 | Vector2 half_extents; 7 | 8 | public: 9 | virtual void set_data(const Variant &p_data) override; 10 | virtual Variant get_data() const override; 11 | virtual int get_b2Shape_count(bool is_static) const override { return 1; } 12 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 13 | 14 | Box2DShapeRectangle() { type = PhysicsServer2D::SHAPE_RECTANGLE; } 15 | ~Box2DShapeRectangle() {} 16 | }; 17 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_segment.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_segment.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void Box2DShapeSegment::set_data(const Variant &p_data) { 11 | ERR_FAIL_COND(p_data.get_type() != Variant::RECT2); 12 | Rect2 rect = p_data; 13 | a = rect.get_position(); 14 | b = rect.get_size(); 15 | half_extents = Vector2((a - b).length(), GODOT_LINEAR_SLOP); 16 | if (half_extents.x < GODOT_LINEAR_SLOP) { 17 | half_extents.x = GODOT_LINEAR_SLOP; 18 | } 19 | configured = true; 20 | } 21 | 22 | Variant Box2DShapeSegment::get_data() const { 23 | return Rect2(a, b); 24 | } 25 | 26 | b2Shape *Box2DShapeSegment::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 27 | ERR_FAIL_INDEX_V(p_index, 1, nullptr); 28 | // make a line if it's static 29 | if (is_static) { 30 | b2EdgeShape *shape = memnew(b2EdgeShape); 31 | b2Vec2 edge_endpoints[2]; 32 | godot_to_box2d(p_transform.xform(a), edge_endpoints[0]); 33 | godot_to_box2d(p_transform.xform(b), edge_endpoints[1]); 34 | if (one_way) { 35 | b2Vec2 dirV0 = edge_endpoints[0] - edge_endpoints[1]; 36 | shape->SetOneSided(edge_endpoints[1] + dirV0, edge_endpoints[0], edge_endpoints[1], edge_endpoints[0] - dirV0); 37 | } else { 38 | shape->SetTwoSided(edge_endpoints[0], edge_endpoints[1]); 39 | } 40 | return shape; 41 | } 42 | // make a square if not 43 | b2PolygonShape *shape = memnew(b2PolygonShape); 44 | b2Vec2 *box2d_points = new b2Vec2[4]; 45 | Vector2 dir = (a - b).normalized(); 46 | Vector2 right(dir.y, -dir.x); 47 | godot_to_box2d(p_transform.xform(a - right * 0.1), box2d_points[0]); 48 | godot_to_box2d(p_transform.xform(a + right * 0.1), box2d_points[1]); 49 | godot_to_box2d(p_transform.xform(b - right * 0.1), box2d_points[2]); 50 | godot_to_box2d(p_transform.xform(b + right * 0.1), box2d_points[3]); 51 | shape->Set(box2d_points, 4); 52 | delete[] box2d_points; 53 | return shape; 54 | } 55 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_segment.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape_rectangle.h" 3 | 4 | class Box2DShapeSegment : public Box2DShapeRectangle { 5 | protected: 6 | Vector2 a; 7 | Vector2 b; 8 | 9 | public: 10 | virtual void set_data(const Variant &p_data) override; 11 | virtual Variant get_data() const override; 12 | virtual int get_b2Shape_count(bool is_static) const override { return 1; }; 13 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 14 | 15 | Box2DShapeSegment() { type = PhysicsServer2D::SHAPE_SEGMENT; } 16 | ~Box2DShapeSegment() {} 17 | }; 18 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_separation_ray.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_separation_ray.h" 2 | #include "../box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void Box2DShapeSeparationRay::set_data(const Variant &p_data) { 11 | ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY); 12 | Dictionary dict = p_data; 13 | ERR_FAIL_COND(dict.size() != 2); 14 | ERR_FAIL_COND(!dict.has("length")); 15 | ERR_FAIL_COND(!dict.has("slide_on_slope")); 16 | float length = dict["length"]; 17 | bool slide_on_slope = dict["slide_on_slope"]; 18 | a = Vector2(); 19 | b = Vector2(0, length); 20 | half_extents = Vector2((a - b).length(), GODOT_LINEAR_SLOP); 21 | if (half_extents.x < GODOT_LINEAR_SLOP) { 22 | half_extents.x = GODOT_LINEAR_SLOP; 23 | } 24 | configured = true; 25 | } 26 | 27 | Variant Box2DShapeSeparationRay::get_data() const { 28 | Dictionary dict; 29 | dict["length"] = b.x; 30 | dict["slide_on_slope"] = false; 31 | return dict; 32 | } 33 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_separation_ray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape.h" 3 | #include "box2d_shape_segment.h" 4 | 5 | class Box2DShapeSeparationRay : public Box2DShapeSegment { 6 | public: 7 | virtual void set_data(const Variant &p_data) override; 8 | virtual Variant get_data() const override; 9 | 10 | Box2DShapeSeparationRay() { type = PhysicsServer2D::SHAPE_SEPARATION_RAY; } 11 | ~Box2DShapeSeparationRay() {} 12 | }; 13 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_world_boundary.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_shape_world_boundary.h" 2 | #include "box2d_type_conversions.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WORLD_SHAPE_SIZE 100000 10 | 11 | void Box2DShapeWorldBoundary::set_data(const Variant &p_data) { 12 | ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY); 13 | Array arr = p_data; 14 | ERR_FAIL_COND(arr.size() != 2); 15 | normal = arr[0]; 16 | distance = arr[1]; // no need to bring it to box2d here as we will do it later 17 | configured = true; 18 | } 19 | 20 | Variant Box2DShapeWorldBoundary::get_data() const { 21 | Array data; 22 | data.resize(2); 23 | data[0] = normal; 24 | data[1] = distance; // no need to bring it to godot here as we didn't cast this one 25 | return data; 26 | } 27 | 28 | b2Shape *Box2DShapeWorldBoundary::get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) { 29 | ERR_FAIL_INDEX_V(p_index, 1, nullptr); 30 | b2PolygonShape *shape = memnew(b2PolygonShape); 31 | b2Vec2 points[4]; 32 | Vector2 right(normal.y, -normal.x); 33 | Vector2 left(-right); 34 | left *= WORLD_SHAPE_SIZE; 35 | right *= WORLD_SHAPE_SIZE; 36 | left = left + normal * distance; 37 | right = right + normal * distance; 38 | godot_to_box2d(p_transform.xform(left), points[0]); 39 | godot_to_box2d(p_transform.xform(right), points[1]); 40 | godot_to_box2d(p_transform.xform(right - normal * WORLD_SHAPE_SIZE), points[2]); 41 | godot_to_box2d(p_transform.xform(right - normal * WORLD_SHAPE_SIZE), points[3]); 42 | shape->Set(points, 4); 43 | return shape; 44 | } 45 | -------------------------------------------------------------------------------- /src/shapes/box2d_shape_world_boundary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "box2d_shape_segment.h" 3 | #include 4 | 5 | class Box2DShapeWorldBoundary : public Box2DShapeSegment { 6 | Vector2 normal; 7 | real_t distance; 8 | 9 | public: 10 | virtual void set_data(const Variant &p_data) override; 11 | virtual Variant get_data() const override; 12 | virtual int get_b2Shape_count(bool is_static) const override { return 1; }; 13 | virtual b2Shape *get_transformed_b2Shape(int p_index, const Transform2D &p_transform, bool one_way, bool is_static) override; 14 | 15 | Box2DShapeWorldBoundary() { type = PhysicsServer2D::SHAPE_WORLD_BOUNDARY; } 16 | ~Box2DShapeWorldBoundary() {} 17 | }; 18 | -------------------------------------------------------------------------------- /src/spaces/box2d_direct_space_state.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_direct_space_state.h" 2 | 3 | #include "../b2_user_settings.h" 4 | 5 | #include "../bodies/box2d_collision_object.h" 6 | #include "../box2d_type_conversions.h" 7 | #include "box2d_query_callback.h" 8 | #include "box2d_ray_cast_callback.h" 9 | 10 | #include 11 | #include 12 | 13 | PhysicsDirectSpaceState2D *Box2DDirectSpaceState::get_space_state() { 14 | ERR_FAIL_NULL_V(space, nullptr); 15 | return space->get_direct_state(); 16 | } 17 | 18 | bool Box2DDirectSpaceState::_intersect_ray(const Vector2 &from, const Vector2 &to, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, bool hit_from_inside, PhysicsServer2DExtensionRayResult *result) { 19 | Box2DRayCastCallback callback(result, collision_mask, collide_with_bodies, collide_with_areas, hit_from_inside); 20 | space->get_b2World()->RayCast(&callback, godot_to_box2d(from), godot_to_box2d(to)); 21 | return callback.get_hit(); 22 | } 23 | int32_t Box2DDirectSpaceState::_intersect_point(const Vector2 &position, uint64_t canvas_instance_id, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, PhysicsServer2DExtensionShapeResult *results, int32_t max_results) { 24 | /* 25 | Box2DQueryCallback callback(results, collision_mask, collide_with_bodies, collide_with_areas, canvas_instance_id, max_results); 26 | b2Vec2 pos(godot_to_box2d(position)); 27 | b2AABB aabb; 28 | float point_size = 1.0f; 29 | 30 | aabb.lowerBound.Set(pos.x - point_size, pos.y - point_size); 31 | aabb.upperBound.Set(pos.x + point_size, pos.y + point_size); 32 | space->get_b2World()->QueryAABB(&callback, aabb); 33 | return callback.get_hit_count(); 34 | */ 35 | // TODO reenable this when I figure out how to send Object* from this method 36 | return 0; 37 | } 38 | int32_t Box2DDirectSpaceState::_intersect_shape(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, PhysicsServer2DExtensionShapeResult *result, int32_t max_results) { 39 | return 0; 40 | } 41 | bool Box2DDirectSpaceState::_cast_motion(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, float *closest_safe, float *closest_unsafe) { 42 | return false; 43 | } 44 | bool Box2DDirectSpaceState::_collide_shape(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, void *results, int32_t max_results, int32_t *result_count) { 45 | return false; 46 | } 47 | bool Box2DDirectSpaceState::_rest_info(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, PhysicsServer2DExtensionShapeRestInfo *rest_info) { 48 | return false; 49 | } 50 | -------------------------------------------------------------------------------- /src/spaces/box2d_direct_space_state.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "box2d_space.h" 9 | 10 | using namespace godot; 11 | 12 | class Box2DDirectSpaceState : public PhysicsDirectSpaceState2DExtension { 13 | GDCLASS(Box2DDirectSpaceState, PhysicsDirectSpaceState2DExtension); 14 | 15 | protected: 16 | static void _bind_methods() {} 17 | 18 | public: 19 | Box2DSpace *space = nullptr; 20 | virtual bool _intersect_ray(const Vector2 &from, const Vector2 &to, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, bool hit_from_inside, PhysicsServer2DExtensionRayResult *result) override; 21 | virtual int32_t _intersect_point(const Vector2 &position, uint64_t canvas_instance_id, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, PhysicsServer2DExtensionShapeResult *results, int32_t max_results) override; 22 | virtual int32_t _intersect_shape(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, PhysicsServer2DExtensionShapeResult *result, int32_t max_results) override; 23 | virtual bool _cast_motion(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, float *closest_safe, float *closest_unsafe) override; 24 | virtual bool _collide_shape(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, void *results, int32_t max_results, int32_t *result_count) override; 25 | virtual bool _rest_info(const RID &shape_rid, const Transform2D &transform, const Vector2 &motion, double margin, uint32_t collision_mask, bool collide_with_bodies, bool collide_with_areas, PhysicsServer2DExtensionShapeRestInfo *rest_info) override; 26 | 27 | PhysicsDirectSpaceState2D *get_space_state(); 28 | ~Box2DDirectSpaceState() override = default; 29 | }; 30 | -------------------------------------------------------------------------------- /src/spaces/box2d_query_callback.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_query_callback.h" 2 | 3 | #include "../b2_user_settings.h" 4 | 5 | Box2DQueryCallback::Box2DQueryCallback(PhysicsServer2DExtensionShapeResult *p_results, 6 | uint32_t p_collision_mask, 7 | bool p_collide_with_bodies, 8 | bool p_collide_with_areas, 9 | uint64_t p_canvas_instance_id, 10 | int32_t p_max_results) { 11 | results = p_results; 12 | collision_mask = p_collision_mask; 13 | collide_with_bodies = p_collide_with_bodies; 14 | collide_with_areas = p_collide_with_areas; 15 | canvas_instance_id = p_canvas_instance_id; 16 | max_results = p_max_results; 17 | } 18 | 19 | int32_t Box2DQueryCallback::get_hit_count() { 20 | return hit_count; 21 | } 22 | 23 | bool Box2DQueryCallback::ReportFixture(b2Fixture *fixture) { 24 | if ((fixture->GetFilterData().maskBits & collision_mask) != 0 && 25 | ((fixture->IsSensor() && collide_with_areas) || 26 | (!fixture->IsSensor() && collide_with_bodies))) { 27 | hit_count++; 28 | PhysicsServer2DExtensionShapeResult &result = *results++; 29 | result.shape = fixture->GetUserData().shape_idx; 30 | Box2DCollisionObject *collision_object = fixture->GetBody()->GetUserData().collision_object; 31 | result.rid = collision_object->get_self(); 32 | result.collider_id = collision_object->get_object_instance_id(); 33 | result.collider = collision_object->get_object(); 34 | } 35 | return max_results - hit_count > 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/spaces/box2d_query_callback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../bodies/box2d_collision_object.h" 4 | #include "../box2d_type_conversions.h" 5 | #include "box2d_direct_space_state.h" 6 | #include 7 | #include 8 | 9 | class Box2DQueryCallback : public b2QueryCallback { 10 | PhysicsServer2DExtensionShapeResult *results; 11 | uint32_t collision_mask; 12 | bool collide_with_bodies; 13 | bool collide_with_areas; 14 | uint64_t canvas_instance_id; 15 | int32_t max_results; 16 | int hit_count = 0; 17 | 18 | public: 19 | Box2DQueryCallback(PhysicsServer2DExtensionShapeResult *results, 20 | uint32_t collision_mask, 21 | bool collide_with_bodies, 22 | bool collide_with_areas, 23 | uint64_t canvas_instance_id, 24 | int32_t max_results); 25 | 26 | int32_t get_hit_count(); 27 | 28 | /// Called for each fixture found in the query AABB. 29 | /// @return false to terminate the query. 30 | virtual bool ReportFixture(b2Fixture *fixture) override; 31 | }; 32 | -------------------------------------------------------------------------------- /src/spaces/box2d_ray_cast_callback.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_ray_cast_callback.h" 2 | 3 | #include "../b2_user_settings.h" 4 | 5 | Box2DRayCastCallback::Box2DRayCastCallback(PhysicsServer2DExtensionRayResult *p_result, 6 | uint32_t p_collision_mask, 7 | bool p_collide_with_bodies, 8 | bool p_collide_with_areas, 9 | bool p_hit_from_inside) { 10 | result = p_result; 11 | collision_mask = p_collision_mask; 12 | collide_with_bodies = p_collide_with_bodies; 13 | collide_with_areas = p_collide_with_areas; 14 | hit_from_inside = p_hit_from_inside; 15 | } 16 | 17 | bool Box2DRayCastCallback::get_hit() { 18 | return hit; 19 | } 20 | 21 | float Box2DRayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, 22 | const b2Vec2 &normal, float fraction) { 23 | if ((fixture->GetFilterData().maskBits & collision_mask) != 0 && 24 | ((fixture->IsSensor() && collide_with_areas) || 25 | (!fixture->IsSensor() && collide_with_bodies))) { 26 | result->normal = box2d_to_godot(normal); 27 | result->position = box2d_to_godot(point); 28 | result->shape = fixture->GetUserData().shape_idx; 29 | Box2DCollisionObject *collision_object = fixture->GetBody()->GetUserData().collision_object; 30 | result->rid = collision_object->get_self(); 31 | result->collider_id = collision_object->get_object_instance_id(); 32 | result->collider = collision_object->get_object_unsafe(); 33 | hit = true; 34 | return 0; 35 | } 36 | return 1; 37 | } 38 | -------------------------------------------------------------------------------- /src/spaces/box2d_ray_cast_callback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../bodies/box2d_collision_object.h" 4 | #include "../box2d_type_conversions.h" 5 | #include "box2d_direct_space_state.h" 6 | #include 7 | #include 8 | 9 | class Box2DRayCastCallback : public b2RayCastCallback { 10 | PhysicsServer2DExtensionRayResult *result; 11 | uint32_t collision_mask; 12 | bool collide_with_bodies; 13 | bool collide_with_areas; 14 | bool hit_from_inside; 15 | bool hit = false; 16 | 17 | public: 18 | Box2DRayCastCallback(PhysicsServer2DExtensionRayResult *result, 19 | uint32_t collision_mask, 20 | bool collide_with_bodies, 21 | bool collide_with_areas, 22 | bool hit_from_inside); 23 | 24 | bool get_hit(); 25 | 26 | /// Called for each fixture found in the query. You control how the ray cast 27 | /// proceeds by returning a float: 28 | /// return -1: ignore this fixture and continue 29 | /// return 0: terminate the ray cast 30 | /// return fraction: clip the ray to this point 31 | /// return 1: don't clip the ray and continue 32 | /// @param fixture the fixture hit by the ray 33 | /// @param point the point of initial intersection 34 | /// @param normal the normal vector at the point of intersection 35 | /// @param fraction the fraction along the ray at the point of intersection 36 | /// @return -1 to filter, 0 to terminate, fraction to clip the ray for 37 | /// closest hit, 1 to continue 38 | virtual float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, 39 | const b2Vec2 &normal, float fraction) override; 40 | }; 41 | -------------------------------------------------------------------------------- /src/spaces/box2d_space.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_space.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../bodies/box2d_body.h" 9 | #include "../bodies/box2d_collision_object.h" 10 | #include "box2d_direct_space_state.h" 11 | #include "box2d_space_contact_filter.h" 12 | #include "box2d_space_contact_listener.h" 13 | 14 | /* PHYSICS SERVER API */ 15 | 16 | int Box2DSpace::get_active_body_count() { 17 | return process_info.active_body_count; 18 | } 19 | 20 | int32_t Box2DSpace::get_collision_pairs() { 21 | return world->GetContactCount(); 22 | } 23 | int32_t Box2DSpace::get_island_count() { 24 | return 0; // not sure if this is exposed 25 | } 26 | int32_t Box2DSpace::get_contact_count() const { 27 | int32 contact_count = world->GetContactCount(); 28 | int32_t contact_total = 0; 29 | b2Contact *contacts = world->GetContactList(); 30 | for (int i = 0; i < contact_count; i++) { 31 | contact_total += contacts->GetManifold()->pointCount; 32 | } 33 | return contact_total; 34 | } 35 | 36 | PackedVector2Array Box2DSpace::get_contacts() const { 37 | PackedVector2Array vector_array; 38 | int32 contact_count = world->GetContactCount(); 39 | int32_t contact_total = 0; 40 | b2Contact *contacts = world->GetContactList(); 41 | for (int i = 0; i < contact_count; i++) { 42 | b2WorldManifold worldManifold; 43 | contacts->GetWorldManifold(&worldManifold); 44 | for (int j = 0; j < contacts->GetManifold()->pointCount; j++) { 45 | vector_array.append(box2d_to_godot(worldManifold.points[j])); 46 | } 47 | } 48 | return vector_array; 49 | } 50 | 51 | void Box2DSpace::set_debug_contacts(int32_t max_contacts) { 52 | } 53 | 54 | void Box2DSpace::set_solver_iterations(int32 p_iterations) { 55 | solver_iterations = p_iterations; 56 | } 57 | 58 | int32 Box2DSpace::get_solver_iterations() const { 59 | return solver_iterations; 60 | } 61 | 62 | void Box2DSpace::step(float p_step) { 63 | const int32 velocityIterations = solver_iterations; 64 | const int32 positionIterations = solver_iterations; 65 | 66 | const SelfList::List *body_list = &get_active_body_list(); 67 | const SelfList *b = body_list->first(); 68 | while (b) { 69 | b->self()->before_step(); 70 | b = b->next(); 71 | } 72 | world->Step(p_step, velocityIterations, positionIterations); 73 | step_count++; 74 | 75 | body_list = &get_active_body_list(); 76 | b = body_list->first(); 77 | process_info.active_body_count = 0; 78 | while (b) { 79 | if (!b->self()->is_sleeping()) { 80 | process_info.active_body_count++; 81 | } 82 | b->self()->after_step(); 83 | b = b->next(); 84 | } 85 | } 86 | 87 | void Box2DSpace::call_queries() { 88 | while (state_query_list.first()) { 89 | Box2DBody *b = state_query_list.first()->self(); 90 | state_query_list.remove(state_query_list.first()); 91 | b->call_queries(); 92 | } 93 | // TODO: areas 94 | } 95 | 96 | Box2DDirectSpaceState *Box2DSpace::get_direct_state() { 97 | if (!direct_state) { 98 | direct_state = memnew(Box2DDirectSpaceState); 99 | direct_state->space = this; 100 | } 101 | return direct_state; 102 | } 103 | /* BODY API */ 104 | 105 | const SelfList::List &Box2DSpace::get_active_body_list() const { 106 | return active_list; 107 | } 108 | 109 | void Box2DSpace::body_add_to_active_list(SelfList *p_body) { 110 | active_list.add(p_body); 111 | } 112 | 113 | void Box2DSpace::body_remove_from_active_list(SelfList *p_body) { 114 | active_list.remove(p_body); 115 | } 116 | 117 | void Box2DSpace::body_add_to_state_query_list(SelfList *p_body) { 118 | state_query_list.add(p_body); 119 | } 120 | 121 | void Box2DSpace::body_remove_from_state_query_list(SelfList *p_body) { 122 | state_query_list.remove(p_body); 123 | } 124 | /* COLLISION OBJECT API */ 125 | void Box2DSpace::add_object(Box2DCollisionObject *p_object) { 126 | ERR_FAIL_COND(!p_object); 127 | p_object->set_b2Body(world->CreateBody(p_object->get_b2BodyDef())); 128 | } 129 | void Box2DSpace::remove_object(Box2DCollisionObject *p_object) { 130 | ERR_FAIL_COND(!p_object); 131 | world->DestroyBody(p_object->get_b2Body()); 132 | p_object->set_b2Body(nullptr); 133 | for (Box2DJoint *joint : p_object->get_joints()) { 134 | joint->set_b2Joint(nullptr); // joint is destroyed when destroying body 135 | } 136 | } 137 | /* JOINT API */ 138 | void Box2DSpace::create_joint(Box2DJoint *joint) { 139 | remove_joint(joint); 140 | // create joint once and if both body exist 141 | if (joint->is_configured()) { 142 | b2JointDef *joint_def = joint->get_b2JointDef(); 143 | joint->set_b2Joint(world->CreateJoint(joint_def)); 144 | } 145 | } 146 | void Box2DSpace::remove_joint(Box2DJoint *joint) { 147 | if (joint->get_b2Joint()) { 148 | world->DestroyJoint(joint->get_b2Joint()); 149 | joint->set_b2Joint(nullptr); 150 | } 151 | } 152 | /* DIRECT BODY STATE API */ 153 | 154 | double Box2DSpace::get_step() { 155 | return world->GetProfile().step; 156 | } 157 | 158 | Box2DSpace::Box2DSpace() { 159 | world = memnew(b2World(b2Vec2_zero)); // gravity comes from areas 160 | contact_filter = memnew(Box2DSpaceContactFilter); 161 | contact_listener = memnew(Box2DSpaceContactListener(this)); 162 | world->SetContactFilter(contact_filter); 163 | world->SetContactListener(contact_listener); 164 | } 165 | 166 | Box2DSpace::~Box2DSpace() { 167 | memdelete(world); 168 | memdelete(contact_filter); 169 | memdelete(contact_listener); 170 | if (direct_state) { 171 | memdelete(direct_state); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/spaces/box2d_space.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | using namespace godot; 11 | 12 | class Box2DCollisionObject; 13 | class Box2DBody; 14 | class Box2DDirectSpaceState; 15 | class Box2DJoint; 16 | class Box2DSpaceContactFilter; 17 | class Box2DSpaceContactListener; 18 | class Box2DArea; 19 | 20 | class Box2DSpace { 21 | private: 22 | RID self; 23 | 24 | b2World *world = nullptr; 25 | 26 | SelfList::List active_list; 27 | SelfList::List state_query_list; 28 | struct ProcessInfo { 29 | int active_body_count = 0; 30 | }; 31 | ProcessInfo process_info; 32 | bool locked = false; 33 | double solver_iterations = 8; 34 | 35 | Box2DDirectSpaceState *direct_state = nullptr; 36 | Box2DSpaceContactFilter *contact_filter; 37 | Box2DSpaceContactListener *contact_listener; 38 | int step_count = 0; // used for caching 39 | public: 40 | /* PHYSICS SERVER API */ 41 | int32_t get_active_body_count(); 42 | 43 | int32_t get_collision_pairs(); 44 | int32_t get_island_count(); 45 | int32_t get_contact_count() const; 46 | 47 | PackedVector2Array get_contacts() const; 48 | 49 | void set_debug_contacts(int32_t max_contacts); 50 | 51 | void set_solver_iterations(int32 iterations); 52 | int32 get_solver_iterations() const; 53 | 54 | void step(float p_step); 55 | 56 | void call_queries(); 57 | 58 | Box2DDirectSpaceState *get_direct_state(); 59 | /* BODY API */ 60 | const SelfList::List &get_active_body_list() const; 61 | void body_add_to_active_list(SelfList *p_body); 62 | void body_remove_from_active_list(SelfList *p_body); 63 | 64 | void body_add_to_state_query_list(SelfList *p_body); 65 | void body_remove_from_state_query_list(SelfList *p_body); 66 | /* COLLISION OBJECT API */ 67 | void add_object(Box2DCollisionObject *p_object); 68 | void remove_object(Box2DCollisionObject *p_object); 69 | /* JOINT API */ 70 | void create_joint(Box2DJoint *joint); 71 | void remove_joint(Box2DJoint *joint); 72 | /* BOX2D API */ 73 | b2World *get_b2World() const { return world; } 74 | 75 | /* DIRECT BODY STATE API */ 76 | double get_step(); 77 | 78 | _FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; } 79 | _FORCE_INLINE_ RID get_self() const { return self; } 80 | 81 | bool is_locked() const { return locked; } 82 | void lock() { locked = true; } 83 | void unlock() { locked = false; } 84 | 85 | Box2DSpace(); 86 | ~Box2DSpace(); 87 | }; 88 | -------------------------------------------------------------------------------- /src/spaces/box2d_space_contact_filter.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_space_contact_filter.h" 2 | 3 | #include "../b2_user_settings.h" 4 | 5 | #include "../bodies/box2d_collision_object.h" 6 | #include 7 | 8 | bool Box2DSpaceContactFilter::ShouldCollide(b2Fixture *fixtureA, b2Fixture *fixtureB) { 9 | Box2DCollisionObject *bodyA = fixtureA->GetBody()->GetUserData().collision_object; 10 | Box2DCollisionObject *bodyB = fixtureA->GetBody()->GetUserData().collision_object; 11 | 12 | const b2Filter &filterA = fixtureA->GetFilterData(); 13 | const b2Filter &filterB = fixtureB->GetFilterData(); 14 | 15 | bool collide = (filterA.maskBits & filterB.categoryBits) != 0 && (filterA.categoryBits & filterB.maskBits) != 0; 16 | return collide && !bodyA->is_body_collision_excepted(bodyB); 17 | } 18 | -------------------------------------------------------------------------------- /src/spaces/box2d_space_contact_filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Box2DSpaceContactFilter : public b2ContactFilter { 6 | public: 7 | /// Return true if contact calculations should be performed between these two shapes. 8 | /// @warning for performance reasons this is only called when the AABBs begin to overlap. 9 | virtual bool ShouldCollide(b2Fixture *fixtureA, b2Fixture *fixtureB) override; 10 | }; 11 | -------------------------------------------------------------------------------- /src/spaces/box2d_space_contact_listener.cpp: -------------------------------------------------------------------------------- 1 | #include "box2d_space_contact_listener.h" 2 | 3 | #include "../b2_user_settings.h" 4 | 5 | #include "../bodies/box2d_area.h" 6 | #include "../bodies/box2d_collision_object.h" 7 | #include "box2d/b2_contact.h" 8 | #include 9 | 10 | void Box2DSpaceContactListener::handle_contact(b2Contact *contact, PhysicsServer2D::AreaBodyStatus status) { 11 | Box2DCollisionObject *bodyA = contact->GetFixtureA()->GetBody()->GetUserData().collision_object; 12 | Box2DCollisionObject *bodyB = contact->GetFixtureB()->GetBody()->GetUserData().collision_object; 13 | int shapeA = contact->GetFixtureA()->GetUserData().shape_idx; 14 | int shapeB = contact->GetFixtureA()->GetUserData().shape_idx; 15 | if (bodyA->get_type() == Box2DCollisionObject::Type::TYPE_AREA) { 16 | if (bodyB->get_type() == Box2DCollisionObject::Type::TYPE_AREA) { 17 | ((Box2DArea *)bodyA)->call_area_monitor(((Box2DArea *)bodyB), status, bodyB->get_self(), bodyB->get_object_instance_id(), shapeB, shapeA); 18 | } else { 19 | ((Box2DArea *)bodyA)->call_monitor(bodyB, status, bodyB->get_self(), bodyB->get_object_instance_id(), shapeB, shapeA); 20 | } 21 | } else if (bodyB->get_type() == Box2DCollisionObject::Type::TYPE_AREA) { 22 | ((Box2DArea *)bodyB)->call_monitor(bodyA, status, bodyA->get_self(), bodyA->get_object_instance_id(), shapeA, shapeB); 23 | } 24 | } 25 | 26 | void Box2DSpaceContactListener::BeginContact(b2Contact *contact) { 27 | handle_contact(contact, PhysicsServer2D::AreaBodyStatus::AREA_BODY_ADDED); 28 | } 29 | 30 | void Box2DSpaceContactListener::EndContact(b2Contact *contact) { 31 | handle_contact(contact, PhysicsServer2D::AreaBodyStatus::AREA_BODY_REMOVED); 32 | } 33 | 34 | void Box2DSpaceContactListener::PreSolve(b2Contact *contact, const b2Manifold *oldManifold) { 35 | } 36 | 37 | void Box2DSpaceContactListener::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse) { 38 | } 39 | -------------------------------------------------------------------------------- /src/spaces/box2d_space_contact_listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace godot; 7 | 8 | class Box2DSpace; 9 | class Box2DArea; 10 | class Box2DBody; 11 | class Box2DCollisionObject; 12 | 13 | class Box2DSpaceContactListener : public b2ContactListener { 14 | Box2DSpace *space; 15 | void handle_contact(b2Contact *p_space, PhysicsServer2D::AreaBodyStatus status); 16 | 17 | public: 18 | Box2DSpaceContactListener(Box2DSpace *p_space) { space = p_space; } 19 | 20 | /// Called when two fixtures begin to touch. 21 | virtual void BeginContact(b2Contact *contact) override; 22 | 23 | /// Called when two fixtures cease to touch. 24 | virtual void EndContact(b2Contact *contact) override; 25 | 26 | /// This is called after a contact is updated. This allows you to inspect a 27 | /// contact before it goes to the solver. If you are careful, you can modify the 28 | /// contact manifold (e.g. disable contact). 29 | /// A copy of the old manifold is provided so that you can detect changes. 30 | /// Note: this is called only for awake bodies. 31 | /// Note: this is called even when the number of contact points is zero. 32 | /// Note: this is not called for sensors. 33 | /// Note: if you set the number of contact points to zero, you will not 34 | /// get an EndContact callback. However, you may get a BeginContact callback 35 | /// the next step. 36 | virtual void PreSolve(b2Contact *contact, const b2Manifold *oldManifold) override; 37 | 38 | /// This lets you inspect a contact after the solver is finished. This is useful 39 | /// for inspecting impulses. 40 | /// Note: the contact manifold does not include time of impact impulses, which can be 41 | /// arbitrarily large if the sub-step is small. Hence the impulse is provided explicitly 42 | /// in a separate data structure. 43 | /// Note: this is only called for contacts that are touching, solid, and awake. 44 | virtual void PostSolve(b2Contact *contact, const b2ContactImpulse *impulse) override; 45 | }; 46 | --------------------------------------------------------------------------------