├── .github └── workflows │ └── push.yml ├── .gitignore ├── LICENSE ├── README.md ├── apps ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── stephengold │ │ ├── lbjexamples │ │ ├── AppChooser.java │ │ ├── apps │ │ │ ├── HelloCcd.java │ │ │ ├── HelloCharacter.java │ │ │ ├── HelloCloth.java │ │ │ ├── HelloClothRigid.java │ │ │ ├── HelloContactResponse.java │ │ │ ├── HelloCustomShape.java │ │ │ ├── HelloDamping.java │ │ │ ├── HelloDeactivation.java │ │ │ ├── HelloDoor.java │ │ │ ├── HelloDoubleEnded.java │ │ │ ├── HelloGhost.java │ │ │ ├── HelloJoint.java │ │ │ ├── HelloKinematics.java │ │ │ ├── HelloLimit.java │ │ │ ├── HelloMadMallet.java │ │ │ ├── HelloMassDistribution.java │ │ │ ├── HelloMinkowski.java │ │ │ ├── HelloMotor.java │ │ │ ├── HelloNewHinge.java │ │ │ ├── HelloNonUniformGravity.java │ │ │ ├── HelloPin.java │ │ │ ├── HelloRigidBody.java │ │ │ ├── HelloServo.java │ │ │ ├── HelloSoftBody.java │ │ │ ├── HelloSoftRope.java │ │ │ ├── HelloSoftSoft.java │ │ │ ├── HelloSport.java │ │ │ ├── HelloSpring.java │ │ │ ├── HelloStaticBody.java │ │ │ ├── HelloVehicle.java │ │ │ ├── HelloWalk.java │ │ │ ├── HelloWind.java │ │ │ ├── console │ │ │ │ ├── HelloLibbulletjme.java │ │ │ │ ├── HelloVehicle0.java │ │ │ │ ├── SpeedTest.java │ │ │ │ └── package-info.java │ │ │ └── package-info.java │ │ └── package-info.java │ │ └── shapes │ │ └── custom │ │ ├── CustomBox.java │ │ ├── CustomCone.java │ │ ├── CustomCylinder.java │ │ ├── CustomEllipsoid.java │ │ ├── CustomFrustum.java │ │ ├── CustomHalfCylinder.java │ │ ├── CustomHemisphere.java │ │ ├── CustomLemon.java │ │ ├── CustomParaboloid.java │ │ ├── CustomSegment.java │ │ └── package-info.java │ └── resources │ └── Textures │ ├── Terrain │ └── splat │ │ └── mountains512.png │ ├── TextureTest.png │ └── greenTile.png ├── bash └── meld.sh ├── build.gradle.kts ├── config └── checkstyle │ ├── checkstyle.xml │ └── java-header ├── docs ├── build.gradle.kts ├── en │ ├── antora.yml │ └── modules │ │ └── ROOT │ │ ├── images │ │ ├── CustomConvex.png │ │ ├── box.png │ │ ├── box2d.png │ │ ├── capsule.png │ │ ├── compound.png │ │ ├── cone.png │ │ ├── convex2d.png │ │ ├── conveyorDemo.png │ │ ├── cylinder.png │ │ ├── frustum.png │ │ ├── gimpact.png │ │ ├── heightfield.png │ │ ├── hull.png │ │ ├── margin1.png │ │ ├── margin4.png │ │ ├── margin5.png │ │ ├── margin6.png │ │ ├── mesh.png │ │ ├── minkowski.png │ │ ├── multiSphere.png │ │ ├── newtonsCradle.png │ │ ├── pachinko.png │ │ ├── plane.png │ │ ├── segment.png │ │ ├── simplex.png │ │ ├── sphere.png │ │ ├── splitDemo.png │ │ ├── testGearJoint.png │ │ ├── thousandCubes.png │ │ └── windlass.png │ │ ├── nav.adoc │ │ └── pages │ │ ├── add.adoc │ │ ├── build.adoc │ │ ├── character.adoc │ │ ├── collision.adoc │ │ ├── debug.adoc │ │ ├── demos.adoc │ │ ├── implementation.adoc │ │ ├── lexicon.adoc │ │ ├── misc.adoc │ │ ├── new6dof.adoc │ │ ├── overview.adoc │ │ ├── rigidbody.adoc │ │ ├── shape.adoc │ │ ├── softbody.adoc │ │ ├── sport.adoc │ │ └── vehicle.adoc └── playbook.yml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kotlin-apps ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ └── com │ │ └── github │ │ └── stephengold │ │ └── lbjexamples │ │ └── ktapps │ │ ├── HelloCcd.kt │ │ ├── HelloCharacter.kt │ │ ├── HelloCloth.kt │ │ ├── HelloClothRigid.kt │ │ ├── HelloContactResponse.kt │ │ ├── HelloDamping.kt │ │ ├── HelloDeactivation.kt │ │ ├── HelloDoubleEnded.kt │ │ ├── HelloGhost.kt │ │ ├── HelloJoint.kt │ │ ├── HelloKinematics.kt │ │ ├── HelloLimit.kt │ │ ├── HelloMadMallet.kt │ │ ├── HelloMassDistribution.kt │ │ ├── HelloMotor.kt │ │ ├── HelloNonUniformGravity.kt │ │ ├── HelloPin.kt │ │ ├── HelloRigidBody.kt │ │ ├── HelloServo.kt │ │ ├── HelloSoftBody.kt │ │ ├── HelloSoftRope.kt │ │ ├── HelloSoftSoft.kt │ │ ├── HelloSport.kt │ │ ├── HelloSpring.kt │ │ ├── HelloStaticBody.kt │ │ ├── HelloVehicle.kt │ │ ├── HelloWalk.kt │ │ └── console │ │ ├── HelloLibbulletjme.kt │ │ └── HelloVehicle0.kt │ └── resources │ └── Textures │ ├── Terrain │ └── splat │ │ └── mountains512.png │ └── greenTile.png ├── package-lock.json ├── package.json └── settings.gradle.kts /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # GitHub Actions workflow for commits pushed to the LbjExamples repo - all branches 3 | 4 | name: CI at GitHub 5 | on: [push] 6 | 7 | jobs: 8 | Java8-Linux: 9 | if: contains(toJson(github.event.commits), '[ci skip] ') == false 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/setup-java@v4 13 | with: 14 | distribution: 'zulu' 15 | java-version: 8 16 | - uses: actions/checkout@v4 17 | - run: ./gradlew javadoc --console=plain --stacktrace 18 | - run: ./gradlew :apps:HelloLibbulletjme --console=plain --stacktrace 19 | - run: ./gradlew :apps:HelloVehicle0 --console=plain --stacktrace 20 | 21 | Java11-Linux: 22 | if: contains(toJson(github.event.commits), '[ci skip] ') == false 23 | runs-on: ubuntu-24.04 24 | steps: 25 | - uses: actions/setup-java@v4 26 | with: 27 | distribution: 'zulu' 28 | java-version: 11 29 | - uses: actions/checkout@v4 30 | - uses: gradle/actions/wrapper-validation@v4 31 | - run: ./gradlew javadoc --console=plain --stacktrace 32 | - run: ./gradlew :apps:HelloLibbulletjme --console=plain --stacktrace 33 | - run: ./gradlew :apps:HelloVehicle0 --console=plain --stacktrace 34 | 35 | Java17-MacOS: 36 | if: contains(toJson(github.event.commits), '[ci skip] ') == false 37 | runs-on: macOS-13 38 | steps: 39 | - uses: actions/setup-java@v4 40 | with: 41 | distribution: 'zulu' 42 | java-version: 17 43 | - uses: actions/checkout@v4 44 | - run: ./gradlew javadoc --console=plain --stacktrace 45 | - run: ./gradlew :apps:HelloLibbulletjme --console=plain --stacktrace 46 | - run: ./gradlew :apps:HelloVehicle0 --console=plain --stacktrace 47 | 48 | Java21-MacOS: 49 | if: contains(toJson(github.event.commits), '[ci skip] ') == false 50 | runs-on: macOS-15 51 | steps: 52 | - uses: actions/setup-java@v4 53 | with: 54 | distribution: 'zulu' 55 | java-version: 21 56 | - uses: actions/checkout@v4 57 | - run: ./gradlew javadoc --console=plain --stacktrace 58 | - run: ./gradlew :apps:HelloLibbulletjme --console=plain --stacktrace 59 | - run: ./gradlew :apps:HelloVehicle0 --console=plain --stacktrace 60 | 61 | Java23-Windows: 62 | if: contains(toJson(github.event.commits), '[ci skip] ') == false 63 | runs-on: windows-2025 64 | steps: 65 | - uses: actions/setup-java@v4 66 | with: 67 | distribution: 'zulu' 68 | java-version: 23 69 | - uses: actions/checkout@v4 70 | - run: ./gradlew javadoc --console=plain --stacktrace 71 | shell: bash 72 | - run: ./gradlew :apps:HelloLibbulletjme --console=plain --stacktrace 73 | shell: bash 74 | - run: ./gradlew :apps:HelloVehicle0 --console=plain --stacktrace 75 | shell: bash 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle's project-specific cache directory: 2 | /.gradle/ 3 | 4 | # Ignore Gradle's build output directories: 5 | /apps/build/ 6 | /build/ 7 | /docs/build/ 8 | /kotlin-apps/build/ 9 | 10 | /.kotlin/ 11 | /node_modules/ 12 | 13 | # JVM crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | 16 | # Ignore IDE-specific directories: 17 | /.idea/ 18 | /.nb-gradle/ 19 | 20 | # Ignore extracted native libraries: 21 | *.dll 22 | *.dylib 23 | *.so 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Software packages Copyright (c) 2017-2024 Stephen Gold and Yanis Boudiaf 4 | mountains512.png Copyright (c) 2009-2022 jMonkeyEngine 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Libbulletjme Project logo 2 | 3 | [The LbjExamples Project][project] provides 4 | documentation and example applications 5 | for [the Libbulletjme 3-D physics library][libbulletjme]. 6 | 7 | It contains 3 subprojects: 8 | 9 | 1. docs: [Antora] documentation for Libbulletjme, including the tutorial 10 | 2. apps: [Java] applications referenced in the tutorial 11 | 3. kotlin-apps: [Kotlin] translations of certain apps 12 | 13 | The applications make use of [the SPORT graphics engine][sport], 14 | which was formerly a subproject and 15 | is now a separate project at [GitHub]. 16 | 17 | Complete source code is provided under 18 | [a 3-clause BSD license][license]. 19 | 20 | 21 | ## How to build and run LbjExamples from source 22 | 23 | ### Initial build 24 | 25 | 1. Install a [Java Development Kit (JDK)][adoptium], 26 | if you don't already have one. 27 | 2. Point the `JAVA_HOME` environment variable to your JDK installation: 28 | (In other words, set it to the path of a directory/folder 29 | containing a "bin" that contains a Java executable. 30 | That path might look something like 31 | "C:\Program Files\Eclipse Adoptium\jdk-17.0.3.7-hotspot" 32 | or "/usr/lib/jvm/java-17-openjdk-amd64/" or 33 | "/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home" .) 34 | + using Bash or Zsh: `export JAVA_HOME="` *path to installation* `"` 35 | + using [Fish]: `set -g JAVA_HOME "` *path to installation* `"` 36 | + using Windows Command Prompt: `set JAVA_HOME="` *path to installation* `"` 37 | + using PowerShell: `$env:JAVA_HOME = '` *path to installation* `'` 38 | 3. Download and extract the LbjExamples source code from GitHub: 39 | + using [Git]: 40 | + `git clone https://github.com/stephengold/LbjExamples.git` 41 | + `cd LbjExamples` 42 | 4. Run the [Gradle] wrapper: 43 | + using Bash or Fish or PowerShell or Zsh: `./gradlew build` 44 | + using Windows Command Prompt: `.\gradlew build` 45 | 46 | ### Tutorials 47 | 48 | The tutorial apps all have names starting with "Hello". 49 | For instance, the first tutorial app is named "HelloLibbulletjme". 50 | 51 | To execute "HelloLibbulletjme": 52 | + using Bash or Fish or PowerShell or Zsh: `./gradlew :apps:HelloLibbulletjme` 53 | + using Windows Command Prompt: `.\gradlew :apps:HelloLibbulletjme` 54 | 55 | ### Chooser 56 | 57 | A [Swing]-based chooser application is provided. 58 | However, it includes only the graphical apps and doesn't work on macOS yet. 59 | 60 | To run the chooser: 61 | + using Bash or Fish or PowerShell or Zsh: `./gradlew AppChooser` 62 | + using Windows Command Prompt: `.\gradlew AppChooser` 63 | 64 | ### Cleanup 65 | 66 | You can restore the project to a pristine state: 67 | + using Bash or Fish or PowerShell or Zsh: `./gradlew clean` 68 | + using Windows Command Prompt: `.\gradlew clean` 69 | 70 | 71 | [adoptium]: https://adoptium.net/releases.html "Adoptium" 72 | [antora]: https://antora.org/ "Antora site generator" 73 | [fish]: https://fishshell.com/ "Fish command-line shell" 74 | [git]: https://git-scm.com "Git version-control system" 75 | [github]: https://en.wikipedia.org/wiki/GitHub "GitHub" 76 | [gradle]: https://gradle.org "Gradle build tool" 77 | [java]: https://en.wikipedia.org/wiki/Java_(programming_language) "Java programming language" 78 | [kotlin]: https://en.wikipedia.org/wiki/Kotlin_(programming_language) "Kotlin programming language" 79 | [libbulletjme]: https://stephengold.github.io/Libbulletjme/lbj-en/English/overview.html "Libbulletjme Project" 80 | [license]: https://github.com/stephengold/LbjExamples/blob/master/LICENSE "LbjExamples license" 81 | [project]: https://github.com/stephengold/LbjExamples "LbjExamples Project" 82 | [sport]: https://github.com/stephengold/sport "SPORT Project" 83 | [swing]: https://en.wikipedia.org/wiki/Swing_(Java) "Swing toolkit" 84 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloCcd.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp; 32 | import com.jme3.bullet.PhysicsSpace; 33 | import com.jme3.bullet.collision.shapes.CollisionShape; 34 | import com.jme3.bullet.collision.shapes.CylinderCollisionShape; 35 | import com.jme3.bullet.collision.shapes.SphereCollisionShape; 36 | import com.jme3.bullet.objects.PhysicsBody; 37 | import com.jme3.bullet.objects.PhysicsRigidBody; 38 | import com.jme3.math.Vector3f; 39 | 40 | /** 41 | * A simple example of continuous collision detection (CCD). 42 | *

43 | * Builds upon HelloStaticBody. 44 | * 45 | * @author Stephen Gold sgold@sonic.net 46 | */ 47 | public class HelloCcd extends BasePhysicsApp { 48 | // ************************************************************************* 49 | // constructors 50 | 51 | /** 52 | * Instantiate the HelloCcd application. 53 | *

54 | * This no-arg constructor was made explicit to avoid javadoc warnings from 55 | * JDK 18+. 56 | */ 57 | public HelloCcd() { 58 | // do nothing 59 | } 60 | // ************************************************************************* 61 | // new methods exposed 62 | 63 | /** 64 | * Main entry point for the HelloCcd application. 65 | * 66 | * @param arguments array of command-line arguments (not null) 67 | */ 68 | public static void main(String[] arguments) { 69 | HelloCcd application = new HelloCcd(); 70 | application.start(); 71 | } 72 | // ************************************************************************* 73 | // BasePhysicsApp methods 74 | 75 | /** 76 | * Create the PhysicsSpace. Invoked once during initialization. 77 | * 78 | * @return a new object 79 | */ 80 | @Override 81 | public PhysicsSpace createSpace() { 82 | PhysicsSpace result 83 | = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT); 84 | 85 | // Increase gravity to make the balls fall faster. 86 | result.setGravity(new Vector3f(0f, -100f, 0f)); 87 | 88 | return result; 89 | } 90 | 91 | /** 92 | * Initialize the application. Invoked once. 93 | */ 94 | @Override 95 | public void initialize() { 96 | super.initialize(); 97 | setVsync(true); 98 | } 99 | 100 | /** 101 | * Populate the PhysicsSpace. Invoked once during initialization. 102 | */ 103 | @Override 104 | public void populateSpace() { 105 | // Create a CollisionShape for balls. 106 | float ballRadius = 0.1f; 107 | CollisionShape ballShape = new SphereCollisionShape(ballRadius); 108 | 109 | // Create 2 dynamic balls, one with CCD and one without, 110 | // and add them to the space. 111 | float mass = 1f; 112 | PhysicsRigidBody ccdBall = new PhysicsRigidBody(ballShape, mass); 113 | physicsSpace.addCollisionObject(ccdBall); 114 | ccdBall.setCcdMotionThreshold(ballRadius); 115 | ccdBall.setCcdSweptSphereRadius(ballRadius); 116 | ccdBall.setPhysicsLocation(new Vector3f(-1f, 4f, 0f)); 117 | 118 | PhysicsRigidBody controlBall = new PhysicsRigidBody(ballShape, mass); 119 | physicsSpace.addCollisionObject(controlBall); 120 | controlBall.setPhysicsLocation(new Vector3f(1f, 4f, 0f)); 121 | 122 | // Create a thin, static disc and add it to the space. 123 | float discRadius = 2f; 124 | float discThickness = 0.05f; 125 | CollisionShape discShape = new CylinderCollisionShape( 126 | discRadius, discThickness, PhysicsSpace.AXIS_Y); 127 | PhysicsRigidBody disc 128 | = new PhysicsRigidBody(discShape, PhysicsBody.massForStatic); 129 | physicsSpace.addCollisionObject(disc); 130 | 131 | // Visualize the shapes of all 3 rigid bodies: 132 | visualizeShape(ccdBall); 133 | visualizeShape(controlBall); 134 | visualizeShape(disc).setProgram("Unshaded/Monochrome"); 135 | } 136 | 137 | /** 138 | * Advance the physics simulation by the specified amount. Invoked during 139 | * each update. 140 | * 141 | * @param wallClockSeconds the elapsed wall-clock time since the previous 142 | * invocation of {@code updatePhysics} (in seconds, ≥0) 143 | */ 144 | @Override 145 | public void updatePhysics(float wallClockSeconds) { 146 | // For clarity, simulate at 1/10th normal speed. 147 | float simulateSeconds = 0.1f * wallClockSeconds; 148 | physicsSpace.update(simulateSeconds); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloCloth.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.Mesh; 32 | import com.github.stephengold.sport.mesh.ClothGrid; 33 | import com.github.stephengold.sport.physics.BasePhysicsApp; 34 | import com.github.stephengold.sport.physics.LinksGeometry; 35 | import com.jme3.bullet.PhysicsSoftSpace; 36 | import com.jme3.bullet.PhysicsSpace; 37 | import com.jme3.bullet.collision.shapes.SphereCollisionShape; 38 | import com.jme3.bullet.objects.PhysicsBody; 39 | import com.jme3.bullet.objects.PhysicsRigidBody; 40 | import com.jme3.bullet.objects.PhysicsSoftBody; 41 | import com.jme3.bullet.objects.infos.SoftBodyConfig; 42 | import com.jme3.bullet.objects.infos.SoftBodyMaterial; 43 | import com.jme3.bullet.util.NativeSoftBodyUtil; 44 | import com.jme3.math.Vector3f; 45 | 46 | /** 47 | * A simple cloth simulation using a soft body. 48 | *

49 | * Builds upon HelloSoftBody. 50 | * 51 | * @author Stephen Gold sgold@sonic.net 52 | */ 53 | public class HelloCloth extends BasePhysicsApp { 54 | // ************************************************************************* 55 | // constructors 56 | 57 | /** 58 | * Instantiate the HelloCloth application. 59 | *

60 | * This no-arg constructor was made explicit to avoid javadoc warnings from 61 | * JDK 18+. 62 | */ 63 | public HelloCloth() { 64 | // do nothing 65 | } 66 | // ************************************************************************* 67 | // new methods exposed 68 | 69 | /** 70 | * Main entry point for the HelloCloth application. 71 | * 72 | * @param arguments array of command-line arguments (not null) 73 | */ 74 | public static void main(String[] arguments) { 75 | HelloCloth application = new HelloCloth(); 76 | application.start(); 77 | } 78 | // ************************************************************************* 79 | // BasePhysicsApp methods 80 | 81 | /** 82 | * Create the PhysicsSpace. Invoked once during initialization. 83 | * 84 | * @return a new object 85 | */ 86 | @Override 87 | public PhysicsSoftSpace createSpace() { 88 | Vector3f worldMin = new Vector3f(-999f, -999f, -999f); 89 | Vector3f worldMax = new Vector3f(+999f, +999f, +999f); 90 | PhysicsSoftSpace result = new PhysicsSoftSpace( 91 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT); 92 | 93 | return result; 94 | } 95 | 96 | /** 97 | * Initialize the application. Invoked once. 98 | */ 99 | @Override 100 | public void initialize() { 101 | super.initialize(); 102 | setVsync(true); 103 | 104 | // Relocate the camera. 105 | cam.setLocation(new Vector3f(0f, 1f, 8f)); 106 | } 107 | 108 | /** 109 | * Populate the PhysicsSpace. Invoked once during initialization. 110 | */ 111 | @Override 112 | public void populateSpace() { 113 | // Create a static, rigid sphere and add it to the physics space. 114 | float radius = 1f; 115 | SphereCollisionShape shape = new SphereCollisionShape(radius); 116 | PhysicsRigidBody sphere 117 | = new PhysicsRigidBody(shape, PhysicsBody.massForStatic); 118 | physicsSpace.addCollisionObject(sphere); 119 | visualizeShape(sphere); 120 | 121 | // Generate a subdivided square mesh with alternating diagonals. 122 | int numLines = 41; 123 | float lineSpacing = 0.1f; // mesh units 124 | Mesh squareGrid = new ClothGrid(numLines, numLines, lineSpacing); 125 | 126 | // Create a soft square and add it to the physics space. 127 | PhysicsSoftBody cloth = new PhysicsSoftBody(); 128 | NativeSoftBodyUtil.appendFromTriMesh(squareGrid, cloth); 129 | physicsSpace.addCollisionObject(cloth); 130 | /* 131 | * Make the cloth flexible by reducing the angular stiffness 132 | * of its material. 133 | */ 134 | SoftBodyMaterial mat = cloth.getSoftMaterial(); 135 | mat.setAngularStiffness(0f); // default=1 136 | /* 137 | * Improve simulation accuracy by increasing 138 | * the number of position-solver iterations for the cloth. 139 | */ 140 | SoftBodyConfig config = cloth.getSoftConfig(); 141 | config.setPositionIterations(9); // default=1 142 | 143 | // Translate the cloth upward to its starting location. 144 | cloth.applyTranslation(new Vector3f(0f, 2f, 0f)); 145 | 146 | // Visualize the soft-body links: 147 | new LinksGeometry(cloth); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloDamping.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp; 32 | import com.jme3.bullet.PhysicsSpace; 33 | import com.jme3.bullet.collision.shapes.BoxCollisionShape; 34 | import com.jme3.bullet.collision.shapes.CollisionShape; 35 | import com.jme3.bullet.objects.PhysicsRigidBody; 36 | import com.jme3.math.Vector3f; 37 | 38 | /** 39 | * A simple example illustrating the effect of damping on dynamic rigid bodies. 40 | *

41 | * Builds upon HelloRigidBody. 42 | * 43 | * @author Stephen Gold sgold@sonic.net 44 | */ 45 | public class HelloDamping extends BasePhysicsApp { 46 | // ************************************************************************* 47 | // constructors 48 | 49 | /** 50 | * Instantiate the HelloDamping application. 51 | *

52 | * This no-arg constructor was made explicit to avoid javadoc warnings from 53 | * JDK 18+. 54 | */ 55 | public HelloDamping() { 56 | // do nothing 57 | } 58 | // ************************************************************************* 59 | // new methods exposed 60 | 61 | /** 62 | * Main entry point for the HelloDamping application. 63 | * 64 | * @param arguments array of command-line arguments (not null) 65 | */ 66 | public static void main(String[] arguments) { 67 | HelloDamping application = new HelloDamping(); 68 | application.start(); 69 | } 70 | // ************************************************************************* 71 | // BasePhysicsApp methods 72 | 73 | /** 74 | * Create the PhysicsSpace. Invoked once during initialization. 75 | * 76 | * @return a new object 77 | */ 78 | @Override 79 | public PhysicsSpace createSpace() { 80 | PhysicsSpace result 81 | = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT); 82 | 83 | // For clarity, disable gravity. 84 | result.setGravity(Vector3f.ZERO); 85 | 86 | return result; 87 | } 88 | 89 | /** 90 | * Populate the PhysicsSpace. Invoked once during initialization. 91 | */ 92 | @Override 93 | public void populateSpace() { 94 | // Create a CollisionShape for unit cubes. 95 | float cubeHalfExtent = 0.5f; 96 | CollisionShape cubeShape = new BoxCollisionShape(cubeHalfExtent); 97 | 98 | // Create 4 cubes (dynamic rigid bodies) and add them to the space. 99 | int numCubes = 4; 100 | float cubeMass = 2f; 101 | PhysicsRigidBody[] cube = new PhysicsRigidBody[numCubes]; 102 | for (int cubeIndex = 0; cubeIndex < numCubes; ++cubeIndex) { 103 | cube[cubeIndex] = new PhysicsRigidBody(cubeShape, cubeMass); 104 | physicsSpace.addCollisionObject(cube[cubeIndex]); 105 | 106 | // Disable sleep (deactivation) for clarity. 107 | cube[cubeIndex].setEnableSleep(false); 108 | } 109 | 110 | // Locate the cubes 4 psu apart, center to center. 111 | cube[0].setPhysicsLocation(new Vector3f(0f, +2f, 0f)); 112 | cube[1].setPhysicsLocation(new Vector3f(4f, +2f, 0f)); 113 | cube[2].setPhysicsLocation(new Vector3f(0f, -2f, 0f)); 114 | cube[3].setPhysicsLocation(new Vector3f(4f, -2f, 0f)); 115 | 116 | // Give each cube its own set of damping parameters (linear, angular). 117 | cube[0].setDamping(0f, 0f); 118 | cube[1].setDamping(0f, 0.9f); 119 | cube[2].setDamping(0.9f, 0f); 120 | cube[3].setDamping(0.9f, 0.9f); 121 | /* 122 | * Apply an off-center impulse to each cube, 123 | * causing it to drift and spin. 124 | */ 125 | Vector3f impulse = new Vector3f(-1f, 0f, 0f); 126 | Vector3f offset = new Vector3f(0f, 1f, 1f); 127 | for (int cubeIndex = 0; cubeIndex < numCubes; ++cubeIndex) { 128 | cube[cubeIndex].applyImpulse(impulse, offset); 129 | } 130 | 131 | // Visualize the shapes of all 4 cubes: 132 | for (int cubeIndex = 0; cubeIndex < numCubes; ++cubeIndex) { 133 | visualizeShape(cube[cubeIndex]); 134 | } 135 | } 136 | 137 | /** 138 | * Advance the physics simulation by the specified amount. Invoked during 139 | * each update. 140 | * 141 | * @param wallClockSeconds the elapsed wall-clock time since the previous 142 | * invocation of {@code updatePhysics} (in seconds, ≥0) 143 | */ 144 | @Override 145 | public void updatePhysics(float wallClockSeconds) { 146 | physicsSpace.update(wallClockSeconds); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloMadMallet.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.input.RotateMode; 32 | import com.github.stephengold.sport.physics.BasePhysicsApp; 33 | import com.jme3.bullet.PhysicsSpace; 34 | import com.jme3.bullet.collision.shapes.CollisionShape; 35 | import com.jme3.bullet.collision.shapes.CompoundCollisionShape; 36 | import com.jme3.bullet.collision.shapes.CylinderCollisionShape; 37 | import com.jme3.bullet.objects.PhysicsBody; 38 | import com.jme3.bullet.objects.PhysicsRigidBody; 39 | import com.jme3.math.Vector3f; 40 | 41 | /** 42 | * A simple example of a dynamic rigid body with an implausible center. 43 | *

44 | * Builds upon HelloStaticBody. 45 | * 46 | * @author Stephen Gold sgold@sonic.net 47 | */ 48 | public class HelloMadMallet extends BasePhysicsApp { 49 | // ************************************************************************* 50 | // constructors 51 | 52 | /** 53 | * Instantiate the HelloMadMallet application. 54 | *

55 | * This no-arg constructor was made explicit to avoid javadoc warnings from 56 | * JDK 18+. 57 | */ 58 | public HelloMadMallet() { 59 | // do nothing 60 | } 61 | // ************************************************************************* 62 | // new methods exposed 63 | 64 | /** 65 | * Main entry point for the HelloMadMallet application. 66 | * 67 | * @param arguments array of command-line arguments (not null) 68 | */ 69 | public static void main(String[] arguments) { 70 | HelloMadMallet application = new HelloMadMallet(); 71 | application.start(); 72 | } 73 | // ************************************************************************* 74 | // BasePhysicsApp methods 75 | 76 | /** 77 | * Create the PhysicsSpace. Invoked once during initialization. 78 | * 79 | * @return a new object 80 | */ 81 | @Override 82 | public PhysicsSpace createSpace() { 83 | PhysicsSpace result 84 | = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT); 85 | result.setGravity(new Vector3f(0f, -50f, 0f)); 86 | 87 | return result; 88 | } 89 | 90 | /** 91 | * Initialize the application. Invoked once. 92 | */ 93 | @Override 94 | public void initialize() { 95 | super.initialize(); 96 | setVsync(true); 97 | getCameraInputProcessor().setRotationMode(RotateMode.DragLMB); 98 | 99 | // Position the camera for a good view. 100 | cam.setLocation(new Vector3f(10f, -2.75f, 0f)); 101 | cam.setUpAngle(0.05f); 102 | cam.setAzimuth(-3.05f); 103 | } 104 | 105 | /** 106 | * Populate the PhysicsSpace. Invoked once during initialization. 107 | */ 108 | @Override 109 | public void populateSpace() { 110 | // Construct a compound shape for the mallet. 111 | float headLength = 1f; 112 | float headRadius = 0.5f; 113 | Vector3f hes = new Vector3f(headLength / 2f, headRadius, headRadius); 114 | CollisionShape headShape 115 | = new CylinderCollisionShape(hes, PhysicsSpace.AXIS_X); 116 | 117 | float handleLength = 3f; 118 | float handleRadius = 0.3f; 119 | hes.set(handleRadius, handleRadius, handleLength / 2f); 120 | CollisionShape handleShape 121 | = new CylinderCollisionShape(hes, PhysicsSpace.AXIS_Z); 122 | 123 | CompoundCollisionShape malletShape = new CompoundCollisionShape(); 124 | malletShape.addChildShape(handleShape, 0f, 0f, handleLength / 2f); 125 | malletShape.addChildShape(headShape, 0f, 0f, handleLength); 126 | 127 | // Create a dynamic body for the mallet. 128 | float mass = 2f; 129 | PhysicsRigidBody mallet = new PhysicsRigidBody(malletShape, mass); 130 | mallet.setPhysicsLocation(new Vector3f(0f, 4f, 0f)); 131 | 132 | // Increase the mallet's angular damping to stabilize it. 133 | mallet.setAngularDamping(0.9f); 134 | 135 | physicsSpace.addCollisionObject(mallet); 136 | 137 | // Create a static disc and add it to the space. 138 | float discRadius = 5f; 139 | float discThickness = 0.5f; 140 | CollisionShape discShape = new CylinderCollisionShape( 141 | discRadius, discThickness, PhysicsSpace.AXIS_Y); 142 | PhysicsRigidBody disc 143 | = new PhysicsRigidBody(discShape, PhysicsBody.massForStatic); 144 | physicsSpace.addCollisionObject(disc); 145 | disc.setPhysicsLocation(new Vector3f(0f, -3f, 0f)); 146 | 147 | // Visualize the mallet, including its local axes. 148 | visualizeShape(mallet); 149 | float debugAxisLength = 1f; 150 | visualizeAxes(mallet, debugAxisLength); 151 | 152 | // Visualize the shape of the disc: 153 | visualizeShape(disc); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloMinkowski.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.input.RotateMode; 32 | import com.github.stephengold.sport.physics.BasePhysicsApp; 33 | import com.jme3.bullet.PhysicsSpace; 34 | import com.jme3.bullet.collision.shapes.ConeCollisionShape; 35 | import com.jme3.bullet.collision.shapes.MinkowskiSum; 36 | import com.jme3.bullet.objects.PhysicsBody; 37 | import com.jme3.bullet.objects.PhysicsRigidBody; 38 | import com.jme3.math.Vector3f; 39 | 40 | /** 41 | * A simple example of a MinkowskiSum collision shape. 42 | *

43 | * Builds upon HelloStaticBody. 44 | * 45 | * @author Stephen Gold sgold@sonic.net 46 | */ 47 | public class HelloMinkowski extends BasePhysicsApp { 48 | // ************************************************************************* 49 | // constructors 50 | 51 | /** 52 | * Instantiate the HelloMinkowski application. 53 | *

54 | * This no-arg constructor was made explicit to avoid javadoc warnings from 55 | * JDK 18+. 56 | */ 57 | public HelloMinkowski() { 58 | // do nothing 59 | } 60 | // ************************************************************************* 61 | // new methods exposed 62 | 63 | /** 64 | * Main entry point for the HelloMinkowski application. 65 | * 66 | * @param arguments array of command-line arguments (not null) 67 | */ 68 | public static void main(String[] arguments) { 69 | HelloMinkowski application = new HelloMinkowski(); 70 | application.start(); 71 | } 72 | // ************************************************************************* 73 | // BasePhysicsApp methods 74 | 75 | /** 76 | * Create the PhysicsSpace. Invoked once during initialization. 77 | * 78 | * @return a new object 79 | */ 80 | @Override 81 | public PhysicsSpace createSpace() { 82 | PhysicsSpace result 83 | = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT); 84 | return result; 85 | } 86 | 87 | /** 88 | * Initialize the application. Invoked once. 89 | */ 90 | @Override 91 | public void initialize() { 92 | super.initialize(); 93 | 94 | setVsync(true); 95 | getCameraInputProcessor().setRotationMode(RotateMode.DragLMB); 96 | } 97 | 98 | /** 99 | * Populate the PhysicsSpace. Invoked once during initialization. 100 | */ 101 | @Override 102 | public void populateSpace() { 103 | // Add a static rigid body with a cone shape. 104 | float radius = 1f; 105 | float height = 1f; 106 | ConeCollisionShape coneShape = new ConeCollisionShape(radius, height); 107 | PhysicsRigidBody cone 108 | = new PhysicsRigidBody(coneShape, PhysicsBody.massForStatic); 109 | cone.setPhysicsLocation(new Vector3f(3f, 0f, 0f)); 110 | physicsSpace.addCollisionObject(cone); 111 | visualizeShape(cone); 112 | 113 | // Add a static rigid body with a Minkowski-sum shape. 114 | MinkowskiSum sumShape = new MinkowskiSum(coneShape, coneShape); 115 | PhysicsRigidBody sum 116 | = new PhysicsRigidBody(sumShape, PhysicsBody.massForStatic); 117 | physicsSpace.addCollisionObject(sum); 118 | visualizeShape(sum); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloRigidBody.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp; 32 | import com.jme3.bullet.PhysicsSpace; 33 | import com.jme3.bullet.collision.shapes.CollisionShape; 34 | import com.jme3.bullet.collision.shapes.SphereCollisionShape; 35 | import com.jme3.bullet.objects.PhysicsRigidBody; 36 | import com.jme3.math.Vector3f; 37 | 38 | /** 39 | * A simple example of 2 colliding balls, illustrating the 5 basic features of 40 | * responsive, dynamic, rigid bodies:

47 | *

48 | * Builds upon HelloSport. 49 | * 50 | * @author Stephen Gold sgold@sonic.net 51 | */ 52 | public class HelloRigidBody extends BasePhysicsApp { 53 | // ************************************************************************* 54 | // constructors 55 | 56 | /** 57 | * Instantiate the HelloRigidBody application. 58 | *

59 | * This no-arg constructor was made explicit to avoid javadoc warnings from 60 | * JDK 18+. 61 | */ 62 | public HelloRigidBody() { 63 | // do nothing 64 | } 65 | // ************************************************************************* 66 | // new methods exposed 67 | 68 | /** 69 | * Main entry point for the HelloRigidBody application. 70 | * 71 | * @param arguments array of command-line arguments (not null) 72 | */ 73 | public static void main(String[] arguments) { 74 | HelloRigidBody application = new HelloRigidBody(); 75 | application.start(); 76 | } 77 | // ************************************************************************* 78 | // BasePhysicsApp methods 79 | 80 | /** 81 | * Create the PhysicsSpace. Invoked once during initialization. 82 | * 83 | * @return a new object 84 | */ 85 | @Override 86 | public PhysicsSpace createSpace() { 87 | PhysicsSpace result 88 | = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT); 89 | return result; 90 | } 91 | 92 | /** 93 | * Populate the PhysicsSpace. Invoked once during initialization. 94 | */ 95 | @Override 96 | public void populateSpace() { 97 | // Create a collision shape for balls. 98 | float ballRadius = 1f; 99 | CollisionShape ballShape = new SphereCollisionShape(ballRadius); 100 | 101 | // Create 2 balls (dynamic rigid bodies) and add them to the space. 102 | float ballMass = 2f; 103 | PhysicsRigidBody ball1 = new PhysicsRigidBody(ballShape, ballMass); 104 | physicsSpace.addCollisionObject(ball1); 105 | PhysicsRigidBody ball2 = new PhysicsRigidBody(ballShape, ballMass); 106 | physicsSpace.addCollisionObject(ball2); 107 | 108 | // Locate the balls initially 2 PSU (physics-space units) apart. 109 | // In other words, 4 PSU from center to center. 110 | ball1.setPhysicsLocation(new Vector3f(1f, 1f, 0f)); 111 | ball2.setPhysicsLocation(new Vector3f(5f, 1f, 0f)); 112 | 113 | // Set ball #2 on a collision course with ball #1. 114 | ball2.applyCentralImpulse(new Vector3f(-25f, 0f, 0f)); 115 | 116 | // Visualize the shapes of both rigid bodies: 117 | visualizeShape(ball1); 118 | visualizeShape(ball2); 119 | } 120 | 121 | /** 122 | * Advance the physics simulation by the specified amount. Invoked during 123 | * each update. 124 | * 125 | * @param wallClockSeconds the elapsed wall-clock time since the previous 126 | * invocation of {@code updatePhysics} (in seconds, ≥0) 127 | */ 128 | @Override 129 | public void updatePhysics(float wallClockSeconds) { 130 | // For clarity, simulate at 1/10th normal speed. 131 | float simulateSeconds = 0.1f * wallClockSeconds; 132 | physicsSpace.update(simulateSeconds); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloSoftBody.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.Mesh; 32 | import com.github.stephengold.sport.mesh.IcosphereMesh; 33 | import com.github.stephengold.sport.physics.BasePhysicsApp; 34 | import com.github.stephengold.sport.physics.FacesGeometry; 35 | import com.jme3.bullet.PhysicsSoftSpace; 36 | import com.jme3.bullet.PhysicsSpace; 37 | import com.jme3.bullet.collision.shapes.BoxCollisionShape; 38 | import com.jme3.bullet.objects.PhysicsBody; 39 | import com.jme3.bullet.objects.PhysicsRigidBody; 40 | import com.jme3.bullet.objects.PhysicsSoftBody; 41 | import com.jme3.bullet.objects.infos.Sbcp; 42 | import com.jme3.bullet.objects.infos.SoftBodyConfig; 43 | import com.jme3.bullet.util.NativeSoftBodyUtil; 44 | import com.jme3.math.Vector3f; 45 | 46 | /** 47 | * A simple example of a soft body colliding with a static rigid body. 48 | *

49 | * Builds upon HelloStaticBody. 50 | * 51 | * @author Stephen Gold sgold@sonic.net 52 | */ 53 | public class HelloSoftBody extends BasePhysicsApp { 54 | // ************************************************************************* 55 | // constructors 56 | 57 | /** 58 | * Instantiate the HelloSoftBody application. 59 | *

60 | * This no-arg constructor was made explicit to avoid javadoc warnings from 61 | * JDK 18+. 62 | */ 63 | public HelloSoftBody() { 64 | // do nothing 65 | } 66 | // ************************************************************************* 67 | // new methods exposed 68 | 69 | /** 70 | * Main entry point for the HelloSoftBody application. 71 | * 72 | * @param arguments array of command-line arguments (not null) 73 | */ 74 | public static void main(String[] arguments) { 75 | HelloSoftBody application = new HelloSoftBody(); 76 | application.start(); 77 | } 78 | // ************************************************************************* 79 | // BasePhysicsApp methods 80 | 81 | /** 82 | * Create the PhysicsSpace. Invoked once during initialization. 83 | * 84 | * @return a new object 85 | */ 86 | @Override 87 | public PhysicsSoftSpace createSpace() { 88 | Vector3f worldMin = new Vector3f(-999f, -999f, -999f); 89 | Vector3f worldMax = new Vector3f(+999f, +999f, +999f); 90 | PhysicsSoftSpace result = new PhysicsSoftSpace( 91 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT); 92 | 93 | return result; 94 | } 95 | 96 | /** 97 | * Initialize the application. Invoked once. 98 | */ 99 | @Override 100 | public void initialize() { 101 | super.initialize(); 102 | setVsync(true); 103 | 104 | // Relocate the camera. 105 | cam.setLocation(new Vector3f(0f, 1f, 8f)); 106 | } 107 | 108 | /** 109 | * Populate the PhysicsSpace. Invoked once during initialization. 110 | */ 111 | @Override 112 | public void populateSpace() { 113 | addBox(); 114 | 115 | // A mesh is used to generate the shape and topology of the soft body. 116 | int numRefinementIterations = 3; 117 | boolean indexed = true; 118 | Mesh mesh = new IcosphereMesh(numRefinementIterations, indexed); 119 | 120 | // Create a soft ball and add it to the physics space. 121 | PhysicsSoftBody body = new PhysicsSoftBody(); 122 | NativeSoftBodyUtil.appendFromTriMesh(mesh, body); 123 | physicsSpace.addCollisionObject(body); 124 | /* 125 | * Set the ball's default frame pose: if deformed, 126 | * it will tend to return to its current shape. 127 | */ 128 | boolean setVolumePose = false; 129 | boolean setFramePose = true; 130 | body.setPose(setVolumePose, setFramePose); 131 | 132 | // Enable pose matching to make the body bouncy. 133 | SoftBodyConfig config = body.getSoftConfig(); 134 | config.set(Sbcp.PoseMatching, 0.05f); 135 | 136 | // Translate the body to its start location. 137 | body.applyTranslation(new Vector3f(0f, 3f, 0f)); 138 | 139 | // Visualize the soft-body faces: 140 | new FacesGeometry(body); 141 | } 142 | // ************************************************************************* 143 | // private methods 144 | 145 | /** 146 | * Add a large static cube to serve as a platform. 147 | */ 148 | private void addBox() { 149 | float halfExtent = 3f; // mesh units 150 | BoxCollisionShape shape = new BoxCollisionShape(halfExtent); 151 | 152 | PhysicsRigidBody body 153 | = new PhysicsRigidBody(shape, PhysicsBody.massForStatic); 154 | body.setPhysicsLocation(new Vector3f(0f, -halfExtent, 0f)); 155 | physicsSpace.addCollisionObject(body); 156 | 157 | visualizeShape(body); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloSoftRope.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.Mesh; 32 | import com.github.stephengold.sport.mesh.DividedLine; 33 | import com.github.stephengold.sport.physics.BasePhysicsApp; 34 | import com.github.stephengold.sport.physics.LinksGeometry; 35 | import com.github.stephengold.sport.physics.PinsGeometry; 36 | import com.jme3.bullet.PhysicsSoftSpace; 37 | import com.jme3.bullet.PhysicsSpace; 38 | import com.jme3.bullet.objects.PhysicsBody; 39 | import com.jme3.bullet.objects.PhysicsSoftBody; 40 | import com.jme3.bullet.util.NativeSoftBodyUtil; 41 | import com.jme3.math.Vector3f; 42 | 43 | /** 44 | * A simple rope simulation using a soft body. 45 | *

46 | * Builds upon HelloPin. 47 | * 48 | * @author Stephen Gold sgold@sonic.net 49 | */ 50 | public class HelloSoftRope extends BasePhysicsApp { 51 | // ************************************************************************* 52 | // constructors 53 | 54 | /** 55 | * Instantiate the HelloSoftRope application. 56 | *

57 | * This no-arg constructor was made explicit to avoid javadoc warnings from 58 | * JDK 18+. 59 | */ 60 | public HelloSoftRope() { 61 | // do nothing 62 | } 63 | // ************************************************************************* 64 | // new methods exposed 65 | 66 | /** 67 | * Main entry point for the HelloSoftRope application. 68 | * 69 | * @param arguments array of command-line arguments (not null) 70 | */ 71 | public static void main(String[] arguments) { 72 | HelloSoftRope application = new HelloSoftRope(); 73 | application.start(); 74 | } 75 | // ************************************************************************* 76 | // BasePhysicsApp methods 77 | 78 | /** 79 | * Create the PhysicsSpace. Invoked once during initialization. 80 | * 81 | * @return a new object 82 | */ 83 | @Override 84 | public PhysicsSoftSpace createSpace() { 85 | Vector3f worldMin = new Vector3f(-999f, -999f, -999f); 86 | Vector3f worldMax = new Vector3f(+999f, +999f, +999f); 87 | PhysicsSoftSpace result = new PhysicsSoftSpace( 88 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT); 89 | 90 | return result; 91 | } 92 | 93 | /** 94 | * Initialize the application. Invoked once. 95 | */ 96 | @Override 97 | public void initialize() { 98 | super.initialize(); 99 | setVsync(true); 100 | 101 | // Relocate the camera: 102 | cam.setLocation(new Vector3f(0f, 1f, 8f)); 103 | } 104 | 105 | /** 106 | * Populate the PhysicsSpace with bodies. Invoked once during 107 | * initialization. 108 | */ 109 | @Override 110 | public void populateSpace() { 111 | // Generate a subdivided line segment: 112 | int numSegments = 40; 113 | Vector3f endPoint1 = new Vector3f(0f, 4f, 0f); 114 | Vector3f endPoint2 = new Vector3f(2f, 4f, 2f); 115 | Mesh lineMesh = new DividedLine(endPoint1, endPoint2, numSegments); 116 | 117 | // Create a soft body and add it to the physics space: 118 | PhysicsSoftBody rope = new PhysicsSoftBody(); 119 | NativeSoftBodyUtil.appendFromLineMesh(lineMesh, rope); 120 | physicsSpace.addCollisionObject(rope); 121 | 122 | // Pin one of the end nodes by zeroing its mass: 123 | int nodeIndex = 0; 124 | rope.setNodeMass(nodeIndex, PhysicsBody.massForStatic); 125 | 126 | // Visualize the soft-body links and the pin: 127 | new LinksGeometry(rope); 128 | new PinsGeometry(rope); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloSport.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp; 32 | import com.jme3.bullet.PhysicsSpace; 33 | import com.jme3.bullet.collision.shapes.CollisionShape; 34 | import com.jme3.bullet.collision.shapes.PlaneCollisionShape; 35 | import com.jme3.bullet.collision.shapes.SphereCollisionShape; 36 | import com.jme3.bullet.objects.PhysicsBody; 37 | import com.jme3.bullet.objects.PhysicsRigidBody; 38 | import com.jme3.math.Plane; 39 | import com.jme3.math.Vector3f; 40 | 41 | /** 42 | * Drop a dynamic sphere onto a horizontal surface and visualize them both using 43 | * SPORT graphics. 44 | *

45 | * Builds upon HelloLibbulletjme. 46 | * 47 | * @author Stephen Gold sgold@sonic.net 48 | */ 49 | public class HelloSport extends BasePhysicsApp { 50 | // ************************************************************************* 51 | // constructors 52 | 53 | /** 54 | * Instantiate the HelloSport application. 55 | *

56 | * This no-arg constructor was made explicit to avoid javadoc warnings from 57 | * JDK 18+. 58 | */ 59 | public HelloSport() { 60 | // do nothing 61 | } 62 | // ************************************************************************* 63 | // new methods exposed 64 | 65 | /** 66 | * Main entry point for the HelloSport application. 67 | * 68 | * @param arguments array of command-line arguments (not null) 69 | */ 70 | public static void main(String[] arguments) { 71 | HelloSport application = new HelloSport(); 72 | application.start(); 73 | /* 74 | * During initialization, BasePhysicsApp loads the native library 75 | * and invokes createSpace() and populateSpace(). 76 | */ 77 | } 78 | // ************************************************************************* 79 | // BasePhysicsApp methods 80 | 81 | /** 82 | * Create the PhysicsSpace. Invoked once during initialization. 83 | * 84 | * @return a new object 85 | */ 86 | @Override 87 | public PhysicsSpace createSpace() { 88 | PhysicsSpace.BroadphaseType bPhase = PhysicsSpace.BroadphaseType.DBVT; 89 | return new PhysicsSpace(bPhase); 90 | } 91 | 92 | /** 93 | * Populate the PhysicsSpace. Invoked once during initialization. 94 | */ 95 | @Override 96 | public void populateSpace() { 97 | // Add a static horizontal plane at y=-1: 98 | float groundY = -1f; 99 | Plane plane = new Plane(Vector3f.UNIT_Y, groundY); 100 | CollisionShape planeShape = new PlaneCollisionShape(plane); 101 | float mass = PhysicsBody.massForStatic; 102 | PhysicsRigidBody floor = new PhysicsRigidBody(planeShape, mass); 103 | physicsSpace.addCollisionObject(floor); 104 | 105 | // Add a sphere-shaped, dynamic, rigid body at the origin: 106 | float ballRadius = 0.3f; 107 | CollisionShape ballShape = new SphereCollisionShape(ballRadius); 108 | mass = 1f; 109 | PhysicsRigidBody ball = new PhysicsRigidBody(ballShape, mass); 110 | physicsSpace.addCollisionObject(ball); 111 | 112 | // Visualize the shapes of both rigid bodies: 113 | visualizeShape(floor); 114 | visualizeShape(ball); 115 | } 116 | 117 | /** 118 | * Advance the physics simulation by the specified amount. Invoked during 119 | * each update. 120 | * 121 | * @param wallClockSeconds the elapsed wall-clock time since the previous 122 | * invocation of {@code updatePhysics} (in seconds, ≥0) 123 | */ 124 | @Override 125 | public void updatePhysics(float wallClockSeconds) { 126 | physicsSpace.update(wallClockSeconds); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/HelloStaticBody.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.apps; 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp; 32 | import com.jme3.bullet.PhysicsSpace; 33 | import com.jme3.bullet.collision.shapes.CollisionShape; 34 | import com.jme3.bullet.collision.shapes.SphereCollisionShape; 35 | import com.jme3.bullet.objects.PhysicsBody; 36 | import com.jme3.bullet.objects.PhysicsRigidBody; 37 | import com.jme3.math.Vector3f; 38 | 39 | /** 40 | * A simple example combining static and dynamic rigid bodies. 41 | *

42 | * Builds upon HelloRigidBody. 43 | * 44 | * @author Stephen Gold sgold@sonic.net 45 | */ 46 | public class HelloStaticBody extends BasePhysicsApp { 47 | // ************************************************************************* 48 | // constructors 49 | 50 | /** 51 | * Instantiate the HelloStaticBody application. 52 | *

53 | * This no-arg constructor was made explicit to avoid javadoc warnings from 54 | * JDK 18+. 55 | */ 56 | public HelloStaticBody() { 57 | // do nothing 58 | } 59 | // ************************************************************************* 60 | // new methods exposed 61 | 62 | /** 63 | * Main entry point for the HelloStaticBody application. 64 | * 65 | * @param arguments array of command-line arguments (not null) 66 | */ 67 | public static void main(String[] arguments) { 68 | HelloStaticBody application = new HelloStaticBody(); 69 | application.start(); 70 | } 71 | // ************************************************************************* 72 | // BasePhysicsApp methods 73 | 74 | /** 75 | * Create the PhysicsSpace. Invoked once during initialization. 76 | * 77 | * @return a new object 78 | */ 79 | @Override 80 | public PhysicsSpace createSpace() { 81 | PhysicsSpace result 82 | = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT); 83 | return result; 84 | } 85 | 86 | /** 87 | * Populate the PhysicsSpace. Invoked once during initialization. 88 | */ 89 | @Override 90 | public void populateSpace() { 91 | // Create a CollisionShape for balls. 92 | float ballRadius = 1f; 93 | CollisionShape ballShape = new SphereCollisionShape(ballRadius); 94 | 95 | // Create a dynamic body and add it to the space. 96 | float mass = 2f; 97 | PhysicsRigidBody dynaBall = new PhysicsRigidBody(ballShape, mass); 98 | physicsSpace.addCollisionObject(dynaBall); 99 | 100 | // Create a static body and add it to the space. 101 | PhysicsRigidBody statBall 102 | = new PhysicsRigidBody(ballShape, PhysicsBody.massForStatic); 103 | physicsSpace.addCollisionObject(statBall); 104 | 105 | // Position the balls in physics space. 106 | dynaBall.setPhysicsLocation(new Vector3f(0f, 4f, 0f)); 107 | statBall.setPhysicsLocation(new Vector3f(0.1f, 0f, 0f)); 108 | 109 | // Visualize the shapes of both rigid bodies: 110 | visualizeShape(dynaBall); 111 | visualizeShape(statBall); 112 | } 113 | 114 | /** 115 | * Advance the physics simulation by the specified amount. Invoked during 116 | * each update. 117 | * 118 | * @param wallClockSeconds the elapsed wall-clock time since the previous 119 | * invocation of {@code updatePhysics} (in seconds, ≥0) 120 | */ 121 | @Override 122 | public void updatePhysics(float wallClockSeconds) { 123 | physicsSpace.update(wallClockSeconds); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/console/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022, Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | /** 30 | * Examples of console applications using Libbulletjme. No graphics! 31 | */ 32 | package com.github.stephengold.lbjexamples.apps.console; 33 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/apps/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022, Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | /** 30 | * Graphical example applications from the Libbulletjme tutorials. 31 | */ 32 | package com.github.stephengold.lbjexamples.apps; 33 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/lbjexamples/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022-2024 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | /** 30 | * LbjExamples test chooser. 31 | */ 32 | package com.github.stephengold.lbjexamples; 33 | -------------------------------------------------------------------------------- /apps/src/main/java/com/github/stephengold/shapes/custom/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | /** 30 | * Classes that generate custom collision shapes for testing. 31 | */ 32 | package com.github.stephengold.shapes.custom; 33 | -------------------------------------------------------------------------------- /apps/src/main/resources/Textures/Terrain/splat/mountains512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/apps/src/main/resources/Textures/Terrain/splat/mountains512.png -------------------------------------------------------------------------------- /apps/src/main/resources/Textures/TextureTest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/apps/src/main/resources/Textures/TextureTest.png -------------------------------------------------------------------------------- /apps/src/main/resources/Textures/greenTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/apps/src/main/resources/Textures/greenTile.png -------------------------------------------------------------------------------- /bash/meld.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | GitDir=~/NetBeansProjects 6 | #GitDir="/c/Users/sgold/My Documents/NetBeansProjects" 7 | 8 | S1="$GitDir/LbjExamples/apps/src/main/java/com/github/stephengold/lbjexamples/apps" 9 | D1="$GitDir/Minie/TutorialApps/src/main/java/jme3utilities/tutorial" 10 | 11 | S2="$GitDir/LbjExamples/apps/src/main/java/com/github/stephengold/lbjexamples" 12 | D2="$GitDir/sport/apps/src/main/java/com/github/stephengold/sport/demo" 13 | 14 | S3="$GitDir/LbjExamples/docs/en/modules/ROOT" 15 | D3="$GitDir/Minie/MinieLibrary/src/site/antora/tutorials/modules/minie-library-tutorials" 16 | 17 | S4="$GitDir/LbjExamples/docs/en/modules/ROOT" 18 | D4="$GitDir/Minie/src/site/antora/minie-project/modules/ROOT" 19 | 20 | S5="$GitDir/LbjExamples/apps/src/main/java/com/github/stephengold/lbjexamples" 21 | D5="$GitDir/V-Sport/apps/src/main/java/com/github/stephengold/vsport/demo" 22 | 23 | S6="$GitDir/LbjExamples/apps/src/main/java/com/github/stephengold/lbjexamples/apps" 24 | D6="$GitDir/V-Sport/apps/src/main/java/com/github/stephengold/vsport/tutorial" 25 | 26 | S7="$GitDir/LbjExamples/apps/src/main/java" 27 | D7="$GitDir/Minie/MinieExamples/src/main/java" 28 | 29 | Meld="/usr/bin/meld" 30 | #Meld="/c/Program Files/Meld/meld" 31 | 32 | "$Meld" --diff "$S1" "$D1" --diff "$S2" "$D2" --diff "$S3" "$D3" --diff "$S4" "$D4" \ 33 | --diff "$S5" "$D5" --diff "$S6" "$D6" --diff "$S7" "$D7" 34 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Gradle script to build the LbjExamples project 2 | 3 | plugins { 4 | base // to add a "clean" task to the root project 5 | } 6 | 7 | configurations.all { 8 | resolutionStrategy.cacheChangingModulesFor(0, "seconds") // to disable caching of snapshots 9 | } 10 | 11 | tasks.register("checkstyle") { 12 | dependsOn(":apps:checkstyleMain") 13 | description = "Checks the style of all Java sourcecode." 14 | } 15 | 16 | // Register cleanup tasks: 17 | 18 | tasks.named("clean") { 19 | dependsOn("cleanDocsBuild", "cleanNodeModules") 20 | } 21 | tasks.register("cleanDocsBuild") { 22 | delete("docs/build") 23 | } 24 | tasks.register("cleanNodeModules") { 25 | delete("node_modules") 26 | } 27 | -------------------------------------------------------------------------------- /config/checkstyle/java-header: -------------------------------------------------------------------------------- 1 | /* 2 | [Copyright information goes here.] 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | -------------------------------------------------------------------------------- /docs/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Gradle script to build the "docs" subproject of LbjExamples 2 | 3 | plugins { 4 | java 5 | } 6 | 7 | sourceSets.main { 8 | java { 9 | srcDir("en") // for IDE access (no Java there) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /docs/en/antora.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # descriptor of the "lbj-en" Antora component 3 | 4 | name: lbj-en 5 | title: The Libbulletjme project 6 | version: English 7 | start_page: ROOT:overview.adoc 8 | nav: 9 | - modules/ROOT/nav.adoc 10 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/CustomConvex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/CustomConvex.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/box.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/box2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/box2d.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/capsule.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/compound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/compound.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/cone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/cone.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/convex2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/convex2d.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/conveyorDemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/conveyorDemo.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/cylinder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/cylinder.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/frustum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/frustum.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/gimpact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/gimpact.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/heightfield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/heightfield.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/hull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/hull.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/margin1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/margin1.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/margin4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/margin4.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/margin5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/margin5.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/margin6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/margin6.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/mesh.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/minkowski.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/minkowski.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/multiSphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/multiSphere.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/newtonsCradle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/newtonsCradle.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/pachinko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/pachinko.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/plane.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/segment.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/simplex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/simplex.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/sphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/sphere.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/splitDemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/splitDemo.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/testGearJoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/testGearJoint.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/thousandCubes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/thousandCubes.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/images/windlass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/docs/en/modules/ROOT/images/windlass.png -------------------------------------------------------------------------------- /docs/en/modules/ROOT/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:implementation.adoc[Implementation] 2 | * Library tutorials 3 | ** xref:add.adoc[Add to existing project] 4 | ** xref:sport.adoc[SPORT visualization] 5 | ** xref:rigidbody.adoc[Rigid bodies] 6 | ** xref:shape.adoc[Collision shapes] 7 | ** xref:character.adoc[Characters] 8 | ** xref:vehicle.adoc[Vehicles] 9 | ** xref:new6dof.adoc[New6Dof constraints] 10 | ** xref:collision.adoc[Managing collisions] 11 | ** xref:softbody.adoc[Soft bodies] 12 | ** xref:debug.adoc[Troubleshooting] 13 | ** xref:misc.adoc[Miscellany] 14 | * xref:build.adoc[Build procedures] 15 | * xref:demos.adoc[Demo apps] 16 | * xref:lexicon.adoc[Type lexicon] 17 | * https://github.com/stephengold/Libbulletjme[GitHub repository] 18 | ** https://github.com/stephengold/Libbulletjme/releases/latest[Latest release] 19 | ** https://github.com/stephengold/Libbulletjme/releases[Recent releases] 20 | ** https://github.com/stephengold/Libbulletjme/blob/master/README.md[ReadMe file] 21 | ** https://raw.githubusercontent.com/stephengold/Libbulletjme/master/LICENSE[License] 22 | ** https://github.com/stephengold/Libbulletjme/blob/master/release-notes.md[Release log] 23 | ** https://github.com/stephengold/Libbulletjme/commits/master[Recent commits] 24 | ** https://github.com/stephengold/Libbulletjme/issues[Issue tracker] 25 | ** https://github.com/stephengold/Libbulletjme/issues/new[Report an issue] 26 | * Library APIs (javadoc) 27 | ** https://stephengold.github.io/Libbulletjme/javadoc/master["master" branch] 28 | * Libbulletjme at other sites 29 | ** https://repo1.maven.org/maven2/com/github/stephengold/Libbulletjme[Maven Central download] 30 | ** https://central.sonatype.com/search?q=Libbulletjme&namespace=com.github.stephengold[Maven Central search] 31 | ** https://jitpack.io/#stephengold/Libbulletjme[JitPack (unofficial)] 32 | * Projects using Libbulletjme 33 | ** https://stephengold.github.io/Minie[Minie] 34 | ** https://dynamx.fr/[DynamX] 35 | ** https://github.com/stephengold/LbjExamples[LbjExamples] 36 | ** https://github.com/stephengold/macana[Macana] 37 | ** https://github.com/stephengold/sport[SPORT] 38 | ** https://github.com/stephengold/V-Sport[V-Sport] 39 | * Other related projects 40 | ** https://pybullet.org/wordpress[Bullet] 41 | ** https://jmonkeyengine.org[JMonkeyEngine] 42 | ** https://github.com/Simsilica/SimMath[SimMath] 43 | ** https://github.com/kmammou/v-hacd[V-HACD] 44 | ** https://github.com/stephengold/antora-ui-bundle[Website UI bundle] 45 | * People 46 | ** https://stephengold.github.io[Stephen Gold] 47 | ** Yanis Boudiaf 48 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/build.adoc: -------------------------------------------------------------------------------- 1 | = How to build Libbulletjme from source 2 | :Project: Libbulletjme 3 | 4 | {Project} is free, open-source software. 5 | 6 | While pre-built library artifacts (including AARs and JARs) 7 | can be downloaded from the Maven Central Repository, 8 | some people prefer to build them from source. 9 | 10 | You can also build a local copy of the documentation website (including HTML). 11 | This is useful if you are editing the documentation, 12 | or if your Internet access is limited or unreliable. 13 | 14 | == Libraries 15 | 16 | Here's the recommended procedure: 17 | 18 | . Install build software: 19 | .. a https://adoptium.net/releases.html[Java Development Kit (JDK)], 20 | version 8 or higher, and 21 | .. one of the supported C++ compilers: 22 | ... for Linux: https://gcc.gnu.org/[the GNU Compiler Collection] or https://www.llvm.org[Clang] 23 | ... for macOS: https://developer.apple.com/xcode[Xcode] 24 | ... for Windows: https://visualstudio.microsoft.com[Microsoft Visual Studio] 25 | .. If you use GCC, you might also need to install the "g++-multilib" package: 26 | ... `sudo apt install g++-multilib` 27 | . Point the `JAVA_HOME` environment variable to your JDK installation: 28 | (The path might look something like "C:\Program Files\Java\jre1.8.0_301" 29 | or "/usr/lib/jvm/java-8-openjdk-amd64/" or 30 | "/Library/Java/JavaVirtualMachines/liberica-jdk-17-full.jdk/Contents/Home" .) 31 | .. using Bash or Zsh: `export JAVA_HOME="` *path to installation* `"` 32 | .. using https://fishshell.com/[Fish]: `set -g JAVA_HOME "` *path to installation* `"` 33 | .. using Windows Command Prompt: `set JAVA_HOME="` *path to installation* `"` 34 | .. using PowerShell: `$env:JAVA_HOME = '` *path to installation* `'` 35 | . Download and extract the {Project} source code from GitHub: 36 | .. using https://git-scm.com[Git]: 37 | ... `git clone https://github.com/stephengold/Libbulletjme.git` 38 | ... `cd Libbulletjme` 39 | ... `git checkout -b latest 22.0.1` 40 | .. using a web browser: 41 | ... browse to https://github.com/stephengold/Libbulletjme/releases/latest 42 | ... follow the "Source code (zip)" link 43 | ... save the ZIP file 44 | ... extract the contents of the saved ZIP file 45 | ... `cd` to the extracted directory 46 | . Cross-compilation using GCC requires the g++-multilib package: 47 | .. `sudo apt-get install g++-multilib` 48 | . (optional) Edit the "gradle.properties" file to configure the build. 49 | . Run the https://gradle.org[Gradle] wrapper on the desktop build script: 50 | .. using Bash or Fish or PowerShell or Zsh: `./gradlew build` 51 | .. using Windows Command Prompt: `.\gradlew build` 52 | . To build Android artifacts, you'll need to 53 | install https://developer.android.com/studio[Android Studio] 54 | and point the `ANDROID_HOME` environment variable to that installation. 55 | . Run the Gradle wrapper on the Android build script: 56 | .. using Bash or Fish or PowerShell or Zsh: `./gradlew -b android.gradle build` 57 | .. using Windows Command Prompt: `.\gradlew -b android.gradle build` 58 | 59 | After a successful build, 60 | artifacts will be found in "build/libs" (desktop) and "build/outputs/aar" (Android). 61 | 62 | === Install artifacts 63 | 64 | You can install the built artifacts to your local Maven repository: 65 | 66 | * using Bash or Fish or PowerShell or Zsh: `./gradlew install;./gradlew -b android.gradle install` 67 | * using Windows Command Prompt: 68 | ** `.\gradlew install` 69 | ** `.\gradlew -b android.gradle install` 70 | 71 | === Cleanup 72 | 73 | After a build, you can restore the project to a pristine state: 74 | 75 | * using Bash or Fish or PowerShell or Zsh: `./gradlew clean;./gradlew -b android.gradle clean` 76 | * using Windows Command Prompt: 77 | ** `.\gradlew clean` 78 | ** `.\gradlew -b android.gradle clean` 79 | 80 | == Website content 81 | 82 | . Download and extract the source code from GitHub: 83 | .. `git clone https://github.com/stephengold/LbjExamples.git` 84 | .. `cd LbjExamples` 85 | . Edit "docs/playbook.yml" and replace "/home/sgold/NetBeansProjects/LbjExamples" 86 | with an absolute path to your checkout directory (2 places). 87 | . https://docs.antora.org/antora/latest/install-and-run-quickstart/#install-nodejs[Install Node.js] 88 | . Run Antora: 89 | .. `npx antora docs/playbook.yml` 90 | 91 | After a successful build, 92 | the local copy of the site will be found in the "docs/build/site" directory. 93 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/character.adoc: -------------------------------------------------------------------------------- 1 | = An introduction to character physics 2 | :experimental: 3 | :page-pagination: 4 | :pi: π 5 | :Sport: SPORT 6 | :url-api: https://stephengold.github.io/Libbulletjme/javadoc/master/com/jme3/bullet 7 | :url-kt: https://github.com/stephengold/LbjExamples/blob/master/kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps 8 | :url-tutorial: https://github.com/stephengold/LbjExamples/blob/master/apps/src/main/java/com/github/stephengold/lbjexamples/apps 9 | 10 | A _physics character_ is a collision object 11 | used to simulate game characters (people) walking, jumping, and falling. 12 | 13 | The standard physics character makes some simplifying assumptions: 14 | 15 | * The character's shape must be convex. 16 | * The character remains upright even when jumping or falling: 17 | no sitting, squatting, ducking, or lying down. 18 | * Limits are placed on: 19 | ** how steep a slope the character can climb, 20 | ** how high a step the character can climb, and 21 | ** how fast the character can fall. 22 | 23 | Like a dynamic rigid body, 24 | a physics character has a collision shape, a location, and 25 | a gravity vector, along with velocities and damping parameters. 26 | It also has an option for contact response, which is enabled by default. 27 | 28 | However, a physics character has no concept of mass, 29 | and its motion is controlled very differently from a dynamic body. 30 | Instead of applying forces or directly updating the character's velocities, 31 | the app should specify when and how the character should walk and jump. 32 | 33 | == Creation 34 | 35 | You can create a character by invoking the 36 | {url-api}/objects/PhysicsCharacter.html[`PhysicsCharacter`] constructor. 37 | This allows you to specify its collision shape and step height. 38 | 39 | {url-tutorial}/HelloCharacter.java[HelloCharacter] (also {url-kt}/HelloCharacter.kt[in Kotlin]) is a SPORT app 40 | that demonstrates the creation of a character, 41 | followed by automated jumps. 42 | Things to notice about the app: 43 | 44 | . The constructor requires a convex shape. 45 | In this app, a capsule is used. 46 | . The `onGround()` method tests whether the character is supported 47 | by a solid surface (as opposed to jumping or falling). 48 | . In {Sport}, characters that are jumping or falling are shown in pink, 49 | while characters that are on ground are shown in brown. 50 | 51 | == Walking 52 | 53 | A character's walking motion is controlled by its _walk direction_ vector. 54 | During each simulation step, the horizontal component of the walk direction 55 | gets added to the character's location. 56 | To stop the character from walking, invoke `setWalkDirection(Vector3f.ZERO)`. 57 | 58 | NOTE: Despite its name, the walk direction need not be a unit vector. 59 | 60 | {url-tutorial}/HelloWalk.java[HelloWalk] (also {url-kt}/HelloWalk.kt[in Kotlin]) demonstrates 61 | keyboard-controlled motion of a physics character. 62 | Things to notice while running the app: 63 | 64 | . Drag with the left mouse button to rotate the camera. 65 | . Press kbd:[Space bar] to jump. 66 | . Press kbd:[W] to walk in the camera's forward direction. 67 | . Some of the mountains have slopes too steep for the character to climb. 68 | 69 | 70 | == Configuration 71 | 72 | === Gravity and up direction 73 | 74 | The _up direction_ of a physics character is a unit vector 75 | that points in the direction opposite its gravity vector. 76 | By default, the up direction is (0,1,0) and 77 | the gravity vector is (0,-29.4,0). 78 | 79 | NOTE: A character's default gravity is 3x stronger 80 | than the default gravity of a {url-api}/PhysicsSpace.html[`PhysicsSpace`]. 81 | 82 | NOTE: Unlike the gravity of a rigid body, a character's gravity is never 83 | overridden by any physics space. 84 | 85 | To alter a character's gravity vector, 86 | use `character.setGravity(Vector3f)`. 87 | 88 | Altering the gravity vector automatically updates the character's up direction. 89 | To alter the *magnitude* of a character's gravity 90 | (without changing its direction) use `character.setGravity(float)`. 91 | 92 | To alter a character's up direction, use `character.setUp(Vector3f)`. 93 | Altering the up direction automatically updates the gravity vector. 94 | 95 | === Jump speed 96 | 97 | If a character jumps in its up direction, 98 | its predicted rise time and jump height 99 | are determined by its gravity and jump speed. 100 | Roughly speaking: 101 | 102 | [source,java] 103 | ---- 104 | float g = character.getGravity(null).length(); 105 | float v0 = character.getJumpSpeed(); 106 | float riseSeconds = v0 / g; 107 | float jumpHeight = v0 * v0 / (2f * g); 108 | ---- 109 | 110 | The default jump speed is 10 psu per second. 111 | To alter a character's jump speed, use `character.setJumpSpeed(float)`. 112 | 113 | === Fall speed 114 | 115 | _Fall speed_ limits the speed of a falling character. 116 | To be realistic, it should be larger than the character's jump speed. 117 | 118 | The default fall speed is 55 psu per second. 119 | To alter a character's fall speed, use `character.setFallSpeed(float)`. 120 | 121 | === Step height 122 | 123 | _Step height_ limits how high a step the character can climb. 124 | To be realistic, it should be less than the character's height. 125 | 126 | A character's initial step height is set by the constructor. 127 | To alter it, use `character.setStepHeight(float)`. 128 | 129 | === Maximum slope 130 | 131 | _Maximum slope_ limits how steep a slope the character can climb. 132 | It is expressed as an angle in radians relative to the horizontal plane. 133 | 134 | The default maximum slope is {pi}/4, indicating a 45-degree angle. 135 | To alter it, use `character.setMaxSlope(float)`. 136 | 137 | === Contact response 138 | 139 | As with a rigid body, you can disable the contact response of a character using 140 | `character.setContactResponse(false)`. 141 | 142 | Disabling a character's contact response 143 | will compel it to fall, at least until contact response is re-enabled. 144 | 145 | == Summary 146 | 147 | * A physics character simulates a game character walking, jumping, and falling. -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/debug.adoc: -------------------------------------------------------------------------------- 1 | = Troubleshooting physics issues 2 | :Project: Libbulletjme 3 | :page-pagination: 4 | 5 | {Project} uses `assert` statements to verify assumptions made while coding. 6 | During development, Java assertions should be enabled using: 7 | 8 | * the "-ea" JVM argument or 9 | * the `enableAssertions` setting of Gradle `JavaExec` tasks. 10 | 11 | Similarly, native-code assertions should be enabled during development, 12 | by loading a native library of the "debug" type, 13 | such as "com.github.stephengold:Libbulletjme-Linux64:22.0.1:SpDebug". 14 | 15 | == Visualization 16 | 17 | Many physics issues can be diagnosed visually. 18 | For instance, if you neglect to add a collision object to the physics space, 19 | SPORT will automatically omit it from the visualization. 20 | 21 | SPORT can be used to visualize: 22 | 23 | * collision shapes, 24 | * vehicle wheels, 25 | * physics joints, 26 | * soft bodies, 27 | * the centers and local axes of collision objects, and 28 | * the axis-aligned bounding boxes of collision objects. 29 | 30 | === Color conventions 31 | 32 | When visualizing a collision shape, 33 | SPORT uses colors to distinguish different types of collision objects: 34 | 35 | * yellow for an object without contact response, 36 | including any ghost object, 37 | * magenta for a rigid body (or collider) that's dynamic, responsive, and active, 38 | * gray for a rigid body or collider 39 | (with contact response) that's static, kinematic, or inactive, 40 | * brown for a character that's grounded, and 41 | * pink for a character (with contact response) that isn't grounded. 42 | 43 | When visualizing a physics joint, SPORT uses: 44 | 45 | * green for a "A" end and 46 | * red for a "B" end. 47 | 48 | When visualizing a soft body, SPORT uses: 49 | 50 | * red for the faces and 51 | * orange for the links. 52 | 53 | When visualizing coordinate axes, SPORT uses: 54 | 55 | * red for the +X axis, 56 | * green for the +Y axis, and 57 | * blue for the +Z axis. 58 | 59 | When visualizing bounding boxes, SPORT uses: 60 | 61 | * red for a ghost object whose overlapping count has increased, 62 | * green for a ghost whose count has decreased, 63 | * yellow for a ghost whose count is unchanged, and 64 | * white for a non-ghost collision object. 65 | 66 | == An introduction to `PhysicsDumper` 67 | 68 | The following temporary statements could be used to dump 69 | (to `System.out`) all collision objects in a physics space: 70 | 71 | [source,java] 72 | ---- 73 | PhysicsDumper dumper = new PhysicsDumper(); 74 | dumper.dump(physicsSpace); 75 | ---- 76 | 77 | Here is sample output for a space containing 2 rigid bodies and nothing else: 78 | 79 | .... 80 | PhysicsSpace with 0 chars, 0 ghosts, 0 joints, 2 rigids, 0 vehicles 81 | bphase=DBVT grav[y=-9.81] timeStep[0.0166667 maxSS=4] listeners[c=0 t=1] 82 | solver[SI iters=10 cfm=0 batch=128 splitImp[th=global erp=0.1] mode=WarmStart,VelocityDependent,SIMD,Cone] 83 | rayTest=SubSimplex,HeightfieldAccel 84 | Rigid Kin msLoc[x=-0.243501 y=-0.317344] 85 | contact[fric=0.5 rest=0 damp=0.1 pth=1e+18 stiff=1e+18] 86 | Sphere r=1 marg=0 87 | with 0 ignores and 0 joints 88 | Rigid Dyn(mass=2) msLoc[x=3.39744 y=-4.02647] loc[x=3.42799 y=-4.13896] orient[x=0 y=0 z=-0.24 w=0.971] 89 | contact[fric=0.5 rest=0 damp=0.1 pth=1e+18 stiff=1e+18] 90 | grav[y=-9.81] NOTprotected ccd[mth=0] damp[l=0 a=0] sleep[lth=0.8 ath=1 time=0] 91 | v[x=2.96385 y=-10.9131] force[xyz=0] lFact[xyz=1] 92 | inert[xyz=0.8] w[z=-0.714854] torq[xyz=0] aFact[xyz=1] 93 | Sphere r=1 marg=0 94 | with 0 ignores and 0 joints 95 | .... 96 | 97 | 2-space indentation indicates the hierarchy of spaces/objects/joints. 98 | Single-space indentation indicates additional description 99 | of the foregoing object. 100 | Related values are enclosed in square brackets. 101 | 102 | To dump a physics space to a text file: 103 | 104 | [source,java] 105 | ---- 106 | PrintStream dumpStream = new PrintStream("dump.txt"); 107 | PhysicsDumper dumper = new PhysicsDumper(dumpStream); 108 | dumper.dump(physicsSpace); 109 | ---- 110 | 111 | === What is dumped 112 | 113 | You can dump specific collision objects: 114 | 115 | [source,java] 116 | ---- 117 | dumper.dump(character); 118 | dumper.dump(multiBodyCollider); 119 | dumper.dump(ghostObject); 120 | dumper.dump(rigidBody); 121 | dumper.dump(softBody); 122 | ---- 123 | 124 | You can dump specific collision shapes: 125 | 126 | [source,java] 127 | ---- 128 | dumper.dump(collisionShape, ""); 129 | ---- 130 | 131 | When dumping a space, 132 | the default is to describe every collision object; 133 | physics joints are counted but not described in detail. 134 | To describe the joints in each body, configure the dumper like so: 135 | 136 | [source,java] 137 | ---- 138 | dumper.setEnabled(DumpFlags.JointsInBodies, true); // default=false 139 | ---- 140 | 141 | To describe the motors in each joint, configure the dumper like so: 142 | 143 | [source,java] 144 | ---- 145 | dumper.setEnabled(DumpFlags.Motors, true); // default=false 146 | ---- 147 | 148 | To dump just the physics joints (no collision objects): 149 | 150 | [source,java] 151 | ---- 152 | dumper.setEnabled(DumpFlags.Pcos, false); // default=true 153 | dumper.setEnabled(DumpFlags.JointsInSpaces, true); // default=false 154 | ---- 155 | 156 | Other dump flags can be set to describe the nodes or clusters in each soft body 157 | or the child shapes in each compound collision shape. 158 | 159 | == Summary 160 | 161 | * During development, enable Java assertions and use debug-enabled libraries. 162 | * SPORT visualization can be used to diagnose issues. 163 | * To obtain detailed information about scenes and physics, use a dumper. 164 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/demos.adoc: -------------------------------------------------------------------------------- 1 | = An overview of the demo applications 2 | :Project: Libbulletjme 3 | :experimental: 4 | :url-examples: https://github.com/stephengold/sport/tree/master/apps/src/main/java/com/github/stephengold/sport/demo 5 | 6 | _Demo applications_ are provided to showcase certain features of {Project}. 7 | The following demos are found in 8 | {url-examples}[the com.github.stephengold.sport.demo package of the SPORT "apps" sub-project]: 9 | 10 | * {url-examples}/ConveyorDemo.java[`ConveyorDemo`] 11 | demonstrates contact-point modifications + 12 | image:conveyorDemo.png[height=80] 13 | * {url-examples}/NewtonsCradle.java[`NewtonsCradle`] 14 | demonstrates dynamic restitution and point-to-point joints + 15 | image:newtonsCradle.png[height=50] 16 | * {url-examples}/Pachinko.java[`Pachinko`] 17 | demonstrates compound collision shapes and 2-D physics + 18 | image:pachinko.png[height=100] 19 | * {url-examples}/SplitDemo.java[`SplitDemo`] 20 | demonstrates dynamic splitting of rigid bodies + 21 | image:splitDemo.png[height=80] 22 | * {url-examples}/TestGearJoint.java[`TestGearJoint`] 23 | demonstrates a gear joint + 24 | image:testGearJoint.png[height=80] 25 | * {url-examples}/ThousandCubes.java[`ThousandCubes`] 26 | demonstrates stacking cubes and launching projectiles + 27 | image:thousandCubes.png[height=100] 28 | * {url-examples}/Windlass.java[`Windlass`] 29 | demonstrates rope simulation using rigid bodies + 30 | image:windlass.png[height=100] 31 | 32 | == User interface 33 | 34 | The demos are controlled primarily by keyboard input. 35 | 36 | NOTE: The descriptions below assume a keyboard with the "US" (QWERTY) layout. 37 | On keyboards with other layouts, the keys may be labeled differently. 38 | 39 | For convenience, the mapping of keys to actions is partly standardized. 40 | In most demos: 41 | 42 | * kbd:[.] and kbd:[Pause] toggle the simulation between running and paused, 43 | * kbd:[C] dumps the camera's position to the console, and 44 | * kbd:[Esc] ends the application. 45 | 46 | For camera control, 47 | all demos except `ThousandCubes` use `DragLMB` rotation mode. 48 | This means you can rotate the camera 49 | by dragging the mouse with the left button depressed. 50 | Furthermore: 51 | 52 | * kbd:[W] and kbd:[S] dolly the camera forward and back, respectively, 53 | * kbd:[A] and kbd:[D] dolly the camera left and right, respectively, 54 | * kbd:[Q] raises the camera, and 55 | * kbd:[Z] lowers the camera. 56 | 57 | Additional mappings are specific to each application: 58 | 59 | * In `ConveyorDemo`: 60 | 61 | ** kbd:[Enter], kbd:[I], kbd:[Ins], kbd:[Numpad0], and kbd:[Spacebar] 62 | drop a box onto the conveyor belt 63 | 64 | * In `NewtonsCradle`: 65 | 66 | ** kbd:[F1], kbd:[1], and kbd:[Numpad1] restart with a single ball 67 | ** kbd:[F2], kbd:[2], and kbd:[Numpad2] restart with 2 balls 68 | ** kbd:[F3], kbd:[3], and kbd:[Numpad3] restart with 3 balls 69 | ** kbd:[F4], kbd:[4], and kbd:[Numpad4] restart with 4 balls 70 | ** kbd:[F5], kbd:[5], and kbd:[Numpad5] restart with 5 balls 71 | 72 | * In `Pachinko`: 73 | 74 | ** kbd:[F4], kbd:[4], and kbd:[Numpad4] restart with 4 rows of pins 75 | ** kbd:[F5], kbd:[5], and kbd:[Numpad5] restart with 5 rows of pins 76 | ** kbd:[F6], kbd:[6], and kbd:[Numpad6] restart with 6 rows of pins 77 | ** kbd:[F7], kbd:[7], and kbd:[Numpad7] restart with 7 rows of pins 78 | ** kbd:[F8], kbd:[8], and kbd:[Numpad8] restart with 8 rows of pins 79 | ** kbd:[F9], kbd:[9], and kbd:[Numpad9] restart with 9 rows of pins 80 | 81 | * In `SplitDemo`: 82 | 83 | ** kbd:[Tab] convert all bodies to splittable shapes 84 | ** kbd:[Home] and kbd:[Numpad5] restart with a random shape 85 | ** kbd:[Enter], kbd:[Numpad0], and kbd:[Spacebar] split all shapes 86 | ** kbd:[L bracket] and kbd:[R bracket] rotate the splitting plane 87 | 88 | * In `TestGearJoint`: 89 | 90 | ** kbd:[R] and kbd:[Down arrow] apply +Y torque to the drive shaft 91 | ** kbd:[F] and kbd:[Up arrow] apply -Y torque to the drive shaft 92 | 93 | * In `ThousandCubes`: 94 | 95 | ** kbd:[E] launch a red ball 96 | 97 | * In `Windlass`: 98 | 99 | ** kbd:[Down arrow] turn the barrel counter-clockwise 100 | ** kbd:[Up arrow] turn the barrel clockwise 101 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/misc.adoc: -------------------------------------------------------------------------------- 1 | = Miscellaneous tutorial material 2 | :page-pagination: 3 | :url-api: https://stephengold.github.io/Libbulletjme/javadoc/master/com/jme3/bullet 4 | :url-examples: https://github.com/stephengold/sport/blob/master/apps/src/main/java/com/github/stephengold/sport/demo 5 | 6 | This page acts as a holding area for tutorial material that's waiting 7 | to be organized. 8 | 9 | == Startup message 10 | 11 | By default, the native library prints a startup message to `System.out`. 12 | Once the library is loaded (but not started) you can disable this message: 13 | 14 | [source,java] 15 | ---- 16 | NativeLibrary.setStartupMessageEnabled(false); 17 | ---- 18 | 19 | == Library versions and properties 20 | 21 | Once the native library is loaded, 22 | you can test whether it uses double-precision arithmetic: 23 | 24 | [source,java] 25 | ---- 26 | boolean doublePrecision = NativeLibrary.isDoublePrecision(); 27 | ---- 28 | 29 | You can also test whether it was built for debugging 30 | (with assertions enabled, symbols not stripped, 31 | and debug information generated): 32 | 33 | [source,java] 34 | ---- 35 | boolean debug = NativeLibrary.isDebug(); 36 | ---- 37 | 38 | You can also read the native library's version string, 39 | which consists of 3 unsigned decimal numbers separated by dots: 40 | 41 | [source,java] 42 | ---- 43 | String nativeVersion = NativeLibrary.versionNumber(); 44 | ---- 45 | 46 | 47 | == Default collision margin 48 | 49 | The default collision margin for new shapes is 0.04 physics-space units. 50 | To configure a default margin of 0.1 psu: 51 | 52 | [source,java] 53 | ---- 54 | CollisionShape.setDefaultMargin(0.1f); 55 | ---- 56 | 57 | NOTE: The Bullet Manual advises against changing the default margin. 58 | 59 | == Broadphase types 60 | 61 | By default, a Dynamic Bounding-Volume Tree (DBVT) is used for broadphase 62 | collision detection. 63 | To specify a different data structure, specify the corresponding enum value 64 | in the `PhysicsSpace` constructor: 65 | 66 | [source,java] 67 | ---- 68 | PhysicsSoftSpace physicsSpace = new PhysicsSoftSpace(worldMin, worldMax, 69 | PhysicsSpace.BroadphaseType.AXIS_SWEEP_3); 70 | ---- 71 | 72 | NOTE: The world max/min bounds are used 73 | only by the `AXIS_SWEEP_3` and `AXIS_SWEEP_3_32` broadphase algorithms. 74 | The `SIMPLE` and `DBVT` algorithms ignore those parameters. 75 | 76 | 77 | == Contact-and-constraint solver 78 | 79 | === Algorithms 80 | 81 | By default, a 82 | http://allenchou.net/2013/12/game-physics-constraints-sequential-impulse[Sequential Impulse (SI) solver] 83 | is used to resolve contacts and constraints. 84 | To specify a different type of solver, specify the corresponding enum value 85 | in the `PhysicsSpace` constructor: 86 | 87 | [source,java] 88 | ---- 89 | PhysicsSoftSpace physicsSpace = new PhysicsSpace(worldMin, worldMax, 90 | broadphaseType, SolverType.Dantzig); 91 | ---- 92 | 93 | NOTE: For soft-body simulations, SI is the only supported solver type. 94 | 95 | NOTE: The NNCG solver doesn't support multibodies. 96 | 97 | === Tuning parameters 98 | 99 | The contact-and-constraint solver 100 | performs a limited number of iterations per simulation step, 101 | by default, 10. 102 | For higher-quality (but slower) simulation, increase this number. 103 | For instance, to use 20 iterations: 104 | 105 | [source,java] 106 | ---- 107 | space.getSolverInfo().setNumIterations(20); 108 | ---- 109 | 110 | Other solver parameters can be tuned, including: 111 | 112 | * the global error reduction parameter (ERP) for physics joints, 113 | described on xref:new6dof.adoc#_caveats[the New6Dof page] 114 | * the contact ERP 115 | * the constraint-force mixing parameter (CFM) 116 | * the batch size 117 | * the mode flags, 118 | which enable warm start, constraint ordering, and other features 119 | * the flag to enable the split-impulse feature 120 | 121 | 122 | == Advanced rigid-body friction 123 | 124 | In addition to the basic friction parameter (which affects sliding friction) 125 | each rigid body has 2 additional friction parameters: 126 | one for rolling friction and one for spinning friction. 127 | Both parameters default to zero. 128 | 129 | _Rolling friction_ generates torque orthogonal to the contact normal, 130 | which tends to slow down a rolling body. 131 | _Spinning friction_ generates torque parallel to the contact normal, 132 | which tends to prevent a body from spinning when grasped. 133 | 134 | To simulate objects with grooved surfaces, it's also possible to configure 135 | a rigid body for _anisotropic friction_: 136 | friction that depends on the direction of relative motion. 137 | 138 | 139 | == Gear joint 140 | 141 | {url-api}/joints/GearJoint.html[`GearJoint`] implements 142 | a special type of constraint used to simulate rotating shafts 143 | linked by belts, cables, chains, or gears. 144 | Unlike other constraints, it has no pivot points, only axes. 145 | It's a double-ended constraint 146 | with a single rotational degree-of-freedom. 147 | The rotational rate of the A body around its axis 148 | is matched to that of the B body around its axis, or made proportional. 149 | 150 | To see a gear joint in action, run 151 | {url-examples}/TestGearJoint.java[the TestGearJoint application]. 152 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/overview.adoc: -------------------------------------------------------------------------------- 1 | = Libbulletjme project overview 2 | :Cplusplus: C++ 3 | :Project: Libbulletjme 4 | :url-enwiki: https://en.wikipedia.org/wiki 5 | 6 | _{Project}_ is a {url-enwiki}/Physics_engine[physics-simulation] library 7 | for {url-enwiki}/Java_virtual_machine[Java]. 8 | 9 | Use it to: 10 | 11 | * add solidity to walls, characters, projectiles, landscapes, and vehicles, 12 | * detect collisions between complex shapes, 13 | * perform contact, ray, and sweep tests, 14 | * simulate characters and buildings collapsing in real time, and 15 | * simulate cloth, rope, and deformable volumes, 16 | including their interactions with rigid bodies and each other. 17 | 18 | Tutorials and example apps are provided to help you get started. 19 | 20 | All software in the {Project} project is free and open-source, 21 | including the JVM library, the native libraries, the demo and tutorial apps, 22 | and the software used to build, test, and run them. 23 | 24 | The underlying simulation software comes from 25 | https://pybullet.org/wordpress[_Bullet_], a mature real-time physics simulator. 26 | But although Bullet is written in {url-enwiki}/C%2B%2B[C++], 27 | you won't need any knowledge of {Cplusplus} to use {Project}. 28 | 29 | {Project} also incorporates source code from 30 | https://github.com/kmammou/v-hacd[the V-HACD project] and 31 | https://github.com/Simsilica/SimMath[the SimMath library]. 32 | 33 | 34 | == Caveats 35 | 36 | {Project}'s focus is on simulating 37 | {url-enwiki}/Classical_mechanics[classical mechanics] 38 | in just enough detail to add verisimilitude to 3-D games. 39 | It's not intended for 2-D {url-enwiki}/Platform_game[platformers], 40 | nor for scientific research. 41 | For 2-D physics, consider using http://www.dyn4j.org/[dyn4j] instead. 42 | 43 | {Project} and its documentation 44 | assume a certain level of proficiency with 45 | {url-enwiki}/Java_(programming_language)[the Java programming language]. 46 | The project isn't aimed at non-programmers, 47 | nor developers without prior Java experience. 48 | Similarly, it assumes familiarity with vector math and 3-D computer graphics. 49 | 50 | While an understanding of classical mechanics is assumed, 51 | many of its technical terms are linked (at first use) 52 | to the relevant article in the English Wikipedia. 53 | 54 | https://raw.githubusercontent.com/stephengold/Libbulletjme/master/LICENSE[The project's mixed open-source license] 55 | disclaims liability for defects. 56 | Please don't use this software in safety-critical applications. 57 | 58 | Starting with version 16.1.0, 59 | {Project} is no longer compatible with old versions of the "libc" library 60 | found on systems such as CentOS 7 and Ubuntu 16.04 (Xenial). 61 | 62 | 63 | == What's missing 64 | 65 | Bullet is a large project, and 66 | Libbulletjme doesn't yet provide access to all its features. 67 | In particular: 68 | 69 | * `btRigidBodyConstructionInfo` 70 | * "additional damping" for rigid bodies 71 | * Bullet's debug drawer (but see the https://github.com/stephengold/sport[SPORT] 72 | and https://github.com/stephengold/V-Sport[V-Sport] graphics engines) 73 | * serialization (file loader) for classes other than `btOptimizedBvh` 74 | * certain constraints: 75 | ** `btFixedConstraint` 76 | ** `btUniversalConstraint` 77 | * certain collision shapes: 78 | ** `btCompoundFromGimpactShape` 79 | ** `btConvexPointCloudShape` 80 | ** `btConvexTriangleMeshShape` 81 | ** `btGImpactCompoundShape` 82 | ** `btMultimaterialTriangleMeshShape` 83 | ** `btScaledBvhTriangleMeshShape` 84 | ** `btSdfCollisionShape` 85 | ** `btTriangleShape` 86 | ** `btUniformScalingShape` 87 | * certain world types: 88 | ** `btSimpleDynamicsWorld` 89 | ** `btSoftMultiBodyDynamicsWorld` 90 | * inverse dynamics 91 | * Bullet v3 92 | * Bullet extras, examples, and tests (but see the Java examples in LbjExamples) 93 | 94 | Furthermore, Libbulletjme hasn't yet been ported to the following platforms: 95 | 96 | * the FreeBSD and iOS operating systems 97 | * Windows-on-ARM 98 | * MIPS, POWER, RISC-V, and System Z architectures 99 | 100 | We welcome suggestions concerning the future development of {Project}. 101 | 102 | 103 | == Getting help 104 | 105 | For self-help, utilize 106 | xref:add.adoc[the tutorials], 107 | https://stephengold.github.io/Libbulletjme/javadoc/master[the javadoc], and 108 | https://github.com/stephengold/Libbulletjme[the source code]. 109 | 110 | For issues that are best handled privately, contact Stephen by e-mail. 111 | His personal e-mail address appears 112 | on https://stephengold.github.io/[his homepage] and in the source code. 113 | 114 | 115 | == The name 116 | 117 | The project's (somewhat awkward) name 118 | reflects that fact that it was originally created 119 | for the JMonkeyEngine (JME) game engine. 120 | 121 | Despite its history, Libbulletjme is self-contained; 122 | it doesn't depend on JME. 123 | If you're seeking a physics engine that's integrated with JME, 124 | please consider https://stephengold.github.io/Minie[Minie]. 125 | 126 | 127 | == Next steps 128 | 129 | If you're curious how {Project} works, 130 | proceed to xref:implementation.adoc[the Implementation page]. 131 | 132 | To gain hands-on experience, 133 | proceed to xref:add.adoc[the first tutorial page]. 134 | -------------------------------------------------------------------------------- /docs/en/modules/ROOT/pages/sport.adoc: -------------------------------------------------------------------------------- 1 | = Visualization using the SPORT graphics engine 2 | :experimental: 3 | :page-pagination: 4 | :url-kt: https://github.com/stephengold/LbjExamples/blob/master/kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps 5 | :url-tutorial: https://github.com/stephengold/LbjExamples/blob/master/apps/src/main/java/com/github/stephengold/lbjexamples/apps 6 | 7 | The `HelloLibbulletjme` app 8 | (introduced on xref:add.adoc[the previous page]) 9 | is simple, readable and complete. 10 | However, as a console app, its output is limited to text. 11 | 12 | Text provides scant insight into what the physics simulation is doing. 13 | For most people, pictures are much easier to understand. 14 | 15 | For the purpose of this tutorial series, 16 | we've created a graphics engine named SPORT 17 | (the Simple Physics-ORienTed engine). 18 | SPORT enables us to visualize physics objects 19 | without adding much code to the tutorial apps. 20 | It is open-source, written in Java, and available from 21 | https://github.com/stephengold/sport[GitHub] and 22 | https://central.sonatype.com/artifact/com.github.stephengold/sport[Maven Central]. 23 | 24 | == HelloSport 25 | 26 | {url-tutorial}/HelloSport.java[HelloSport] (also {url-kt}/HelloSport.kt[in Kotlin]) 27 | is a direct conversion of `HelloLibbulletjme` into a SPORT app. 28 | 29 | Details to note: 30 | 31 | . The app is declared as a subclass of `BasePhysicsApp`, 32 | indicating that it will simulate a plain `PhysicsSpace`. 33 | . The app implements the 3 abstract methods of `BasePhysicsApp` 34 | (`createSpace`, `populateSpace`, and `updateSpace`), 35 | which are all invoked automatically. 36 | . `BasePhysicsApp` automatically loads the Libbulletjme native library. 37 | . `BasePhysicsApp` provides: 38 | .. the `physicsSpace` field to access the space and 39 | .. the `visualizeShape()` method to visualize the shape of a collision object. 40 | . Whereas `HelloLibbulletjme` used `update(intervalSeconds, 0)` 41 | to simulate one step at a time, 42 | `HelloSport` attempts real-time simulation using `update(intervalSeconds)`. 43 | 44 | Running `HelloSport` should open a window on your computer's desktop 45 | and play a brief animation of a sphere falling onto a horizontal surface. 46 | 47 | Pressing kbd:[Esc] should close the window and terminate the app. 48 | 49 | Hereafter, all our tutorial apps will use SPORT. 50 | Additional features of SPORT will be introduced as needed. 51 | 52 | == Summary 53 | 54 | * SPORT is a graphics engine, created specifically for Libbulletjme tutorials. 55 | * SPORT provides a simple toolkit for visualizing 3-D physics. -------------------------------------------------------------------------------- /docs/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # configure Antora for a local test using the published UI bundle 3 | 4 | site: 5 | title: The Libbulletjme project (local test using the published bundle) 6 | start_page: lbj-en:ROOT:overview.adoc 7 | url: /home/sgold/NetBeansProjects/LbjExamples/docs/build/site 8 | content: 9 | sources: 10 | - url: /home/sgold/NetBeansProjects/LbjExamples 11 | branches: master 12 | start_path: docs/en 13 | ui: 14 | bundle: 15 | url: https://github.com/stephengold/antora-ui-bundle/releases/download/v-sgold250101a/ui-bundle.zip 16 | snapshot: true 17 | output: 18 | clean: true 19 | dir: docs/build/site 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## configure Gradle properties for building the LbjExamples project 2 | 3 | #bft = SpDebug 4 | bft = SpRelease 5 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | ## catalog of libraries and plugins used to build the LbjExamples project 2 | 3 | [versions] 4 | 5 | checkstyle = "9.3" 6 | libbulletjme = "22.0.1" 7 | lwjgl = "3.3.6" 8 | 9 | [libraries] 10 | 11 | jsnaploader = "io.github.electrostat-lab:snaploader:1.1.1-stable" 12 | 13 | libbulletjme-linux64 = { module = "com.github.stephengold:Libbulletjme-Linux64", version.ref = "libbulletjme" } 14 | libbulletjme-linuxarm32hf = { module = "com.github.stephengold:Libbulletjme-Linux_ARM32hf", version.ref = "libbulletjme" } 15 | libbulletjme-linuxarm64 = { module = "com.github.stephengold:Libbulletjme-Linux_ARM64", version.ref = "libbulletjme" } 16 | libbulletjme-macosx64 = { module = "com.github.stephengold:Libbulletjme-MacOSX64", version.ref = "libbulletjme" } 17 | libbulletjme-macosxarm64 = { module = "com.github.stephengold:Libbulletjme-MacOSX_ARM64", version.ref = "libbulletjme" } 18 | libbulletjme-windows64 = { module = "com.github.stephengold:Libbulletjme-Windows64", version.ref = "libbulletjme" } 19 | 20 | lwjgl = { module = "org.lwjgl:lwjgl", version.ref = "lwjgl" } 21 | lwjgl-assimp = { module = "org.lwjgl:lwjgl-assimp", version.ref = "lwjgl" } 22 | lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw", version.ref = "lwjgl" } 23 | lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl" } 24 | 25 | sport = "com.github.stephengold:sport:0.9.6" 26 | 27 | [bundles] 28 | 29 | [plugins] 30 | 31 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.2.0" } 32 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloCcd.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.collision.shapes.CylinderCollisionShape 34 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 35 | import com.jme3.bullet.objects.PhysicsBody 36 | import com.jme3.bullet.objects.PhysicsRigidBody 37 | import com.jme3.math.Vector3f 38 | 39 | /* 40 | * A simple example of continuous collision detection (CCD). 41 | * 42 | * Builds upon HelloStaticBody. 43 | * 44 | * author: Stephen Gold sgold@sonic.net 45 | */ 46 | 47 | /* 48 | * Main entry point for the HelloCcd application. 49 | */ 50 | fun main() { 51 | val application = HelloCcd() 52 | application.start() 53 | } 54 | 55 | class HelloCcd : BasePhysicsApp() { 56 | /* 57 | * Create the PhysicsSpace. Invoked once during initialization. 58 | */ 59 | override fun createSpace(): PhysicsSpace { 60 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 61 | 62 | // Increase gravity to make the balls fall faster. 63 | result.setGravity(Vector3f(0f, -100f, 0f)) 64 | 65 | return result 66 | } 67 | 68 | /* 69 | * Populate the PhysicsSpace. Invoked once during initialization. 70 | */ 71 | override fun populateSpace() { 72 | // Create a CollisionShape for balls. 73 | val ballRadius = 0.1f 74 | val ballShape = SphereCollisionShape(ballRadius) 75 | 76 | // Create 2 dynamic balls, one with CCD and one without, 77 | // and add them to the space. 78 | val mass = 1f 79 | val ccdBall = PhysicsRigidBody(ballShape, mass) 80 | physicsSpace.addCollisionObject(ccdBall) 81 | ccdBall.setCcdMotionThreshold(ballRadius) 82 | ccdBall.setCcdSweptSphereRadius(ballRadius) 83 | ccdBall.setPhysicsLocation(Vector3f(-1f, 4f, 0f)) 84 | 85 | val controlBall = PhysicsRigidBody(ballShape, mass) 86 | physicsSpace.addCollisionObject(controlBall) 87 | controlBall.setPhysicsLocation(Vector3f(1f, 4f, 0f)) 88 | 89 | // Create a thin, static disc and add it to the space. 90 | val discRadius = 2f 91 | val discThickness = 0.05f 92 | val discShape = CylinderCollisionShape( 93 | discRadius, discThickness, PhysicsSpace.AXIS_Y) 94 | val disc = PhysicsRigidBody(discShape, PhysicsBody.massForStatic) 95 | physicsSpace.addCollisionObject(disc) 96 | 97 | // Visualize the shapes of all 3 rigid bodies: 98 | visualizeShape(ccdBall) 99 | visualizeShape(controlBall) 100 | visualizeShape(disc).setProgram("Unshaded/Monochrome") 101 | } 102 | 103 | /* 104 | * Advance the physics simulation by the specified amount. Invoked during 105 | * each update. 106 | * 107 | * wallClockSeconds: the elapsed wall-clock time since the previous 108 | * invocation of updatePhysics() (in seconds, >=0) 109 | */ 110 | override fun updatePhysics(wallClockSeconds: Float) { 111 | // For clarity, simulate at 1/10th normal speed. 112 | val simulateSeconds = 0.1f * wallClockSeconds 113 | physicsSpace.update(simulateSeconds) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloCloth.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.Mesh 32 | import com.github.stephengold.sport.mesh.ClothGrid 33 | import com.github.stephengold.sport.physics.BasePhysicsApp 34 | import com.github.stephengold.sport.physics.LinksGeometry 35 | import com.jme3.bullet.PhysicsSoftSpace 36 | import com.jme3.bullet.PhysicsSpace 37 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 38 | import com.jme3.bullet.objects.PhysicsBody 39 | import com.jme3.bullet.objects.PhysicsRigidBody 40 | import com.jme3.bullet.objects.PhysicsSoftBody 41 | import com.jme3.bullet.objects.infos.SoftBodyConfig 42 | import com.jme3.bullet.objects.infos.SoftBodyMaterial 43 | import com.jme3.bullet.util.NativeSoftBodyUtil 44 | import com.jme3.math.Vector3f 45 | 46 | /* 47 | * A simple cloth simulation using a soft body. 48 | * 49 | * Builds upon HelloSoftBody. 50 | * 51 | * author: Stephen Gold sgold@sonic.net 52 | */ 53 | 54 | /* 55 | * Main entry point for the HelloCloth application. 56 | */ 57 | fun main() { 58 | val application = HelloCloth() 59 | application.start() 60 | } 61 | 62 | class HelloCloth : BasePhysicsApp() { 63 | /* 64 | * Create the PhysicsSpace. Invoked once during initialization. 65 | */ 66 | override fun createSpace(): PhysicsSoftSpace { 67 | val worldMin = Vector3f(-999f, -999f, -999f) 68 | val worldMax = Vector3f(+999f, +999f, +999f) 69 | val result = PhysicsSoftSpace( 70 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT) 71 | 72 | return result 73 | } 74 | 75 | /* 76 | * Initialize the application. Invoked once. 77 | */ 78 | override fun initialize() { 79 | super.initialize() 80 | 81 | // Relocate the camera. 82 | cam.setLocation(Vector3f(0f, 1f, 8f)) 83 | } 84 | 85 | /* 86 | * Populate the PhysicsSpace. Invoked once during initialization. 87 | */ 88 | override fun populateSpace() { 89 | // Create a static, rigid sphere and add it to the physics space. 90 | val radius = 1f 91 | val shape = SphereCollisionShape(radius) 92 | val sphere = PhysicsRigidBody(shape, PhysicsBody.massForStatic) 93 | physicsSpace.addCollisionObject(sphere) 94 | visualizeShape(sphere) 95 | 96 | // Generate a subdivided square mesh with alternating diagonals. 97 | val numLines = 41 98 | val lineSpacing = 0.1f // mesh units 99 | val squareGrid = ClothGrid(numLines, numLines, lineSpacing) 100 | 101 | // Create a soft square and add it to the physics space. 102 | val cloth = PhysicsSoftBody() 103 | NativeSoftBodyUtil.appendFromTriMesh(squareGrid, cloth) 104 | physicsSpace.addCollisionObject(cloth) 105 | /* 106 | * Make the cloth flexible by reducing the angular stiffness 107 | * of its material. 108 | */ 109 | val mat = cloth.getSoftMaterial() 110 | mat.setAngularStiffness(0f) // default=1 111 | /* 112 | * Improve simulation accuracy by increasing 113 | * the number of position-solver iterations for the cloth. 114 | */ 115 | val config = cloth.getSoftConfig() 116 | config.setPositionIterations(9) // default=1 117 | 118 | // Translate the cloth upward to its starting location. 119 | cloth.applyTranslation(Vector3f(0f, 2f, 0f)) 120 | 121 | // Visualize the soft-body links: 122 | LinksGeometry(cloth) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloContactResponse.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.input.InputProcessor 32 | import com.github.stephengold.sport.physics.BasePhysicsApp 33 | import com.jme3.bullet.PhysicsSpace 34 | import com.jme3.bullet.collision.shapes.BoxCollisionShape 35 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 36 | import com.jme3.bullet.objects.PhysicsBody 37 | import com.jme3.bullet.objects.PhysicsRigidBody 38 | import com.jme3.math.Vector3f 39 | import org.lwjgl.glfw.GLFW 40 | 41 | /* 42 | * A simple demonstration of contact response. 43 | * 44 | * Press the E key to disable the ball's contact response. Once this happens, 45 | * the gray (static) box no longer exerts any contact force on the ball. Gravity 46 | * takes over, and the ball falls through. 47 | * 48 | * Builds upon HelloStaticBody. 49 | * 50 | * author: Stephen Gold sgold@sonic.net 51 | */ 52 | 53 | /* 54 | * Main entry point for the HelloContactResponse application. 55 | */ 56 | fun main() { 57 | val application = HelloContactResponse() 58 | application.start() 59 | } 60 | 61 | /* 62 | * collision object for the dynamic ball 63 | */ 64 | private var ball : PhysicsRigidBody? = null 65 | 66 | class HelloContactResponse : BasePhysicsApp() { 67 | // ************************************************************************* 68 | // BasePhysicsApp override functions 69 | 70 | /* 71 | * Create the PhysicsSpace. Invoked once during initialization. 72 | */ 73 | override fun createSpace(): PhysicsSpace { 74 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 75 | return result 76 | } 77 | 78 | /* 79 | * Initialize the application. Invoked once. 80 | */ 81 | override fun initialize() { 82 | super.initialize() 83 | configureInput() 84 | } 85 | 86 | /* 87 | * Populate the PhysicsSpace. Invoked once during initialization. 88 | */ 89 | override fun populateSpace() { 90 | // Add a static box to the space, to serve as a horizontal platform. 91 | val boxHalfExtent = 3f 92 | val boxShape = BoxCollisionShape(boxHalfExtent) 93 | val box = PhysicsRigidBody(boxShape, PhysicsBody.massForStatic) 94 | physicsSpace.addCollisionObject(box) 95 | box.setPhysicsLocation(Vector3f(0f, -4f, 0f)) 96 | 97 | // Add a dynamic ball to the space. 98 | val ballRadius = 1f 99 | val ballShape = SphereCollisionShape(ballRadius) 100 | val ballMass = 2f 101 | ball = PhysicsRigidBody(ballShape, ballMass) 102 | physicsSpace.addCollisionObject(ball) 103 | assert(ball!!.isContactResponse()) 104 | 105 | // Position the ball directly above the box. 106 | ball!!.setPhysicsLocation(Vector3f(0f, 4f, 0f)) 107 | 108 | // Visualize the shapes of both rigid bodies: 109 | visualizeShape(ball) 110 | visualizeShape(box) 111 | } 112 | 113 | /* 114 | * Advance the physics simulation by the specified amount. Invoked during 115 | * each update. 116 | * 117 | * wallClockSeconds: the elapsed wall-clock time since the previous 118 | * invocation of updatePhysics() (in seconds, >=0) 119 | */ 120 | override fun updatePhysics(wallClockSeconds: Float) { 121 | physicsSpace.update(wallClockSeconds) 122 | } 123 | // ************************************************************************* 124 | // private functions 125 | 126 | /* 127 | * Configure keyboard input during startup. 128 | */ 129 | private fun configureInput() { 130 | getInputManager().add(object : InputProcessor() { 131 | override fun onKeyboard(glfwKeyId: Int, isPressed: Boolean) { 132 | if (glfwKeyId == GLFW.GLFW_KEY_E) { 133 | if (isPressed) { 134 | // Disable the ball's contact response. 135 | ball!!.setContactResponse(false) 136 | 137 | // Activate the ball in case it got deactivated. 138 | ball!!.activate() 139 | } 140 | return 141 | } 142 | super.onKeyboard(glfwKeyId, isPressed) 143 | } 144 | }) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloDamping.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.collision.shapes.BoxCollisionShape 34 | import com.jme3.bullet.objects.PhysicsRigidBody 35 | import com.jme3.math.Vector3f 36 | 37 | /* 38 | * A simple example illustrating the effect of damping on dynamic rigid bodies. 39 | * 40 | * Builds upon HelloRigidBody. 41 | * 42 | * author: Stephen Gold sgold@sonic.net 43 | */ 44 | 45 | /* 46 | * Main entry point for the HelloDamping application. 47 | */ 48 | fun main() { 49 | val application = HelloDamping() 50 | application.start() 51 | } 52 | 53 | class HelloDamping : BasePhysicsApp() { 54 | /* 55 | * Create the PhysicsSpace. Invoked once during initialization. 56 | */ 57 | override fun createSpace(): PhysicsSpace { 58 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 59 | 60 | // For clarity, disable gravity. 61 | result.setGravity(Vector3f.ZERO) 62 | 63 | return result 64 | } 65 | 66 | /* 67 | * Populate the PhysicsSpace. Invoked once during initialization. 68 | */ 69 | override fun populateSpace() { 70 | // Create a CollisionShape for unit cubes. 71 | val cubeHalfExtent = 0.5f 72 | val cubeShape = BoxCollisionShape(cubeHalfExtent) 73 | 74 | // Create 4 cubes (dynamic rigid bodies) and add them to the space. 75 | val numCubes = 4 76 | val cubeMass = 2f 77 | val cube = mutableListOf() 78 | for (cubeIndex in 0 ..< numCubes) { 79 | val c = PhysicsRigidBody(cubeShape, cubeMass) 80 | cube.add(c) 81 | physicsSpace.addCollisionObject(c) 82 | 83 | // Disable sleep (deactivation) for clarity. 84 | c.setEnableSleep(false) 85 | } 86 | 87 | // Locate the cubes 4 psu apart, center to center. 88 | cube[0].setPhysicsLocation(Vector3f(0f, +2f, 0f)) 89 | cube[1].setPhysicsLocation(Vector3f(4f, +2f, 0f)) 90 | cube[2].setPhysicsLocation(Vector3f(0f, -2f, 0f)) 91 | cube[3].setPhysicsLocation(Vector3f(4f, -2f, 0f)) 92 | 93 | // Give each cube its own set of damping parameters (linear, angular). 94 | cube[0].setDamping(0f, 0f) 95 | cube[1].setDamping(0f, 0.9f) 96 | cube[2].setDamping(0.9f, 0f) 97 | cube[3].setDamping(0.9f, 0.9f) 98 | /* 99 | * Apply an off-center impulse to each cube, 100 | * causing it to drift and spin. 101 | */ 102 | val impulse = Vector3f(-1f, 0f, 0f) 103 | val offset = Vector3f(0f, 1f, 1f) 104 | for (c in cube) { 105 | c.applyImpulse(impulse, offset) 106 | } 107 | 108 | // Visualize the shapes of all 4 rigid bodies. 109 | for (c in cube) { 110 | visualizeShape(c) 111 | } 112 | } 113 | 114 | /* 115 | * Advance the physics simulation by the specified amount. Invoked during 116 | * each update. 117 | * 118 | * wallClockSeconds: the elapsed wall-clock time since the previous 119 | * invocation of updatePhysics() (in seconds, >=0) 120 | */ 121 | override fun updatePhysics(wallClockSeconds: Float) { 122 | physicsSpace.update(wallClockSeconds) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloDeactivation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.PhysicsTickListener 34 | import com.jme3.bullet.collision.shapes.BoxCollisionShape 35 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 36 | import com.jme3.bullet.objects.PhysicsBody 37 | import com.jme3.bullet.objects.PhysicsRigidBody 38 | import com.jme3.math.Vector3f 39 | 40 | /* 41 | * A simple example of rigid-body deactivation. 42 | * 43 | * Builds upon HelloStaticBody. 44 | * 45 | * author: Stephen Gold sgold@sonic.net 46 | */ 47 | 48 | private var dynamicCube: PhysicsRigidBody? = null 49 | private var supportCube: PhysicsRigidBody? = null 50 | 51 | /* 52 | * Main entry point for the HelloDeactivation application. 53 | */ 54 | fun main() { 55 | val application = HelloDeactivation() 56 | application.start() 57 | } 58 | 59 | class HelloDeactivation : BasePhysicsApp(), PhysicsTickListener { 60 | // ************************************************************************* 61 | // BasePhysicsApp override functions 62 | 63 | /* 64 | * Create the PhysicsSpace. Invoked once during initialization. 65 | */ 66 | override fun createSpace(): PhysicsSpace { 67 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 68 | 69 | // To enable the callbacks, register the application as a tick listener. 70 | result.addTickListener(this) 71 | 72 | return result 73 | } 74 | 75 | /* 76 | * Populate the PhysicsSpace. Invoked once during initialization. 77 | */ 78 | override fun populateSpace() { 79 | // Create a dynamic cube and add it to the space. 80 | val boxHalfExtent = 0.5f 81 | val smallCubeShape = BoxCollisionShape(boxHalfExtent) 82 | val boxMass = 1f 83 | dynamicCube = PhysicsRigidBody(smallCubeShape, boxMass) 84 | physicsSpace.addCollisionObject(dynamicCube) 85 | dynamicCube!!.setPhysicsLocation(Vector3f(0f, 4f, 0f)) 86 | 87 | // Create 2 static bodies and add them to the space... 88 | // The top body serves as a temporary support. 89 | val cubeHalfExtent = 1f 90 | val largeCubeShape = BoxCollisionShape(cubeHalfExtent) 91 | supportCube = PhysicsRigidBody( 92 | largeCubeShape, PhysicsBody.massForStatic) 93 | physicsSpace.addCollisionObject(supportCube) 94 | 95 | // The bottom body serves as a visual reference point. 96 | val ballRadius = 0.5f 97 | val ballShape = SphereCollisionShape(ballRadius) 98 | val bottomBody = PhysicsRigidBody(ballShape, PhysicsBody.massForStatic) 99 | bottomBody.setPhysicsLocation(Vector3f(0f, -2f, 0f)) 100 | physicsSpace.addCollisionObject(bottomBody) 101 | 102 | // Visualize the shapes of all 3 rigid bodies: 103 | visualizeShape(dynamicCube) 104 | visualizeShape(supportCube) 105 | visualizeShape(bottomBody) 106 | } 107 | 108 | /* 109 | * Advance the physics simulation by the specified amount. Invoked during 110 | * each update. 111 | * 112 | * wallClockSeconds: the elapsed wall-clock time since the previous 113 | * invocation of updatePhysics() (in seconds, >=0) 114 | */ 115 | override fun updatePhysics(wallClockSeconds: Float) { 116 | physicsSpace.update(wallClockSeconds) 117 | } 118 | // ************************************************************************* 119 | // PhysicsTickListener override functions 120 | 121 | /* 122 | * Callback from Bullet, invoked just before each simulation step. 123 | * 124 | * space: the space that's about to be stepped (not null) 125 | * timeStep: the duration of the simulation step (in seconds, >=0) 126 | */ 127 | override fun prePhysicsTick(space: PhysicsSpace, timeStep: Float) { 128 | // do nothing 129 | } 130 | 131 | /* 132 | * Callback from Bullet, invoked just after each simulation step. 133 | * 134 | * space: the space that was just stepped (not null) 135 | * timeStep: the duration of the simulation step (in seconds, >=0) 136 | */ 137 | override fun physicsTick(space: PhysicsSpace, timeStep: Float) { 138 | /* 139 | * Once the dynamic cube gets deactivated, 140 | * remove the support cube from the PhysicsSpace. 141 | */ 142 | if (!dynamicCube!!.isActive() && space.contains(supportCube)) { 143 | space.removeCollisionObject(supportCube) 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloKinematics.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.PhysicsTickListener 34 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 35 | import com.jme3.bullet.objects.PhysicsRigidBody 36 | import com.jme3.math.FastMath 37 | import com.jme3.math.Vector3f 38 | 39 | /* 40 | * A simple example combining kinematic and dynamic rigid bodies. 41 | * 42 | * Builds upon HelloStaticBody. 43 | * 44 | * author: Stephen Gold sgold@sonic.net 45 | */ 46 | 47 | /* 48 | * Main entry point for the HelloKinematics application. 49 | */ 50 | fun main() { 51 | val application = HelloKinematics() 52 | application.start() 53 | } 54 | 55 | /* 56 | * physics-simulation time (in seconds, >=0) 57 | */ 58 | private var elapsedTime = 0f 59 | /* 60 | * kinematic ball, orbiting the origin 61 | */ 62 | private var kineBall: PhysicsRigidBody? = null 63 | 64 | class HelloKinematics : BasePhysicsApp(), PhysicsTickListener { 65 | // ************************************************************************* 66 | // BasePhysicsApp override functions 67 | 68 | /* 69 | * Create the PhysicsSpace. Invoked once during initialization. 70 | */ 71 | override fun createSpace(): PhysicsSpace { 72 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 73 | 74 | // To enable the callbacks, register the application as a tick listener. 75 | result.addTickListener(this) 76 | 77 | return result 78 | } 79 | 80 | /* 81 | * Populate the PhysicsSpace. Invoked once during initialization. 82 | */ 83 | override fun populateSpace() { 84 | // Create a CollisionShape for balls. 85 | val ballRadius = 1f 86 | val ballShape = SphereCollisionShape(ballRadius) 87 | 88 | // Create a dynamic body and add it to the space. 89 | val mass = 2f 90 | val dynaBall = PhysicsRigidBody(ballShape, mass) 91 | physicsSpace.addCollisionObject(dynaBall) 92 | dynaBall.setPhysicsLocation(Vector3f(0f, 4f, 0f)) 93 | 94 | // Create a kinematic body and add it to the space. 95 | kineBall = PhysicsRigidBody(ballShape) 96 | physicsSpace.addCollisionObject(kineBall) 97 | kineBall!!.setKinematic(true) 98 | 99 | // Visualize the shapes of both rigid bodies: 100 | visualizeShape(dynaBall) 101 | visualizeShape(kineBall) 102 | } 103 | 104 | /* 105 | * Advance the physics simulation by the specified amount. Invoked during 106 | * each update. 107 | * 108 | * wallClockSeconds: the elapsed wall-clock time since the previous 109 | * invocation of updatePhysics() (in seconds, >=0) 110 | */ 111 | override fun updatePhysics(wallClockSeconds: Float) { 112 | physicsSpace.update(wallClockSeconds) 113 | } 114 | // ************************************************************************* 115 | // PhysicsTickListener override functions 116 | 117 | /* 118 | * Callback from Bullet, invoked just before each simulation step. 119 | * 120 | * space: the space that's about to be stepped (not null) 121 | * timeStep: the duration of the simulation step (in seconds, >=0) 122 | */ 123 | override fun prePhysicsTick(space: PhysicsSpace, timeStep: Float) { 124 | // Make the kinematic ball orbit the origin. 125 | val orbitalPeriod = 0.8f // seconds 126 | val phaseAngle = elapsedTime * FastMath.TWO_PI / orbitalPeriod 127 | 128 | val orbitRadius = 0.4f // physics-space units 129 | val x = orbitRadius * FastMath.sin(phaseAngle) 130 | val y = orbitRadius * FastMath.cos(phaseAngle) 131 | val location = Vector3f(x, y, 0f) 132 | kineBall!!.setPhysicsLocation(location) 133 | 134 | elapsedTime += timeStep 135 | } 136 | 137 | /* 138 | * Callback from Bullet, invoked just after each simulation step. 139 | * 140 | * space: the space that was just stepped (not null) 141 | * timeStep: the duration of the simulation step (in seconds, >=0) 142 | */ 143 | override fun physicsTick(space: PhysicsSpace, timeStep: Float) { 144 | // do nothing 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloMadMallet.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.input.RotateMode 32 | import com.github.stephengold.sport.physics.BasePhysicsApp 33 | import com.jme3.bullet.PhysicsSpace 34 | import com.jme3.bullet.collision.shapes.CompoundCollisionShape 35 | import com.jme3.bullet.collision.shapes.CylinderCollisionShape 36 | import com.jme3.bullet.objects.PhysicsBody 37 | import com.jme3.bullet.objects.PhysicsRigidBody 38 | import com.jme3.math.Vector3f 39 | 40 | /* 41 | * A simple example of a dynamic rigid body with an implausible center. 42 | * 43 | * Builds upon HelloStaticBody. 44 | * 45 | * author: Stephen Gold sgold@sonic.net 46 | */ 47 | 48 | /* 49 | * Main entry point for the HelloMadMallet application. 50 | */ 51 | fun main() { 52 | val application = HelloMadMallet() 53 | application.start() 54 | } 55 | 56 | class HelloMadMallet : BasePhysicsApp() { 57 | /* 58 | * Create the PhysicsSpace. Invoked once during initialization. 59 | */ 60 | override fun createSpace(): PhysicsSpace { 61 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 62 | result.setGravity(Vector3f(0f, -50f, 0f)) 63 | 64 | return result 65 | } 66 | 67 | /* 68 | * Initialize the application. Invoked once. 69 | */ 70 | override fun initialize() { 71 | super.initialize() 72 | getCameraInputProcessor().setRotationMode(RotateMode.DragLMB) 73 | 74 | // Position the camera for a good view. 75 | cam.setLocation(Vector3f(10f, -2.75f, 0f)) 76 | cam.setUpAngle(0.05f) 77 | cam.setAzimuth(-3.05f) 78 | } 79 | 80 | /* 81 | * Populate the PhysicsSpace. Invoked once during initialization. 82 | */ 83 | override fun populateSpace() { 84 | // Construct a compound shape for the mallet. 85 | val headLength = 1f 86 | val headRadius = 0.5f 87 | val hes = Vector3f(headLength / 2f, headRadius, headRadius) 88 | val headShape = CylinderCollisionShape(hes, PhysicsSpace.AXIS_X) 89 | 90 | val handleLength = 3f 91 | val handleRadius = 0.3f 92 | hes.set(handleRadius, handleRadius, handleLength / 2f) 93 | val handleShape = CylinderCollisionShape(hes, PhysicsSpace.AXIS_Z) 94 | 95 | val malletShape = CompoundCollisionShape() 96 | malletShape.addChildShape(handleShape, 0f, 0f, handleLength / 2f) 97 | malletShape.addChildShape(headShape, 0f, 0f, handleLength) 98 | 99 | // Create a dynamic body for the mallet. 100 | val mass = 2f 101 | val mallet = PhysicsRigidBody(malletShape, mass) 102 | mallet.setPhysicsLocation(Vector3f(0f, 4f, 0f)) 103 | 104 | // Increase the mallet's angular damping to stabilize it. 105 | mallet.setAngularDamping(0.9f) 106 | 107 | physicsSpace.addCollisionObject(mallet) 108 | 109 | // Create a static disc and add it to the space. 110 | val discRadius = 5f 111 | val discThickness = 0.5f 112 | val discShape = CylinderCollisionShape( 113 | discRadius, discThickness, PhysicsSpace.AXIS_Y) 114 | val disc = PhysicsRigidBody(discShape, PhysicsBody.massForStatic) 115 | physicsSpace.addCollisionObject(disc) 116 | disc.setPhysicsLocation(Vector3f(0f, -3f, 0f)) 117 | 118 | // Visualize the mallet, including its local axes: 119 | visualizeShape(mallet) 120 | val debugAxisLength = 1f 121 | visualizeAxes(mallet, debugAxisLength) 122 | 123 | // Visualize the shape of the disc: 124 | visualizeShape(disc) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloMassDistribution.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.input.RotateMode 32 | import com.github.stephengold.sport.physics.BasePhysicsApp 33 | import com.jme3.bullet.PhysicsSpace 34 | import com.jme3.bullet.collision.shapes.CompoundCollisionShape 35 | import com.jme3.bullet.collision.shapes.CylinderCollisionShape 36 | import com.jme3.bullet.objects.PhysicsBody 37 | import com.jme3.bullet.objects.PhysicsRigidBody 38 | import com.jme3.math.Transform 39 | import com.jme3.math.Vector3f 40 | import com.jme3.util.BufferUtils 41 | import java.nio.FloatBuffer 42 | 43 | /* 44 | * A simple example to demonstrate the use of principalAxes() and correctAxes() 45 | * to improve the plausibility of a compound shape. 46 | * 47 | * Builds upon HelloMadMallet. 48 | * 49 | * author: Stephen Gold sgold@sonic.net 50 | */ 51 | 52 | /* 53 | * Main entry point for the HelloMassDistribution application. 54 | */ 55 | fun main() { 56 | val application = HelloMassDistribution() 57 | application.start() 58 | } 59 | 60 | class HelloMassDistribution : BasePhysicsApp() { 61 | /* 62 | * Create the PhysicsSpace. Invoked once during initialization. 63 | */ 64 | override fun createSpace(): PhysicsSpace { 65 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 66 | result.setGravity(Vector3f(0f, -50f, 0f)) 67 | 68 | return result 69 | } 70 | 71 | /* 72 | * Initialize the application. Invoked once. 73 | */ 74 | override fun initialize() { 75 | super.initialize() 76 | getCameraInputProcessor().setRotationMode(RotateMode.DragLMB) 77 | 78 | // Position the camera for a good view. 79 | cam.setLocation(Vector3f(10f, -2.75f, 0f)) 80 | cam.setUpAngle(0.05f) 81 | cam.setAzimuth(-3.05f) 82 | } 83 | 84 | /* 85 | * Populate the PhysicsSpace. Invoked once during initialization. 86 | */ 87 | override fun populateSpace() { 88 | // Construct a compound shape for the mallet. 89 | val headLength = 1f 90 | val headRadius = 0.5f 91 | val hes = Vector3f(headLength / 2f, headRadius, headRadius) 92 | val headShape = CylinderCollisionShape(hes, PhysicsSpace.AXIS_X) 93 | 94 | val handleLength = 3f 95 | val handleRadius = 0.3f 96 | hes.set(handleRadius, handleRadius, handleLength / 2f) 97 | val handleShape = CylinderCollisionShape(hes, PhysicsSpace.AXIS_Z) 98 | 99 | val malletShape = CompoundCollisionShape() 100 | malletShape.addChildShape(handleShape, 0f, 0f, handleLength / 2f) 101 | malletShape.addChildShape(headShape, 0f, 0f, handleLength) 102 | 103 | // Calculate a correction to put 75% of the mass in the head. 104 | val handleMass = 0.5f 105 | val headMass = 1.5f 106 | val massDistribution = BufferUtils.createFloatBuffer( 107 | handleMass, headMass) 108 | val inertiaVector = Vector3f() 109 | val correction = malletShape.principalAxes( 110 | massDistribution, null, inertiaVector) 111 | 112 | // Correct the shape. 113 | malletShape.correctAxes(correction) 114 | 115 | // Create a dynamic body for the mallet. 116 | val mass = handleMass + headMass 117 | val mallet = PhysicsRigidBody(malletShape, mass) 118 | mallet.setPhysicsLocation(Vector3f(0f, 4f, 0f)) 119 | 120 | // Increase the mallet's angular damping to stabilize it. 121 | mallet.setAngularDamping(0.9f) 122 | 123 | // The mallet's center has changed, so adjust its moment of inertia. 124 | val inverseInertia = Vector3f(1f, 1f, 1f).divideLocal(inertiaVector) 125 | mallet.setInverseInertiaLocal(inverseInertia) 126 | 127 | physicsSpace.addCollisionObject(mallet) 128 | 129 | // Create a static disc and add it to the space. 130 | val discRadius = 5f 131 | val discThickness = 0.5f 132 | val discShape = CylinderCollisionShape( 133 | discRadius, discThickness, PhysicsSpace.AXIS_Y) 134 | val disc = PhysicsRigidBody(discShape, PhysicsBody.massForStatic) 135 | physicsSpace.addCollisionObject(disc) 136 | disc.setPhysicsLocation(Vector3f(0f, -3f, 0f)) 137 | 138 | // Visualize the mallet, including its local axes: 139 | visualizeShape(mallet) 140 | val debugAxisLength = 1f 141 | visualizeAxes(mallet, debugAxisLength) 142 | 143 | // Visualize the shape of the disc: 144 | visualizeShape(disc) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloNonUniformGravity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.github.stephengold.sport.physics.LocalAxisGeometry 33 | import com.jme3.bullet.PhysicsSpace 34 | import com.jme3.bullet.PhysicsTickListener 35 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 36 | import com.jme3.bullet.objects.PhysicsRigidBody 37 | import com.jme3.math.Vector3f 38 | import jme3utilities.math.MyVector3f 39 | 40 | /* 41 | * A simple example of non-uniform gravity. 42 | * 43 | * Builds upon HelloRigidBody. 44 | * 45 | * author: Stephen Gold sgold@sonic.net 46 | */ 47 | 48 | /* 49 | * Main entry point for the HelloNonUniformGravity application. 50 | */ 51 | fun main() { 52 | val application = HelloNonUniformGravity() 53 | application.start() 54 | } 55 | 56 | /* 57 | * dynamic body subjected to non-uniform gravity 58 | */ 59 | private var planet: PhysicsRigidBody? = null 60 | /* 61 | * temporary storage for vectors 62 | */ 63 | private val tmpVector = Vector3f() 64 | 65 | class HelloNonUniformGravity : 66 | BasePhysicsApp(), PhysicsTickListener { 67 | // ************************************************************************* 68 | // BasePhysicsApp override functions 69 | 70 | /* 71 | * Create the PhysicsSpace. Invoked once during initialization. 72 | */ 73 | override fun createSpace(): PhysicsSpace { 74 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 75 | 76 | // To enable the callbacks, register the application as a tick listener. 77 | result.addTickListener(this) 78 | 79 | // Reduce the time step for better accuracy. 80 | result.setAccuracy(0.005f) 81 | 82 | return result 83 | } 84 | 85 | /* 86 | * Populate the PhysicsSpace. Invoked once during initialization. 87 | */ 88 | override fun populateSpace() { 89 | // Create a CollisionShape for the planet. 90 | val planetRadius = 0.1f 91 | val planetShape = SphereCollisionShape(planetRadius) 92 | 93 | // Create a planet (dynamic rigid body) and add it to the space. 94 | val planetMass = 1f // physics mass unit = 10^25 kg 95 | planet = PhysicsRigidBody(planetShape, planetMass) 96 | physicsSpace.addCollisionObject(planet) 97 | 98 | // Prevent deactivation of the planet. 99 | planet!!.setEnableSleep(false) 100 | 101 | // Kick the planet into orbit around the central black hole. 102 | planet!!.setPhysicsLocation(Vector3f(2f, 0f, 0f)) 103 | planet!!.applyCentralImpulse(Vector3f(0f, -1f, 0f)) 104 | 105 | // Visualize the shape of the planet: 106 | visualizeShape(planet) 107 | 108 | // Visualize axes to indicate the black hole's location: 109 | LocalAxisGeometry(null, MyVector3f.xAxis, 1f) 110 | LocalAxisGeometry(null, MyVector3f.yAxis, 1f) 111 | } 112 | 113 | /* 114 | * Advance the physics simulation by the specified amount. Invoked during 115 | * each update. 116 | * 117 | * wallClockSeconds: the elapsed wall-clock time since the previous 118 | * invocation of updatePhysics() (in seconds, >=0) 119 | */ 120 | override fun updatePhysics(wallClockSeconds: Float) { 121 | physicsSpace.update(wallClockSeconds) 122 | } 123 | // ************************************************************************* 124 | // PhysicsTickListener override functions 125 | 126 | /* 127 | * Callback from Bullet, invoked just before each simulation step. 128 | * 129 | * space: the space that's about to be stepped (not null) 130 | * timeStep: the duration of the simulation step (in seconds, >=0) 131 | */ 132 | override fun prePhysicsTick(space: PhysicsSpace, timeStep: Float) { 133 | // Calculate the gravitational acceleration GM/r^2. 134 | planet!!.getPhysicsLocation(tmpVector) 135 | val r2 = tmpVector.lengthSquared() //squared distance from black hole 136 | MyVector3f.normalizeLocal(tmpVector) 137 | tmpVector.multLocal(-3f / r2) 138 | planet!!.setGravity(tmpVector) 139 | } 140 | 141 | /* 142 | * Callback from Bullet, invoked just after each simulation step. 143 | * 144 | * space: the space that was just stepped (not null) 145 | * timeStep: the duration of the simulation step (in seconds, >=0) 146 | */ 147 | override fun physicsTick(space: PhysicsSpace, timeStep: Float) { 148 | // do nothing 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloPin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.Mesh 32 | import com.github.stephengold.sport.mesh.ClothGrid 33 | import com.github.stephengold.sport.physics.BasePhysicsApp 34 | import com.github.stephengold.sport.physics.LinksGeometry 35 | import com.github.stephengold.sport.physics.PinsGeometry 36 | import com.jme3.bullet.PhysicsSoftSpace 37 | import com.jme3.bullet.PhysicsSpace 38 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 39 | import com.jme3.bullet.objects.PhysicsBody 40 | import com.jme3.bullet.objects.PhysicsRigidBody 41 | import com.jme3.bullet.objects.PhysicsSoftBody 42 | import com.jme3.bullet.objects.infos.SoftBodyConfig 43 | import com.jme3.bullet.objects.infos.SoftBodyMaterial 44 | import com.jme3.bullet.util.NativeSoftBodyUtil 45 | import com.jme3.math.Vector3f 46 | 47 | /* 48 | * A simple cloth simulation with a pinned node. 49 | * 50 | * Builds upon HelloCloth. 51 | * 52 | * author: Stephen Gold sgold@sonic.net 53 | */ 54 | 55 | /* 56 | * Main entry point for the HelloPin application. 57 | */ 58 | fun main() { 59 | val application = HelloPin() 60 | application.start() 61 | } 62 | 63 | class HelloPin : BasePhysicsApp() { 64 | /* 65 | * Create the PhysicsSpace. Invoked once during initialization. 66 | */ 67 | override fun createSpace(): PhysicsSoftSpace { 68 | val worldMin = Vector3f(-999f, -999f, -999f) 69 | val worldMax = Vector3f(+999f, +999f, +999f) 70 | val result = PhysicsSoftSpace( 71 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT) 72 | 73 | return result 74 | } 75 | 76 | /* 77 | * Initialize the application. Invoked once. 78 | */ 79 | override fun initialize() { 80 | super.initialize() 81 | 82 | // Relocate the camera. 83 | cam.setLocation(Vector3f(0f, 1f, 8f)) 84 | } 85 | 86 | /* 87 | * Populate the PhysicsSpace. Invoked once during initialization. 88 | */ 89 | override fun populateSpace() { 90 | // Create a static, rigid sphere and add it to the physics space. 91 | val radius = 1f 92 | val shape = SphereCollisionShape(radius) 93 | val sphere = PhysicsRigidBody(shape, PhysicsBody.massForStatic) 94 | physicsSpace.addCollisionObject(sphere) 95 | visualizeShape(sphere) 96 | 97 | // Generate a subdivided square mesh with alternating diagonals. 98 | val numLines = 41 99 | val lineSpacing = 0.1f // mesh units 100 | val squareGrid = ClothGrid(numLines, numLines, lineSpacing) 101 | 102 | // Create a soft square and add it to the physics space. 103 | val cloth = PhysicsSoftBody() 104 | NativeSoftBodyUtil.appendFromTriMesh(squareGrid, cloth) 105 | physicsSpace.addCollisionObject(cloth) 106 | 107 | // Pin one of the corner nodes by setting its mass to zero. 108 | val nodeIndex = 0 // upper left corner 109 | cloth.setNodeMass(nodeIndex, PhysicsBody.massForStatic) 110 | /* 111 | * Make the cloth flexible by reducing the angular stiffness 112 | * of its material. 113 | */ 114 | val mat = cloth.getSoftMaterial() 115 | mat.setAngularStiffness(0f) // default=1 116 | /* 117 | * Improve simulation accuracy by increasing 118 | * the number of position-solver iterations for the cloth. 119 | */ 120 | val config = cloth.getSoftConfig() 121 | config.setPositionIterations(9) // default=1 122 | 123 | // Translate the cloth upward to its starting location. 124 | cloth.applyTranslation(Vector3f(0f, 2f, 0f)) 125 | 126 | // Visualize the soft-body links and pin: 127 | LinksGeometry(cloth) 128 | PinsGeometry(cloth) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloRigidBody.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 34 | import com.jme3.bullet.objects.PhysicsRigidBody 35 | import com.jme3.math.Vector3f 36 | 37 | /* 38 | * A simple example of 2 colliding balls, illustrating the 5 basic features of 39 | * responsive, dynamic, rigid bodies: 40 | * 1. rigidity (fixed shape), 41 | * 2. inertia (resistance to changes of motion), 42 | * 3. dynamics (motion determined by forces, torques, and impulses), 43 | * 4. gravity (continual downward force), and 44 | * 5. contact response (avoid intersecting with other bodies). 45 | * 46 | * Builds upon HelloSport. 47 | * 48 | * author: Stephen Gold sgold@sonic.net 49 | */ 50 | 51 | /* 52 | * Main entry point for the HelloRigidBody application. 53 | */ 54 | fun main() { 55 | val application = HelloRigidBody() 56 | application.start() 57 | } 58 | 59 | class HelloRigidBody : BasePhysicsApp() { 60 | /* 61 | * Create the PhysicsSpace. Invoked once during initialization. 62 | */ 63 | override fun createSpace(): PhysicsSpace { 64 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 65 | return result 66 | } 67 | 68 | /* 69 | * Populate the PhysicsSpace. Invoked once during initialization. 70 | */ 71 | override fun populateSpace() { 72 | // Create a CollisionShape for balls. 73 | val ballRadius = 1f 74 | val ballShape = SphereCollisionShape(ballRadius) 75 | 76 | // Create 2 balls (dynamic rigid bodies) and add them to the space. 77 | val ballMass = 2f 78 | val ball1 = PhysicsRigidBody(ballShape, ballMass) 79 | physicsSpace.addCollisionObject(ball1) 80 | val ball2 = PhysicsRigidBody(ballShape, ballMass) 81 | physicsSpace.addCollisionObject(ball2) 82 | 83 | // Locate the balls initially 2 PSU (physics-space units) apart. 84 | // In other words, 4 PSU from center to center. 85 | ball1.setPhysicsLocation(Vector3f(1f, 1f, 0f)) 86 | ball2.setPhysicsLocation(Vector3f(5f, 1f, 0f)) 87 | 88 | // Set ball #2 on a collision course with ball #1. 89 | ball2.applyCentralImpulse(Vector3f(-25f, 0f, 0f)) 90 | 91 | // Visualize the shapes of both rigid bodies: 92 | visualizeShape(ball1) 93 | visualizeShape(ball2) 94 | } 95 | 96 | /* 97 | * Advance the physics simulation by the specified amount. Invoked during 98 | * each update. 99 | * 100 | * wallClockSeconds: the elapsed wall-clock time since the previous 101 | * invocation of updatePhysics() (in seconds, >=0) 102 | */ 103 | override fun updatePhysics(wallClockSeconds: Float) { 104 | // For clarity, simulate at 1/10th normal speed. 105 | val simulateSeconds = 0.1f * wallClockSeconds 106 | physicsSpace.update(simulateSeconds) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloSoftBody.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.Mesh 32 | import com.github.stephengold.sport.mesh.IcosphereMesh 33 | import com.github.stephengold.sport.physics.BasePhysicsApp 34 | import com.github.stephengold.sport.physics.FacesGeometry 35 | import com.jme3.bullet.PhysicsSoftSpace 36 | import com.jme3.bullet.PhysicsSpace 37 | import com.jme3.bullet.collision.shapes.BoxCollisionShape 38 | import com.jme3.bullet.objects.PhysicsBody 39 | import com.jme3.bullet.objects.PhysicsRigidBody 40 | import com.jme3.bullet.objects.PhysicsSoftBody 41 | import com.jme3.bullet.objects.infos.Sbcp 42 | import com.jme3.bullet.objects.infos.SoftBodyConfig 43 | import com.jme3.bullet.util.NativeSoftBodyUtil 44 | import com.jme3.math.Vector3f 45 | 46 | /* 47 | * A simple example of a soft body colliding with a static rigid body. 48 | * 49 | * Builds upon HelloStaticBody. 50 | * 51 | * author: Stephen Gold sgold@sonic.net 52 | */ 53 | 54 | /* 55 | * Main entry point for the HelloSoftBody application. 56 | */ 57 | fun main() { 58 | val application = HelloSoftBody() 59 | application.start() 60 | } 61 | 62 | class HelloSoftBody : BasePhysicsApp() { 63 | // ************************************************************************* 64 | // BasePhysicsApp override functions 65 | 66 | /* 67 | * Create the PhysicsSpace. Invoked once during initialization. 68 | */ 69 | override fun createSpace(): PhysicsSoftSpace { 70 | val worldMin = Vector3f(-999f, -999f, -999f) 71 | val worldMax = Vector3f(+999f, +999f, +999f) 72 | val result = PhysicsSoftSpace( 73 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT) 74 | 75 | return result 76 | } 77 | 78 | /* 79 | * Initialize the application. Invoked once. 80 | */ 81 | override fun initialize() { 82 | super.initialize() 83 | 84 | // Relocate the camera. 85 | cam.setLocation(Vector3f(0f, 1f, 8f)) 86 | } 87 | 88 | /* 89 | * Populate the PhysicsSpace. Invoked once during initialization. 90 | */ 91 | override fun populateSpace() { 92 | addBox() 93 | 94 | // A mesh is used to generate the shape and topology of the soft body. 95 | val numRefinementIterations = 3 96 | val sphere = IcosphereMesh(numRefinementIterations, true) 97 | 98 | // Create a soft ball and add it to the physics space. 99 | val body = PhysicsSoftBody() 100 | NativeSoftBodyUtil.appendFromTriMesh(sphere, body) 101 | physicsSpace.addCollisionObject(body) 102 | /* 103 | * Set the ball's default frame pose: if deformed, 104 | * it will tend to return to its current shape. 105 | */ 106 | val setVolumePose = false 107 | val setFramePose = true 108 | body.setPose(setVolumePose, setFramePose) 109 | 110 | // Enable pose matching to make the body bouncy. 111 | val config = body.getSoftConfig() 112 | config.set(Sbcp.PoseMatching, 0.05f) 113 | 114 | // Translate the body to its start location. 115 | body.applyTranslation(Vector3f(0f, 3f, 0f)) 116 | 117 | // Visualize the soft-body faces: 118 | FacesGeometry(body) 119 | } 120 | // ************************************************************************* 121 | // private methods 122 | 123 | /* 124 | * Add a large static cube to serve as a platform. 125 | */ 126 | private fun addBox() { 127 | val halfExtent = 3f // mesh units 128 | val shape = BoxCollisionShape(halfExtent) 129 | 130 | val body = PhysicsRigidBody(shape, PhysicsBody.massForStatic) 131 | body.setPhysicsLocation(Vector3f(0f, -halfExtent, 0f)) 132 | physicsSpace.addCollisionObject(body) 133 | 134 | visualizeShape(body) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloSoftRope.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.Mesh 32 | import com.github.stephengold.sport.mesh.DividedLine 33 | import com.github.stephengold.sport.physics.BasePhysicsApp 34 | import com.github.stephengold.sport.physics.LinksGeometry 35 | import com.github.stephengold.sport.physics.PinsGeometry 36 | import com.jme3.bullet.PhysicsSoftSpace 37 | import com.jme3.bullet.PhysicsSpace 38 | import com.jme3.bullet.objects.PhysicsBody 39 | import com.jme3.bullet.objects.PhysicsSoftBody 40 | import com.jme3.bullet.util.NativeSoftBodyUtil 41 | import com.jme3.math.Vector3f 42 | 43 | /* 44 | * A simple rope simulation using a soft body. 45 | * 46 | * Builds upon HelloPin. 47 | * 48 | * author: Stephen Gold sgold@sonic.net 49 | */ 50 | 51 | /* 52 | * Main entry point for the HelloSoftRope application. 53 | */ 54 | fun main() { 55 | val application = HelloSoftRope() 56 | application.start() 57 | } 58 | 59 | class HelloSoftRope : BasePhysicsApp() { 60 | /* 61 | * Create the PhysicsSpace. Invoked once during initialization. 62 | */ 63 | override fun createSpace(): PhysicsSoftSpace { 64 | val worldMin = Vector3f(-999f, -999f, -999f) 65 | val worldMax = Vector3f(+999f, +999f, +999f) 66 | val result = PhysicsSoftSpace( 67 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT) 68 | 69 | return result 70 | } 71 | 72 | /* 73 | * Initialize the application. Invoked once. 74 | */ 75 | override fun initialize() { 76 | super.initialize() 77 | 78 | // Relocate the camera. 79 | cam.setLocation(Vector3f(0f, 1f, 8f)) 80 | } 81 | 82 | /** 83 | * Populate the PhysicsSpace. Invoked once during initialization. 84 | */ 85 | override fun populateSpace() { 86 | // Generate a subdivided line segment. 87 | val numSegments = 40 88 | val endPoint1 = Vector3f(0f, 4f, 0f) 89 | val endPoint2 = Vector3f(2f, 4f, 2f) 90 | val lineMesh = DividedLine(endPoint1, endPoint2, numSegments) 91 | 92 | // Create a soft body and add it to the physics space. 93 | val rope = PhysicsSoftBody() 94 | NativeSoftBodyUtil.appendFromLineMesh(lineMesh, rope) 95 | physicsSpace.addCollisionObject(rope) 96 | 97 | // Pin one of the end nodes by setting its mass to zero. 98 | val nodeIndex = 0 99 | rope.setNodeMass(nodeIndex, PhysicsBody.massForStatic) 100 | 101 | // Visualize the soft-body links and pin: 102 | LinksGeometry(rope) 103 | PinsGeometry(rope) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloSoftSoft.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.Mesh 32 | import com.github.stephengold.sport.mesh.IcosphereMesh 33 | import com.github.stephengold.sport.physics.BasePhysicsApp 34 | import com.github.stephengold.sport.physics.FacesGeometry 35 | import com.jme3.bullet.PhysicsSoftSpace 36 | import com.jme3.bullet.PhysicsSpace 37 | import com.jme3.bullet.objects.PhysicsSoftBody 38 | import com.jme3.bullet.objects.infos.ConfigFlag 39 | import com.jme3.bullet.objects.infos.Sbcp 40 | import com.jme3.bullet.objects.infos.SoftBodyConfig 41 | import com.jme3.bullet.util.NativeSoftBodyUtil 42 | import com.jme3.math.Vector3f 43 | 44 | /* 45 | * A simple example of a soft-soft collision. 46 | * 47 | * Builds upon HelloSoftBody. 48 | * 49 | * author: Stephen Gold sgold@sonic.net 50 | */ 51 | 52 | /* 53 | * Main entry point for the HelloSoftSoft application. 54 | */ 55 | fun main() { 56 | val application = HelloSoftSoft() 57 | application.start() 58 | } 59 | 60 | class HelloSoftSoft : BasePhysicsApp() { 61 | /* 62 | * Create the PhysicsSpace. Invoked once during initialization. 63 | */ 64 | override fun createSpace(): PhysicsSoftSpace { 65 | val worldMin = Vector3f(-999f, -999f, -999f) 66 | val worldMax = Vector3f(+999f, +999f, +999f) 67 | val result = PhysicsSoftSpace( 68 | worldMin, worldMax, PhysicsSpace.BroadphaseType.DBVT) 69 | 70 | // Set gravity to zero. 71 | result.setGravity(Vector3f.ZERO) // default = (0,-9.81,0) 72 | 73 | return result 74 | } 75 | 76 | /* 77 | * Initialize the application. Invoked once. 78 | */ 79 | override fun initialize() { 80 | super.initialize() 81 | 82 | // Relocate the camera. 83 | cam.setLocation(Vector3f(0f, 1f, 8f)) 84 | } 85 | 86 | /* 87 | * Populate the PhysicsSpace. Invoked once during initialization. 88 | */ 89 | override fun populateSpace() { 90 | /* 91 | * A mesh is used to generate the shape and topology 92 | * of each soft body. 93 | */ 94 | val numRefinementIterations = 3 95 | val sphere = IcosphereMesh(numRefinementIterations, true) 96 | 97 | // Create 2 squishy balls and add them to the physics space. 98 | val ball1 = PhysicsSoftBody() 99 | NativeSoftBodyUtil.appendFromTriMesh(sphere, ball1) 100 | physicsSpace.addCollisionObject(ball1) 101 | 102 | val ball2 = PhysicsSoftBody() 103 | NativeSoftBodyUtil.appendFromTriMesh(sphere, ball2) 104 | physicsSpace.addCollisionObject(ball2) 105 | /* 106 | * Set each ball's default frame pose: if deformed, 107 | * it will tend to return to its current shape. 108 | */ 109 | val setVolumePose = false 110 | val setFramePose = true 111 | ball1.setPose(setVolumePose, setFramePose) 112 | ball2.setPose(setVolumePose, setFramePose) 113 | 114 | // Enable pose matching to make the balls bouncy. 115 | val config1 = ball1.getSoftConfig() 116 | config1.set(Sbcp.PoseMatching, 0.01f) // default = 0 117 | val config2 = ball2.getSoftConfig() 118 | config2.set(Sbcp.PoseMatching, 0.01f) 119 | /* 120 | * Enable soft-soft collisions for each ball. 121 | * Clearing all other collision flags disables soft-rigid collisions. 122 | */ 123 | config1.setCollisionFlags(ConfigFlag.VF_SS) // default = SDF_RS 124 | config2.setCollisionFlags(ConfigFlag.VF_SS) 125 | 126 | // Translate ball2 upward and put it on a collision course with ball1. 127 | ball2.applyTranslation(Vector3f(0f, 3f, 0f)) 128 | ball2.setVelocity(Vector3f(0f, -1f, 0f)) 129 | 130 | // Visualize the soft-body faces: 131 | FacesGeometry(ball1) 132 | FacesGeometry(ball2) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloSport.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.collision.shapes.PlaneCollisionShape 34 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 35 | import com.jme3.bullet.objects.PhysicsBody 36 | import com.jme3.bullet.objects.PhysicsRigidBody 37 | import com.jme3.math.Plane 38 | import com.jme3.math.Vector3f 39 | 40 | /* 41 | * Drop a dynamic sphere onto a horizontal surface and visualize them both using 42 | * SPORT graphics. 43 | * 44 | * Builds upon HelloLibbulletjme. 45 | * 46 | * author: Stephen Gold sgold@sonic.net 47 | */ 48 | 49 | /* 50 | * Main entry point for the HelloSport application. 51 | */ 52 | fun main() { 53 | val application = HelloSport() 54 | application.start() 55 | /* 56 | * During initialization, BasePhysicsApp loads the native library 57 | * and invokes createSpace() and populateSpace(). 58 | */ 59 | } 60 | 61 | class HelloSport : BasePhysicsApp() { 62 | /* 63 | * Create the PhysicsSpace. Invoked once during initialization. 64 | */ 65 | override fun createSpace(): PhysicsSpace { 66 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 67 | return result 68 | } 69 | 70 | /* 71 | * Populate the PhysicsSpace. Invoked once during initialization. 72 | */ 73 | override fun populateSpace() { 74 | // Add a static horizontal plane at y=-1: 75 | val groundY = -1f 76 | val plane = Plane(Vector3f.UNIT_Y, groundY) 77 | val planeShape = PlaneCollisionShape(plane) 78 | val floor = PhysicsRigidBody(planeShape, PhysicsBody.massForStatic) 79 | physicsSpace!!.addCollisionObject(floor) 80 | 81 | // Add a sphere-shaped, dynamic, rigid body at the origin: 82 | val radius = 0.3f 83 | val ballShape = SphereCollisionShape(radius) 84 | val mass = 1f 85 | val ball = PhysicsRigidBody(ballShape, mass) 86 | physicsSpace!!.addCollisionObject(ball) 87 | 88 | // Visualize the shapes of both rigid bodies: 89 | visualizeShape(floor) 90 | visualizeShape(ball) 91 | } 92 | 93 | /* 94 | * Advance the physics simulation by the specified amount. Invoked during 95 | * each update. 96 | * 97 | * wallClockSeconds: the elapsed wall-clock time since the previous 98 | * invocation of updatePhysics() (in seconds, >=0) 99 | */ 100 | override fun updatePhysics(wallClockSeconds: Float) { 101 | physicsSpace.update(wallClockSeconds) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/HelloStaticBody.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps 30 | 31 | import com.github.stephengold.sport.physics.BasePhysicsApp 32 | import com.jme3.bullet.PhysicsSpace 33 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 34 | import com.jme3.bullet.objects.PhysicsBody 35 | import com.jme3.bullet.objects.PhysicsRigidBody 36 | import com.jme3.math.Vector3f 37 | 38 | /* 39 | * A simple example combining static and dynamic rigid bodies. 40 | * 41 | * Builds upon HelloRigidBody. 42 | * 43 | * author: Stephen Gold sgold@sonic.net 44 | */ 45 | 46 | /* 47 | * Main entry point for the HelloStaticBody application. 48 | */ 49 | fun main() { 50 | val application = HelloStaticBody() 51 | application.start() 52 | } 53 | 54 | class HelloStaticBody : BasePhysicsApp() { 55 | /* 56 | * Create the PhysicsSpace. Invoked once during initialization. 57 | */ 58 | override fun createSpace(): PhysicsSpace { 59 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 60 | return result 61 | } 62 | 63 | /* 64 | * Populate the PhysicsSpace. Invoked once during initialization. 65 | */ 66 | override fun populateSpace() { 67 | // Create a CollisionShape for balls. 68 | val ballRadius = 1f 69 | val ballShape = SphereCollisionShape(ballRadius) 70 | 71 | // Create a dynamic body and add it to the space. 72 | val mass = 2f 73 | val dynaBall = PhysicsRigidBody(ballShape, mass) 74 | physicsSpace.addCollisionObject(dynaBall) 75 | 76 | // Create a static body and add it to the space. 77 | val statBall = PhysicsRigidBody(ballShape, PhysicsBody.massForStatic) 78 | physicsSpace.addCollisionObject(statBall) 79 | 80 | // Position the balls in physics space. 81 | dynaBall.setPhysicsLocation(Vector3f(0f, 4f, 0f)) 82 | statBall.setPhysicsLocation(Vector3f(0.1f, 0f, 0f)) 83 | 84 | // Visualize the shapes of both rigid bodies: 85 | visualizeShape(dynaBall) 86 | visualizeShape(statBall) 87 | } 88 | 89 | /* 90 | * Advance the physics simulation by the specified amount. Invoked during 91 | * each update. 92 | * 93 | * wallClockSeconds: the elapsed wall-clock time since the previous 94 | * invocation of updatePhysics() (in seconds, >=0) 95 | */ 96 | override fun updatePhysics(wallClockSeconds: Float) { 97 | physicsSpace.update(wallClockSeconds) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /kotlin-apps/src/main/kotlin/com/github/stephengold/lbjexamples/ktapps/console/HelloLibbulletjme.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Stephen Gold and Yanis Boudiaf 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package com.github.stephengold.lbjexamples.ktapps.console 30 | 31 | import com.jme3.bullet.PhysicsSpace 32 | import com.jme3.bullet.collision.shapes.PlaneCollisionShape 33 | import com.jme3.bullet.collision.shapes.SphereCollisionShape 34 | import com.jme3.bullet.objects.PhysicsBody 35 | import com.jme3.bullet.objects.PhysicsRigidBody 36 | import com.jme3.math.Plane 37 | import com.jme3.math.Vector3f 38 | import electrostatic4j.snaploader.LibraryInfo; 39 | import electrostatic4j.snaploader.LoadingCriterion; 40 | import electrostatic4j.snaploader.NativeBinaryLoader; 41 | import electrostatic4j.snaploader.filesystem.DirectoryPath; 42 | import electrostatic4j.snaploader.platform.NativeDynamicLibrary; 43 | import electrostatic4j.snaploader.platform.util.PlatformPredicate; 44 | 45 | /* 46 | * Drop a dynamic sphere onto a horizontal surface (non-graphical illustrative 47 | * example). 48 | * 49 | * author: Stephen Gold sgold@sonic.net 50 | */ 51 | 52 | private var ball: PhysicsRigidBody? = null 53 | private var physicsSpace: PhysicsSpace? = null 54 | 55 | /* 56 | * Main entry point for the HelloLibbulletjme application. 57 | */ 58 | fun main() { 59 | val info = LibraryInfo(null, "bulletjme", DirectoryPath.USER_DIR) 60 | val loader = NativeBinaryLoader(info) 61 | 62 | val libraries = arrayOf( 63 | NativeDynamicLibrary("native/linux/arm64", PlatformPredicate.LINUX_ARM_64), 64 | NativeDynamicLibrary("native/linux/arm32", PlatformPredicate.LINUX_ARM_32), 65 | NativeDynamicLibrary("native/linux/x86_64", PlatformPredicate.LINUX_X86_64), 66 | NativeDynamicLibrary("native/osx/arm64", PlatformPredicate.MACOS_ARM_64), 67 | NativeDynamicLibrary("native/osx/x86_64", PlatformPredicate.MACOS_X86_64), 68 | NativeDynamicLibrary("native/windows/x86_64", PlatformPredicate.WIN_X86_64) 69 | ) 70 | loader.registerNativeLibraries(libraries) 71 | .initPlatformLibrary() 72 | .setLoggingEnabled(true) 73 | loader.setRetryWithCleanExtraction(true) 74 | 75 | // Load the Libbulletjme native library for this platform. 76 | try { 77 | loader.loadLibrary(LoadingCriterion.CLEAN_EXTRACTION) 78 | } catch (exception: Exception) { 79 | throw IllegalStateException("Failed to load the Libbulletjme library!") 80 | } 81 | 82 | physicsSpace = createSpace() 83 | populateSpace() 84 | 85 | val location = Vector3f() 86 | for (iteration in 0 ..< 50) { 87 | updatePhysics(intervalSeconds = 0.02f) 88 | 89 | ball!!.getPhysicsLocation(location) 90 | println(location) 91 | } 92 | } 93 | 94 | /* 95 | * Create the PhysicsSpace. Invoked once during initialization. 96 | */ 97 | private fun createSpace(): PhysicsSpace { 98 | val result = PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT) 99 | return result 100 | } 101 | 102 | /* 103 | * Populate the PhysicsSpace. Invoked once during initialization. 104 | */ 105 | private fun populateSpace() { 106 | // Add a static horizontal plane at y=-1: 107 | val groundY = -1f 108 | val plane = Plane(Vector3f.UNIT_Y, groundY) 109 | val planeShape = PlaneCollisionShape(plane) 110 | val floor = PhysicsRigidBody(planeShape, PhysicsBody.massForStatic) 111 | physicsSpace!!.addCollisionObject(floor) 112 | 113 | // Add a sphere-shaped, dynamic, rigid body at the origin: 114 | val radius = 0.3f 115 | val ballShape = SphereCollisionShape(radius) 116 | val mass = 1f 117 | ball = PhysicsRigidBody(ballShape, mass) 118 | physicsSpace!!.addCollisionObject(ball) 119 | } 120 | 121 | /* 122 | * Advance the physics simulation by the specified amount. 123 | * 124 | * intervalSeconds: the amount of time to simulate (in seconds, >=0) 125 | */ 126 | private fun updatePhysics(intervalSeconds: Float) { 127 | val maxSteps = 0 // for a single step of the specified duration 128 | physicsSpace!!.update(intervalSeconds, maxSteps) 129 | } -------------------------------------------------------------------------------- /kotlin-apps/src/main/resources/Textures/Terrain/splat/mountains512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/kotlin-apps/src/main/resources/Textures/Terrain/splat/mountains512.png -------------------------------------------------------------------------------- /kotlin-apps/src/main/resources/Textures/greenTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephengold/LbjExamples/5d761d3708d4bdd7a07f7d89f9cc345b1edd174b/kotlin-apps/src/main/resources/Textures/greenTile.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@antora/cli": "3.1.10", 4 | "@antora/site-generator": "3.1.10" 5 | }, 6 | "overrides": { 7 | "asciidoctor-opal-runtime": { 8 | "glob": "~10.4" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // global build settings shared by all LbjExamples subprojects 2 | 3 | rootProject.name = "LbjExamples" 4 | 5 | dependencyResolutionManagement { 6 | repositories { 7 | //mavenLocal() // to find libraries installed locally 8 | mavenCentral() // to find libraries released to the Maven Central repository 9 | //maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } // to find public snapshots of LWJGL 10 | //maven { url = uri("https://s01.oss.sonatype.org/content/groups/staging") } // to find libraries staged but not yet released 11 | //maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") } // to find public snapshots of libraries 12 | } 13 | } 14 | 15 | // subprojects: 16 | include("apps") 17 | include("docs") 18 | include("kotlin-apps") 19 | --------------------------------------------------------------------------------