Просмотр исходного кода

Merge pull request #5 from jMonkeyEngine/master

Fork update
joliver82 6 лет назад
Родитель
Сommit
38e330bab7
100 измененных файлов с 1741 добавлено и 859 удалено
  1. 93 0
      .github/actions/tools/bintray.sh
  2. 85 0
      .github/actions/tools/uploadToMaven.sh
  3. 548 0
      .github/workflows/main.yml
  4. 5 9
      .gitignore
  5. 0 85
      .travis.yml
  6. 11 0
      CONTRIBUTING.md
  7. 9 11
      README.md
  8. 0 63
      appveyor.yml
  9. 75 2
      build.gradle
  10. 1 1
      common-android-app.gradle
  11. 18 5
      common.gradle
  12. 17 11
      gradle.properties
  13. 9 4
      jme3-android-examples/build.gradle
  14. 5 5
      jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java
  15. 2 0
      jme3-android-native/.gitignore
  16. 1 1
      jme3-android-native/build.gradle
  17. 7 16
      jme3-android-native/decode.gradle
  18. BIN
      jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so
  19. BIN
      jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so
  20. BIN
      jme3-android-native/libs/decode/armeabi/libdecodejme.so
  21. BIN
      jme3-android-native/libs/decode/mips/libdecodejme.so
  22. BIN
      jme3-android-native/libs/decode/mips64/libdecodejme.so
  23. BIN
      jme3-android-native/libs/decode/x86/libdecodejme.so
  24. BIN
      jme3-android-native/libs/decode/x86_64/libdecodejme.so
  25. BIN
      jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so
  26. BIN
      jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so
  27. BIN
      jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so
  28. BIN
      jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so
  29. BIN
      jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so
  30. BIN
      jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so
  31. BIN
      jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so
  32. 11 18
      jme3-android-native/openalsoft.gradle
  33. 2 2
      jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.c
  34. 1 1
      jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.c
  35. 1 1
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.c
  36. 0 173
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.h
  37. 2 2
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.c
  38. 0 77
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.h
  39. 2 2
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.c
  40. 0 101
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.h
  41. 7 0
      jme3-android/build.gradle
  42. 1 1
      jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
  43. 2 2
      jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java
  44. 1 1
      jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java
  45. 2 1
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java
  46. 57 11
      jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
  47. 22 4
      jme3-android/src/main/java/com/jme3/system/android/AndroidConfigChooser.java
  48. 17 5
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
  49. 114 0
      jme3-android/src/main/java/com/jme3/util/AndroidBufferAllocator.java
  50. 1 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  51. 1 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  52. 2 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java
  53. 9 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
  54. 25 59
      jme3-bullet-native-android/build.gradle
  55. BIN
      jme3-bullet-native-android/libs/arm64-v8a/libbulletjme.so
  56. BIN
      jme3-bullet-native-android/libs/armeabi-v7a/libbulletjme.so
  57. BIN
      jme3-bullet-native-android/libs/armeabi/libbulletjme.so
  58. BIN
      jme3-bullet-native-android/libs/mips/libbulletjme.so
  59. BIN
      jme3-bullet-native-android/libs/mips64/libbulletjme.so
  60. BIN
      jme3-bullet-native-android/libs/x86/libbulletjme.so
  61. BIN
      jme3-bullet-native-android/libs/x86_64/libbulletjme.so
  62. 1 0
      jme3-bullet-native-android/src/native/android/Android.mk
  63. 2 2
      jme3-bullet-native-android/src/native/android/Application.mk
  64. 119 83
      jme3-bullet-native/build.gradle
  65. BIN
      jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
  66. BIN
      jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
  67. BIN
      jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
  68. BIN
      jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
  69. BIN
      jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
  70. BIN
      jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll
  71. 18 0
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_PhysicsCollisionObject.cpp
  72. 19 0
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_CollisionShape.cpp
  73. 13 1
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp
  74. 24 0
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_SixDofJoint.cpp
  75. 38 1
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp
  76. 1 3
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp
  77. 6 18
      jme3-bullet/build.gradle
  78. 23 46
      jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java
  79. 12 0
      jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java
  80. 17 4
      jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java
  81. 18 0
      jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java
  82. 1 1
      jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java
  83. 28 0
      jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java
  84. 27 1
      jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java
  85. 34 0
      jme3-core/src/main/java/com/jme3/anim/AnimComposer.java
  86. 3 0
      jme3-core/src/main/java/com/jme3/anim/Joint.java
  87. 6 4
      jme3-core/src/main/java/com/jme3/anim/TransformTrack.java
  88. 1 0
      jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java
  89. 33 1
      jme3-core/src/main/java/com/jme3/app/state/AbstractAppState.java
  90. 6 0
      jme3-core/src/main/java/com/jme3/app/state/AppState.java
  91. 51 2
      jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java
  92. 22 0
      jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java
  93. 18 0
      jme3-core/src/main/java/com/jme3/app/state/RootNodeAppState.java
  94. 4 1
      jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java
  95. 5 1
      jme3-core/src/main/java/com/jme3/audio/AudioNode.java
  96. 3 3
      jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java
  97. 5 5
      jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java
  98. 15 0
      jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java
  99. 1 1
      jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java
  100. 1 1
      jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java

+ 93 - 0
.github/actions/tools/bintray.sh

@@ -0,0 +1,93 @@
+#!/bin/bash
+
+# bintray_createPackage [REPO] [PACKAGE] [USER] [PASSWORD] [GIT REPO] [LICENSE]
+function bintray_createPackage {
+    repo="$1"
+    package="$2"
+    user="$3"
+    password="$4"
+    srcrepo="$5"
+    license="$6"
+
+    repoUrl="https://api.bintray.com/packages/$repo"
+    if [ "`curl -u$user:$password -H Content-Type:application/json -H Accept:application/json \
+    --write-out %{http_code} --silent --output /dev/null -X GET \"$repoUrl/$package\"`" != "200" ];
+    then
+
+        if [ "$srcrepo" != "" -a "$license" != "" ];
+        then 
+            echo "Package does not exist... create."
+            data="{
+                \"name\": \"${package}\",
+                \"labels\": [],
+                \"licenses\": [\"${license}\"],
+                \"vcs_url\": \"${srcrepo}\"
+            }"
+     
+
+            curl -u$user:$password -H "Content-Type:application/json" -H "Accept:application/json" -X POST \
+                -d "${data}" "$repoUrl"
+        else
+            echo "Package does not exist... you need to specify a repo and license for it to be created."
+        fi
+    else    
+        echo "The package already exists. Skip."
+    fi
+}
+
+# uploadFile file destination [REPO] "content" [PACKAGE] [USER] [PASSWORD] [SRCREPO] [LICENSE]
+function bintray_uploadFile {
+    file="$1"
+    dest="$2"
+    
+    echo "Upload $file to $dest"
+
+    repo="$3"
+    type="$4"
+    package="$5"
+
+    user="$6"
+    password="$7"
+   
+    srcrepo="$8"
+    license="$9"
+    publish="${10}"
+
+    bintray_createPackage $repo $package $user $password $srcrepo $license
+
+    url="https://api.bintray.com/$type/$repo/$package/$dest"
+    if [ "$publish" = "true" ]; then url="$url;publish=1"; fi
+
+    curl -T "$file" -u$user:$password "$url"
+     
+}
+
+function bintray_uploadAll {
+    path="$1"
+    destpath="$2"
+    repo="$3"
+    type="$4"
+    package="$5"
+
+    user="$6"
+    password="$7"
+   
+    srcrepo="$8"
+    license="$9"
+    publish="${10}"
+
+    cdir="$PWD"
+    cd "$path"
+
+    files="`find . -type f -print`"
+    IFS="
+"
+    set -f
+    for f in $files; do
+        destfile="$destpath/${f:2}"
+        bintray_uploadFile $f $destfile $repo $type $package $user $password $srcrepo $license $publish
+    done
+    set +f
+    unset IFS
+    cd "$cdir"
+}

+ 85 - 0
.github/actions/tools/uploadToMaven.sh

@@ -0,0 +1,85 @@
+#!/bin/bash
+#############################################
+#
+# Usage
+#       uploadAllToMaven path/of/dist/maven https://api.bintray.com/maven/riccardo/sandbox-maven/ riccardo $BINTRAY_PASSWORD gitrepo license
+#           Note: gitrepo and license are needed only when uploading to bintray if you want to create missing packages automatically
+#                   gitrepo must be a valid source repository
+#                   license must be a license supported by bintray eg "BSD 3-Clause"
+#   or
+#       uploadAllToMaven path/of/dist/maven $GITHUB_PACKAGE_REPOSITORY user password
+#
+#############################################
+root="`dirname  ${BASH_SOURCE[0]}`"
+source $root/bintray.sh
+
+set -e
+function uploadToMaven {
+    file="$1"
+    destfile="$2"
+    repourl="$3"
+    user="$4"
+    password="$5"
+    srcrepo="$6"
+    license="$7"
+
+    auth=""
+
+    if [ "$user" != "token" ];
+    then
+        echo "Upload with username $user and password"
+        auth="-u$user:$password"
+    else
+        echo "Upload with token"
+        auth="-H \"Authorization: token $password\""
+    fi
+
+    
+    if [[ $repourl == https\:\/\/api.bintray.com\/* ]]; 
+    then 
+        package="`dirname $destfile`"
+        version="`basename $package`"
+        package="`dirname $package`"
+        package="`basename $package`"
+
+        if [ "$user" = "" -o "$password" = "" ];
+        then
+            echo "Error! You need username and password to upload to bintray"
+             exit 1
+        fi
+        echo "Detected bintray"
+
+        bintrayRepo="${repourl/https\:\/\/api.bintray.com\/maven/}"   
+        echo "Create package on $bintrayRepo"
+
+        bintray_createPackage $bintrayRepo $package $user $password $srcrepo $license  
+        
+        repourl="$repourl/$package"    
+    fi
+
+    cmd="curl -T \"$file\" $auth \
+        \"$repourl/$destfile\" \
+        -vvv"
+
+    echo "Run $cmd"
+    eval "$cmd"    
+}
+export -f uploadToMaven
+
+function uploadAllToMaven {
+    path="$1"
+    cdir="$PWD"
+    cd "$path"
+    files="`find . \( -name "*.jar" -o -name "*.pom" \) -type f -print`"
+    IFS="
+"
+    set -f
+    for art in $files; do
+        art="${art:2}"
+        uploadToMaven "$art" "$art" ${@:2}   
+    done
+    set +f
+    unset IFS
+
+    cd "$cdir"
+} 

+ 548 - 0
.github/workflows/main.yml

@@ -0,0 +1,548 @@
+######################################################################################
+# JME CI/CD 
+######################################################################################
+# Quick overview of what is going on in this script:
+#   - Build natives for android
+#   - Build natives for linux arm
+#   - Build natives for windows,mac,linux x86_64 and x86
+#   - Merge the natives, build the engine, create the zip release, maven artifacts, javadoc and native snapshot
+#   - (only when there is a change in the native code) Deploy the native snapshot to bintray
+#   - (only when building a release) Deploy everything else to github releases, github packet registry and bintray
+#   - (only when building a release) Update javadoc.jmonkeyengine.org
+# Note:
+#   All the actions/upload-artifact and actions/download-artifact steps are used to pass 
+#   stuff between jobs, github actions has some sort of storage that is local to the
+#   running workflow, we use it to store the result of each job since the filesystem
+#   is not maintained between jobs.
+################# CONFIGURATIONS #####################################################
+# >> Configure BINTRAY RELEASE & NATIVE SNAPSHOT
+#   Configure the following secrets/variables (customize the values with your own)
+#     BINTRAY_GENERIC_REPO=riccardoblsandbox/jmonkeyengine-files
+#     BINTRAY_MAVEN_REPO=riccardoblsandbox/jmonkeyengine
+#     BINTRAY_USER=riccardo
+#     BINTRAY_APIKEY=XXXXXX
+#     BINTRAY_LICENSE="BSD 3-Clause"
+# >> Configure  PACKAGE REGISTRY RELEASE
+#   Nothing to do here, everything is autoconfigured to work with the account/org that 
+#   is running the build.
+# >> Configure  JAVADOC
+#     JAVADOC_GHPAGES_REPO="riccardoblsandbox/javadoc.jmonkeyengine.org.git"
+#   Generate a deloy key
+#       ssh-keygen -t rsa -b 4096 -C "[email protected]" -f javadoc_deploy
+#   Set
+#     JAVADOC_GHPAGES_DEPLOY_PRIVKEY="......."
+#   In github repo -> Settings, use javadoc_deploy.pub as Deploy key with write access  
+######################################################################################
+# Resources:
+#   - Github actions docs: https://help.github.com/en/articles/about-github-actions
+#   - Package registry docs: https://help.github.com/en/articles/about-github-package-registry
+#   - Official actions: https://github.com/actions
+#   - Community actions: https://github.com/sdras/awesome-actions
+######################################################################################
+# - Riccardo Balbo
+######################################################################################
+
+name: Build jMonkeyEngine
+on:
+  push:
+    branches:
+      - master
+      - newbuild
+      - v3.3.*
+      - v3.2
+      - v3.2.*
+  pull_request:
+  release:
+    types: [published]
+  
+jobs:
+  
+  # Builds the natives on linux arm
+  BuildLinuxArmNatives:
+    name: Build natives for linux (arm)
+    runs-on: ubuntu-18.04
+    container:
+      image: riccardoblb/buildenv-jme3:linuxArm
+   
+    steps:
+      - name: Clone the repo
+        uses: actions/checkout@v1     
+        with:
+          fetch-depth: 1
+
+      - name: Build        
+        run: |
+          # Build
+          # Note: since this is crossbuild we use the buildForPlatforms filter to tell
+          # the buildscript wich platforms it should build for.
+          gradle -PuseCommitHashAsVersionName=true --no-daemon -PbuildForPlatforms=LinuxArm,LinuxArmHF,LinuxArm64 -PbuildNativeProjects=true \
+          :jme3-bullet-native:assemble 
+
+      - name: Upload natives
+        uses: actions/upload-artifact@master
+        with:
+          name: linuxarm-natives
+          path: build/native 
+
+  # Build the natives on android
+  BuildAndroidNatives:
+    name: Build natives for android
+    runs-on: ubuntu-18.04
+    container:
+      image: riccardoblb/buildenv-jme3:android
+  
+    steps:
+      - name: Clone the repo
+        uses: actions/checkout@v1     
+        with:
+          fetch-depth: 1
+
+      - name: Build        
+        run: |       
+          gradle -PuseCommitHashAsVersionName=true --no-daemon -PbuildNativeProjects=true \
+          :jme3-android-native:assemble \
+          :jme3-bullet-native-android:assemble 
+     
+      - name: Upload natives
+        uses: actions/upload-artifact@master
+        with:
+          name: android-natives
+          path: build/native
+
+  # Build the natives
+  BuildNatives:
+    strategy:
+      fail-fast: true
+      matrix:
+        os: [ubuntu-18.04,windows-2019,macOS-latest] 
+        jdk: [8.x.x]
+        include:
+          - os: ubuntu-18.04
+            osName: linux
+          - os: windows-2019
+            osName: windows
+          - os: macOS-latest
+            osName: mac
+              
+    name: Build natives for ${{ matrix.osName }}
+    runs-on: ${{ matrix.os }}    
+    steps:    
+    
+      - name: Clone the repo
+        uses: actions/checkout@v1     
+        with:
+          fetch-depth: 1
+           
+      - name: Prepare java environment
+        uses: actions/setup-java@v1
+        with:
+          java-version: ${{ matrix.jdk }}
+          architecture: x64 
+          
+      - name: Build Natives
+        shell: bash
+        env:
+          OS_NAME: ${{ matrix.osName }}
+        run: |
+          # Install dependencies
+          if [ "$OS_NAME" = "mac" ];
+          then
+            echo "Prepare mac"        
+          
+          elif [ "$OS_NAME" = "linux" ];
+          then
+            echo "Prepare linux"
+            sudo apt-get update
+            sudo apt-get install -y gcc-multilib g++-multilib
+          else
+            echo "Prepare windows"
+          fi
+          
+          # Build
+          gradle  -PuseCommitHashAsVersionName=true --no-daemon  -PbuildNativeProjects=true -Dmaven.repo.local="$PWD/dist/maven" \
+          build \
+          :jme3-bullet-native:build
+                  
+      # Upload natives to be used later by the BuildJMonkey job
+      - name: Upload natives
+        uses: actions/upload-artifact@master
+        with:
+          name: ${{ matrix.osName }}-natives
+          path: build/native
+ 
+
+  # Build the engine, we only deploy from ubuntu-18.04 jdk8
+  BuildJMonkey:  
+    needs: [BuildNatives,BuildAndroidNatives]
+    name: Build on ${{ matrix.osName }} jdk${{ matrix.jdk }}
+    runs-on: ${{ matrix.os }}    
+    strategy:
+      fail-fast: true
+      matrix:
+        os: [ubuntu-18.04,windows-2019,macOS-latest]
+        jdk: [8.x.x,11.x.x]
+        include:
+          - os: ubuntu-18.04
+            osName: linux
+            deploy: true
+          - os: windows-2019
+            osName: windows
+          - os: macOS-latest
+            osName: mac 
+          - jdk: 11.x.x
+            deploy: false 
+
+    steps:          
+      - name: Clone the repo
+        uses: actions/checkout@v1
+        with:
+          fetch-depth: 1
+          
+      - name: Setup the java environment
+        uses: actions/setup-java@v1
+        with:
+          java-version: ${{ matrix.jdk }}
+          architecture: x64 
+
+      - name: Download natives for linux
+        uses: actions/download-artifact@master
+        with:
+          name: linux-natives
+          path: build/native
+
+      - name: Download natives for windows
+        uses: actions/download-artifact@master
+        with:
+          name: windows-natives
+          path: build/native
+             
+      - name: Download natives for mac
+        uses: actions/download-artifact@master
+        with:
+          name: mac-natives
+          path: build/native
+
+      - name: Download natives for android
+        uses: actions/download-artifact@master
+        with:
+          name: android-natives
+          path: build/native
+
+      - name: Download natives for linux (arm)
+        uses: actions/download-artifact@master
+        with:
+          name: linuxarm-natives
+          path: build/native
+                                                     
+      - name: Build Engine
+        shell: bash
+        run: |
+          # Build
+          gradle -PuseCommitHashAsVersionName=true -PskipPrebuildLibraries=true build
+          
+          if [ "${{ matrix.deploy }}" = "true" ];
+          then  
+            # We are going to need "zip"
+            sudo apt-get update
+            sudo apt-get install -y zip
+
+            # Create the zip release and the javadoc
+            gradle -PuseCommitHashAsVersionName=true -PskipPrebuildLibraries=true mergedJavadoc createZipDistribution
+          
+            # We prepare the release for deploy
+            mkdir -p ./dist/release/
+            mv build/distributions/*.zip dist/release/
+            
+            # Create the maven artifacts
+            mkdir -p ./dist/maven/
+            gradle -PuseCommitHashAsVersionName=true -PskipPrebuildLibraries=true install -Dmaven.repo.local="$PWD/dist/maven"
+
+            # Zip the natives into a single archive (we are going to use this to deploy native snapshots)
+            echo "Create native zip"
+            cdir="$PWD"
+            cd "build/native"
+            zip -r "$cdir/dist/jme3-natives.zip" *       
+            cd "$cdir"
+            echo "Done"
+          fi         
+
+      # Used later by DeploySnapshot
+      - name: Upload merged natives
+        if: matrix.deploy==true
+        uses: actions/upload-artifact@master
+        with:
+          name: natives
+          path: dist/jme3-natives.zip
+          
+      # Upload maven artifacts to be used later by the deploy job
+      - name: Upload maven artifacts
+        if: matrix.deploy==true
+        uses: actions/upload-artifact@master
+        with:
+          name: maven
+          path: dist/maven          
+
+      - name: Upload javadoc
+        if:  matrix.deploy==true
+        uses: actions/upload-artifact@master
+        with:
+          name: javadoc
+          path: dist/javadoc        
+          
+      # Upload release archive to be used later by the deploy job    
+      - name: Upload release
+        if: github.event_name == 'release' && matrix.deploy==true
+        uses: actions/upload-artifact@master
+        with:
+          name: release
+          path: dist/release             
+
+  # This job deploys the native snapshot.
+  # The snapshot is downloaded when people build the engine without setting buildNativeProject
+  # this is useful for people that want to build only the java part and don't have
+  # all the stuff needed to compile natives.
+  DeploySnapshot:
+    needs: [BuildJMonkey]
+    name: "Deploy snapshot"
+    runs-on: ubuntu-18.04
+    if: github.event_name == 'push'
+    steps:
+
+      # We clone the repo manually, since we are going to push back a reference to the snapshot
+      - name: Clone the repo
+        run: |
+          branch="${GITHUB_REF//refs\/heads\//}"
+          if [ "$branch" != "" ];
+          then
+            git clone --single-branch --branch "$branch" https://github.com/${GITHUB_REPOSITORY}.git .
+          fi
+      
+      - name: Download merged natives
+        uses: actions/download-artifact@master
+        with:
+          name: natives
+          path: dist/
+
+      - name: Deploy natives snapshot
+        run: |
+          source .github/actions/tools/bintray.sh
+          NATIVE_CHANGES="yes"
+          branch="${GITHUB_REF//refs\/heads\//}"
+          if [ "$branch" != "" ];
+          then
+            if [ -f "natives-snapshot.properties" ];
+            then
+              nativeSnapshot=`cat "natives-snapshot.properties"`
+              nativeSnapshot="${nativeSnapshot#*=}"
+              
+              # We deploy ONLY if GITHUB_SHA (the current commit hash) is newer than $nativeSnapshot
+              if [ "`git rev-list --count $nativeSnapshot..$GITHUB_SHA`" = "0" ];
+              then
+                NATIVE_CHANGES=""
+              else
+                # We check if the native code changed.
+                echo "Detect changes"
+                NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- jme3-bullet-native/)"
+                if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot"  --  jme3-android-native/)"; fi
+                if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot"  --  jme3-bullet-native-android/)"; fi
+                if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot"  --  jme3-bullet/)"; fi
+              fi
+            fi
+
+            # We do nothing if there is no change
+            if [ "$NATIVE_CHANGES" = "" ];
+            then
+              echo "No changes, skip."
+            else
+              if [ "${{ secrets.BINTRAY_GENERIC_REPO }}" = "" ];
+              then               
+                echo "Configure the following secrets to enable native snapshot deployment"
+                echo "BINTRAY_GENERIC_REPO, BINTRAY_USER, BINTRAY_APIKEY"           
+              else
+                # Deploy snapshot
+                bintray_uploadFile dist/jme3-natives.zip \
+                  $GITHUB_SHA/$GITHUB_SHA/jme3-natives.zip \
+                  ${{ secrets.BINTRAY_GENERIC_REPO }} "content" "natives" \
+                  ${{ secrets.BINTRAY_USER }} \
+                  ${{ secrets.BINTRAY_APIKEY }}  \
+                  "https://github.com/${GITHUB_REPOSITORY}" \
+                  "${{ secrets.BINTRAY_LICENSE }}" "true"
+
+                # We reference the snapshot by writing its commit hash in  natives-snapshot.properties 
+                echo "natives.snapshot=$GITHUB_SHA" > natives-snapshot.properties
+                
+                # We commit the updated  natives-snapshot.properties
+                git config --global user.name "Github Actions"
+                git config --global user.email "[email protected]"
+      
+                git add natives-snapshot.properties
+              
+                git commit -m "[skip ci] update natives snapshot"
+                
+                # Pull rebase from the remote repo, just in case there was a push in the meantime
+                git pull -q --rebase
+
+                # We need to calculate the header for git authentication
+                header=$(echo -n "ad-m:${{ secrets.GITHUB_TOKEN }}" | base64)
+
+                # Push
+                (git -c http.extraheader="AUTHORIZATION: basic $header" push origin "$branch" || true)
+              
+              fi
+            fi
+          fi
+
+  # This job deploys the release
+  DeployRelease:  
+    needs: [BuildJMonkey]
+    name: Deploy Release
+    runs-on: ubuntu-18.04
+    if: github.event_name == 'release'
+    steps:   
+    
+      # We need to clone everything again for uploadToMaven.sh ...
+      - name: Clone the repo
+        uses: actions/checkout@v1
+        with:
+          fetch-depth: 1
+    
+      # Download all the stuff...
+      - name: Download maven artifacts
+        uses: actions/download-artifact@master
+        with:
+          name: maven
+          path: dist/maven
+      
+      - name: Download release
+        uses: actions/download-artifact@master
+        with:
+          name: release
+          path: dist/release      
+      
+      - name: Deploy to github releases    
+        run: |
+          # We need to get the release id (yeah, it's not the same as the tag)
+          echo "${GITHUB_EVENT_PATH}"
+          cat ${GITHUB_EVENT_PATH}
+          releaseId=$(jq --raw-output '.release.id' ${GITHUB_EVENT_PATH})
+
+          # Now that we have the id, we just upload the release zip from before
+          echo "Upload to release $releaseId"
+          filename="$(ls dist/release/*.zip)"
+          url="https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/$releaseId/assets?name=$(basename $filename)"
+          echo "Upload to $url"
+          curl -L \
+          -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
+          -H "Content-Type: application/zip" \
+          --data-binary @"$filename" \
+          "$url"
+            
+      - name: Deploy to bintray
+        run: |
+          source .github/actions/tools/uploadToMaven.sh
+          if [ "${{ secrets.BINTRAY_MAVEN_REPO }}" = "" ];
+          then
+            echo "Configure the following secrets to enable bintray deployment"
+            echo "BINTRAY_MAVEN_REPO, BINTRAY_USER, BINTRAY_APIKEY"
+          else
+            uploadAllToMaven dist/maven/ https://api.bintray.com/maven/${{ secrets.BINTRAY_MAVEN_REPO }} ${{ secrets.BINTRAY_USER }} ${{ secrets.BINTRAY_APIKEY }} "https://github.com/${GITHUB_REPOSITORY}" "${{ secrets.BINTRAY_LICENSE }}"
+          fi
+      
+      # - name: Deploy to github package registry
+      #   run: |
+      #     source .github/actions/tools/uploadToMaven.sh
+      #     registry="https://maven.pkg.github.com/$GITHUB_REPOSITORY"
+      #     echo "Deploy to github package registry $registry"
+      #     uploadAllToMaven dist/maven/ $registry "token" ${{ secrets.GITHUB_TOKEN }}
+          
+  # Deploy the javadoc
+  DeployJavaDoc:  
+    needs: [BuildJMonkey]
+    name: Deploy Javadoc
+    runs-on: ubuntu-18.04
+    if: github.event_name == 'release'
+    steps:   
+      
+      # We are going to need a deploy key for this, since we need
+      # to push to a different repo
+      - name: Set ssh key
+        run: |
+          mkdir -p ~/.ssh/
+          echo "${{ secrets.JAVADOC_GHPAGES_DEPLOY_PRIVKEY }}" > $HOME/.ssh/deploy.key
+          chmod 600 $HOME/.ssh/deploy.key
+
+      # We clone the javadoc repo
+      - name: Clone gh-pages
+        run: |
+          branch="gh-pages"
+          export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $HOME/.ssh/deploy.key"
+          git clone --single-branch --branch "$branch" [email protected]:${{ secrets.JAVADOC_GHPAGES_REPO }} .
+          
+      # Download the javadoc in the new directory "newdoc"
+      - name: Download javadoc
+        uses: actions/download-artifact@master
+        with:
+          name: javadoc
+          path: newdoc
+      
+      # The actual deploy
+      - name: Deploy to github pages    
+        run: |
+          set -f
+          IFS=$'\n'
+
+          # Get the tag for this release
+          version="`if [[ $GITHUB_REF == refs\/tags* ]]; then echo ${GITHUB_REF//refs\/tags\//}; fi`"
+
+          # If there is no tag, then we do nothing.
+          if [ "$version" != "" ];
+          then
+            echo "Deploy as $version"
+
+            # Remove any older version of the javadoc for this tag
+            if [ -d "$version" ];then rm -Rf "$version"; fi
+
+            # Rename newdoc with the version name
+            mv newdoc "$version"
+
+            # if there isn't an index.txt we create one (we need this to list the versions)
+            if [ ! -f "index.txt" ]; then echo "" > index.txt ; fi
+            index="`cat index.txt`"
+         
+            # Check if this version is already in index.txt
+            addNew=true
+            for v in $index; 
+            do
+              if [ "$v" = "$version" ];
+              then
+                echo "$v" "$version"
+                addNew=false
+                break
+              fi
+            done
+
+            # If not, we add it to the beginning
+            if [ "$addNew" = "true" ];
+            then
+              echo -e "$version\n$index" > index.txt
+              index="`cat index.txt`"
+            fi
+
+            # Regenerate the pages
+            chmod +x make.sh
+            ./make.sh
+
+            # Configure git to use the deploy key
+            export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $HOME/.ssh/deploy.key"
+
+            # Commit the changes
+            git config --global user.name "Github Actions"
+            git config --global user.email "[email protected]"
+      
+            git add .
+            git commit -m "$version"
+
+            branch="gh-pages"                   
+            git push origin "$branch" --force 
+
+          fi

+ 5 - 9
.gitignore

@@ -33,15 +33,7 @@
 /jme3-android-native/OpenALSoft.zip
 /jme3-android-native/src/native/jme_decode/STBI/
 /jme3-android-native/src/native/jme_decode/Tremor/
-/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.h
-/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.h
 /jme3-android-native/stb_image.h
-!/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll
-!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
-!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
-!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
-!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
-!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
 /jme3-examples/private/
 !/jme3-vr/src/main/resources/**/*.dylib
 !/jme3-vr/src/main/resources/**/*.so
@@ -49,4 +41,8 @@
 !/jme3-vr/src/main/resources/**/*.dll
 !/jme3-vr/src/main/resources/**/*.pdb
 /buildMaven.bat
-
+/private
+.travis.yml
+appveyor.yml
+javadoc_deploy
+javadoc_deploy.pub

+ 0 - 85
.travis.yml

@@ -1,85 +0,0 @@
-language: java
-sudo: true
-
-branches:
-  only:
-  - master
-  - /^v3.3.*$/
-  - v3.2
-  - /^v3.2.*$/
-
-matrix:
-  include:
-  - os: linux
-    jdk: oraclejdk8
-    env: UPLOAD=true UPLOAD_NATIVE=true
-  - os: linux
-    jdk: openjdk11
-    dist: xenial
-  - os: osx
-    osx_image: xcode9.3
-    env: UPLOAD_NATIVE=true
-
-addons:
-  ssh_known_hosts: github.com
-  hosts:
-    - travisci
-  hostname: travisci
-  apt:
-    packages:
-    - gcc-multilib
-    - g++-multilib
-
-before_install:
-  - '[ -n "$UPLOAD" ] && git fetch --unshallow || :'
-
-before_cache:
-  - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock
-  - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
-
-cache:
-  directories:
-    - $HOME/.gradle/caches/
-    - $HOME/.gradle/wrapper/
-
-install:
-  - '[ -n "$UPLOAD_NATIVE" ] && ./gradlew -PbuildNativeProjects=true assemble || ./gradlew assemble'
-
-script:
-  - ./gradlew check
-
-after_success:
-  - '[ "$TRAVIS_PULL_REQUEST" == "false" ] && [ -n "$UPLOAD_NATIVE" ] && ./private/upload_native.sh || :'
-  - '[ -n "$TRAVIS_TAG" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ -n "$UPLOAD" ] && ./gradlew bintrayUpload || :'
-
-notifications:
-  slack:
-    on_success: change
-    on_failure: always
-    rooms:
-      secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s="
-
-before_deploy:
-  - ./gradlew createZipDistribution
-  - export RELEASE_DIST=$(ls build/distributions/*.zip)
-
-deploy:
-  provider: releases
-  api_key:
-    secure: PuEsJd6juXBH29ByITW3ntSAyrwWs0IeFvXJ5Y2YlhojhSMtTwkoWeB6YmDJWP4fhzbajk4TQ1HlOX2IxJXSW/8ShOEIUlGXz9fHiST0dkSM+iRAUgC5enCLW5ITPTiem7eY9ZhS9miIam7ngce9jHNMh75PTzZrEJtezoALT9w=
-  file_glob: true
-  file: "${RELEASE_DIST}"
-  skip_cleanup: true
-  on:
-    repo: jMonkeyEngine/jmonkeyengine
-    tags: true
-
-# before_install:
-  # required libs for android build tools
-  # sudo apt-get update
-  # sudo apt-get install -qq p7zip-full
-  # sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs ia32-libs-multiarch
-  # newest Android NDK
-  # wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin -O ndk.bin
-  # 7z x ndk.bin -y > /dev/null
-  # export ANDROID_NDK=`pwd`/android-ndk-r10c

+ 11 - 0
CONTRIBUTING.md

@@ -9,6 +9,8 @@ Communication always comes first. **All** code changes and other contributions s
 
 ### New Contributors
 
+Check out the [Projects](https://github.com/jMonkeyEngine/jmonkeyengine/projects/1) tab, where the team has prioritized issues that you as a new contributor can undertake that will familiarize you to the workflow of contributing. This highlights some issues the team thinks would be a good start for new contributors but you are free to contribute on any other issues or integration you wish.
+
 When you're ready to submit your code, just make a [pull request](https://help.github.com/articles/using-pull-requests).
 
 - Do not commit your code until you have received proper feedback.
@@ -22,6 +24,15 @@ p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/
 
 Developers in the Contributors team can push directly to Main instead of submitting pull requests, however for new features it is often a good idea to do a pull request as a means to get a last code review.
 
+## Customs around integration, branching, tagging, and releases
+
+- Most pull requests are integrated directly into the master branch of the repository.
+- Integrators should note, unless the history of the pull request is important, it should be integrated to a single commit using “squash and merge”. If the history is important, favor “rebase and merge”. Don’t create a merge commit unless GitHub cannot rebase the PR.
+- For each major release (such as v3.0 or v3.3), an appropriately named release branch is created in the repository.
+- For each minor (or “dot-dot”) release (such as v3.2.3), an appropriately named tag is created in the repository.
+- In general, library changes that plausibly might break existing apps appear only in major releases, not minor ones.
+
+
 ## Building the engine
 
 1. Install [Gradle](http://www.gradle.org/)

+ 9 - 11
README.md

@@ -1,9 +1,9 @@
 jMonkeyEngine 
 =============
 
-[![Build Status](https://travis-ci.org/jMonkeyEngine/jmonkeyengine.svg?branch=master)](https://travis-ci.org/jMonkeyEngine/jmonkeyengine)
+[![Build Status](https://github.com/jMonkeyEngine/jmonkeyengine/workflows/Build%20jMonkeyEngine/badge.svg)](https://github.com/jMonkeyEngine/jmonkeyengine/actions)
 
-jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.2 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives.
+jMonkeyEngine is a 3-D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.4 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives.
 
 The engine is used by several commercial game studios and computer-science courses. Here's a taste:
 
@@ -11,17 +11,15 @@ The engine is used by several commercial game studios and computer-science cours
 
  - [jME powered games on IndieDB](http://www.indiedb.com/engines/jmonkeyengine/games)
  - [Maker's Tale](http://steamcommunity.com/sharedfiles/filedetails/?id=93461954t)
- - [Boardtastic 2](https://play.google.com/store/apps/details?id=com.boardtastic.skateboarding)
- - [Copod](http://herebeben.com/copod)
- - [Attack of the Gelatinous Blob](http://attackofthegelatinousblob.com/)
- - [Chaos](http://4realms.net/)
+ - [Boardtastic 2](https://boardtastic-2.fileplanet.com/apk)
+ - [Attack of the Gelatinous Blob](https://attack-gelatinous-blob.softwareandgames.com/)
  - [Mythruna](http://mythruna.com/)
- - [PirateHell](http://www.desura.com/games/piratehell)
- - [3089 (on steam)](http://store.steampowered.com/app/263360/)
- - [3079 (on steam)](http://store.steampowered.com/app/259620/)
- - [Lightspeed Frontier](http://www.lightspeedfrontier.com/)
+ - [PirateHell (on Steam)](https://store.steampowered.com/app/321080/Pirate_Hell/)
+ - [3089 (on Steam)](http://store.steampowered.com/app/263360/)
+ - [3079 (on Steam)](http://store.steampowered.com/app/259620/)
+ - [Lightspeed Frontier (on Steam)](https://store.steampowered.com/app/548650/Lightspeed_Frontier/)
  - [Skullstone](http://www.skullstonegame.com/)
- - [Spoxel](https://store.steampowered.com/app/746880/Spoxel/)
+ - [Spoxel (on Steam)](https://store.steampowered.com/app/746880/Spoxel/)
 
 ## Getting started
 

+ 0 - 63
appveyor.yml

@@ -1,63 +0,0 @@
-version: 1.0.{build}.{branch}
-
-branches:
-  only:
-  - master
-  - v3.2
-
-only_commits:
-  files:
-    - jme3-bullet-native/
-    - appveyor.yml
-    - gradle.properties
-
-skip_tags: true
-
-max_jobs: 1
-
-clone_depth: 1
-
-image: Visual Studio 2013
-
-environment:
-  encrypted_f0a0b284e2e8_iv:
-    secure: aImQXs4g7zMXm1nWRvlh2wPK1UQvozS1fOVNthpyoEDFZ2FvBSdXqh5NPbGh44+F
-  encrypted_f0a0b284e2e8_key:
-    secure: Ek2lqC2e19qQDRRdlvnYyLFBq3TNj6YwKTAPuJ2VElJsxi9lQg+9ZP+VbP4kbHTx6Zaa++vtmOuxLZL7gdILrEEPa1Jix2BBLBfcxBUxe6w=
-
-install:
-- cmd: >-
-    set GRADLE_LOCK=C:\Users\appveyor\.gradle\caches\modules-2\modules-2.lock
-
-    if exist %GRADLE_LOCK% del %GRADLE_LOCK%
-
-build_script:
-  - cmd: gradlew.bat -PbuildNativeProjects=true :jme3-bullet-native:assemble
-
-cache:
-- C:\Users\appveyor\.gradle\caches
-- C:\Users\appveyor\.gradle\wrapper
-- jme3-bullet-native\bullet3.zip -> gradle.properties
-
-test: off
-deploy: off
-
-on_success:
-- cmd: >-
-    if not defined encrypted_f0a0b284e2e8_key appveyor exit
-
-    openssl aes-256-cbc -K %encrypted_f0a0b284e2e8_key% -iv %encrypted_f0a0b284e2e8_iv% -in private\key.enc -out c:\users\appveyor\.ssh\id_rsa -d
-
-    git config --global user.email "appveyor"
-
-    git config --global user.name "appveyor"
-
-    git checkout -q %APPVEYOR_REPO_BRANCH%
-
-    git add -- jme3-bullet-native/libs/native/windows/
-
-    git commit -m "[ci skip] bullet: update windows natives"
-
-    git pull -q --rebase
-
-    git push [email protected]:jMonkeyEngine/jmonkeyengine.git

+ 75 - 2
build.gradle

@@ -1,3 +1,6 @@
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+
 buildscript {
     repositories {
         google()
@@ -9,6 +12,13 @@ buildscript {
     }
 }
 
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
 apply plugin: 'base'
 apply from: file('version.gradle')
 
@@ -102,8 +112,8 @@ task mergedJavadoc(type: Javadoc, description: 'Creates Javadoc from all the pro
     source subprojects.collect {project ->
         project.sourceSets*.allJava
     }
-//    classpath = files(subprojects.collect {project ->
-//            project.sourceSets*.compileClasspath})
+    classpath = files(subprojects.collect {project ->
+            project.sourceSets*.compileClasspath})
     //    source {
     //        subprojects*.sourceSets*.main*.allSource
     //    }
@@ -112,6 +122,11 @@ task mergedJavadoc(type: Javadoc, description: 'Creates Javadoc from all the pro
     }
 }
 
+clean.dependsOn('cleanMergedJavadoc')
+task cleanMergedJavadoc(type: Delete) {
+    delete file('dist/javadoc')
+}
+
 task mergedSource(type: Copy){
 
 }
@@ -141,6 +156,64 @@ task configureAndroidNDK {
     }
 }
 
+gradle.rootProject.ext.set("usePrebuildNatives", buildNativeProjects!="true");
+
+if(skipPrebuildLibraries!="true"&&buildNativeProjects!="true"){
+    String rootPath = rootProject.projectDir.absolutePath
+
+    Properties nativesSnasphotProp = new Properties()
+    File nativesSnasphotPropF=new File("${rootPath}/natives-snapshot.properties");
+    
+    if(nativesSnasphotPropF.exists()){
+
+        nativesSnasphotPropF.withInputStream { nativesSnasphotProp.load(it) }
+
+        String nativesSnasphot=nativesSnasphotProp.getProperty("natives.snapshot");
+        String nativesUrl=PREBUILD_NATIVES_URL.replace('${natives.snapshot}',nativesSnasphot)
+        println "Use natives snapshot: "+nativesUrl
+
+        String nativesZipFile="${rootPath}" + File.separator + "build"+ File.separator +nativesSnasphot+"-natives.zip"
+        String nativesPath="${rootPath}" + File.separator + "build"+ File.separator +"native"
+
+
+        task getNativesZipFile {
+            outputs.file nativesZipFile
+            doFirst {
+                File target = file(nativesZipFile);
+                println("Download natives from "+nativesUrl+" to "+nativesZipFile);
+                target.getParentFile().mkdirs();
+                ant.get(src: nativesUrl, dest: target);
+            }
+        }
+
+        task extractPrebuiltNatives {
+            inputs.file nativesZipFile
+            outputs.dir nativesPath
+            dependsOn getNativesZipFile
+
+            doFirst {
+                for(File src : zipTree(nativesZipFile)){
+                    String srcRel=src.getAbsolutePath().substring((int)(nativesZipFile.length()+1));
+                    srcRel=srcRel.substring(srcRel.indexOf( File.separator)+1);
+
+                    File dest=new File(nativesPath+File.separator+srcRel);
+                    boolean doCopy = !(dest.exists() && dest.lastModified() > src.lastModified())
+                    if (doCopy) {
+                        println("Copy "+src+" "+dest);
+                        dest.getParentFile().mkdirs();
+                        Files.copy(src.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
+                    }
+                }
+            }
+        }
+        build.dependsOn extractPrebuiltNatives
+    }
+}
+
+
+
+
+
 //class IncrementalReverseTask extends DefaultTask {
 //    @InputDirectory
 //    def File inputDir

+ 1 - 1
common-android-app.gradle

@@ -1,7 +1,7 @@
 apply plugin: 'com.android.application'
 
 group   = 'org.jmonkeyengine'
-version = jmeVersion + '-' + jmeVersionTag
+version = jmeFullVersion
 
 sourceCompatibility = '1.6'
 

+ 18 - 5
common.gradle

@@ -3,10 +3,11 @@
 //
 
 apply plugin: 'java'
+apply plugin: 'groovy'
 apply plugin: 'maven'
 
 group = 'org.jmonkeyengine'
-version = jmePomVersion
+version = jmeFullVersion
 
 sourceCompatibility = '1.8'
 [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
@@ -24,10 +25,22 @@ repositories {
 dependencies {
     // Adding dependencies here will add the dependencies to each subproject.
     testCompile group: 'junit', name: 'junit', version: '4.12'
-    testCompile group: 'org.mockito', name: 'mockito-core', version: '1.10.19'
+    testCompile group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
     testCompile group: 'org.easytesting', name: 'fest-assert-core', version: '2.0M10'
+    testCompile 'org.codehaus.groovy:groovy-all:2.5.8'
 }
 
+
+// Uncomment if you want to see the status of every test that is run and
+// the test output.
+/*
+test {
+    testLogging {
+        events "passed", "skipped", "failed", "standardOut", "standardError"
+    }
+}
+*/
+
 jar {
     manifest {
         attributes 'Implementation-Title': 'jMonkeyEngine',
@@ -38,9 +51,9 @@ jar {
 javadoc {
     failOnError = false
     options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
-    options.docTitle = "jMonkeyEngine ${jmeMainVersion} ${project.name} Javadoc"
-    options.windowTitle = "jMonkeyEngine ${jmeMainVersion} ${project.name} Javadoc"
-    options.header = "<b>jMonkeyEngine ${jmeMainVersion} ${project.name}</b>"
+    options.docTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc"
+    options.windowTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc"
+    options.header = "<b>jMonkeyEngine ${jmeFullVersion} ${project.name}</b>"
     options.author = "true"
     options.use = "true"
     options.charSet = "UTF-8"

+ 17 - 11
gradle.properties

@@ -1,11 +1,12 @@
-# Version number used for plugins, only 3 numbers (e.g. 3.1.3)
-jmeVersion = 3.3.0
-# Version used for application and settings folder, no spaces!
-jmeMainVersion = 3.3
-# Version addition pre-alpha-svn, Stable, Beta
-jmeVersionTag = SNAPSHOT
-# Increment this each time jmeVersionTag changes but jmeVersion stays the same
-jmeVersionTagID = 2
+# Version number: Major.Minor (e.g. 3.3)
+jmeVersion = 3.3
+
+# Leave empty  to autogenerate
+# (use -PjmeVersionName="myVersion" from commandline to specify a custom version name )
+jmeVersionName = 
+
+# If true, the version name will contain the commit hash
+useCommitHashAsVersionName = false
 
 # specify if JavaDoc should be built
 buildJavaDoc = true
@@ -14,18 +15,22 @@ buildJavaDoc = true
 buildNativeProjects = false
 buildAndroidExamples = false
 
+buildForPlatforms = Linux64,Linux32,Windows64,Windows32,Mac64
+# Forcefully ignore prebuilt libraries
+skipPrebuildLibraries=false
 # Path to android NDK for building native libraries
 #ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7
 ndkPath = /opt/android-ndk-r16b
 
 # Path for downloading native Bullet
-bulletUrl = https://github.com/bulletphysics/bullet3/archive/2.87.zip
-bulletFolder = bullet3-2.87
+# 2.88+
+bulletUrl = https://github.com/bulletphysics/bullet3/archive/28039903b14c2aec28beff5b3609c9308b17e76a.zip
+bulletFolder = bullet3-28039903b14c2aec28beff5b3609c9308b17e76a
 bulletZipFile = bullet3.zip
 
 # POM settings
 POM_NAME=jMonkeyEngine
-POM_DESCRIPTION=jMonkeyEngine is a 3D game engine for adventurous Java developers
+POM_DESCRIPTION=jMonkeyEngine is a 3-D game engine for adventurous Java developers
 POM_URL=http://jmonkeyengine.org
 POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine
 POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git
@@ -39,3 +44,4 @@ POM_INCEPTION_YEAR=2009
 bintray_user=
 bintray_api_key=
 
+PREBUILD_NATIVES_URL=https://dl.bintray.com/jmonkeyengine/files/${natives.snapshot}/jme3-natives.zip

+ 9 - 4
jme3-android-examples/build.gradle

@@ -1,8 +1,8 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 23
-    buildToolsVersion "23.0.3"
+    compileSdkVersion 28
+    buildToolsVersion "28.0.3"
 
     lintOptions {
         // Fix nifty gui referencing "java.awt" package.
@@ -13,7 +13,7 @@ android {
     defaultConfig {
         applicationId "org.jmonkeyengine.jme3androidexamples"
         minSdkVersion 15       // Android 4.0.3 ICE CREAM SANDWICH
-        targetSdkVersion 22    // Android 5.1 LOLLIPOP
+        targetSdkVersion 28    // Android 9 PIE
         versionCode 1
         versionName "1.0" // TODO: from settings.gradle
     }
@@ -25,6 +25,11 @@ android {
         }
     }
 
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
     sourceSets {
         main {
             java {
@@ -42,7 +47,7 @@ android {
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     testCompile 'junit:junit:4.12'
-    compile 'com.android.support:appcompat-v7:23.3.0'
+    compile 'com.android.support:appcompat-v7:28.0.0'
 
     compile project(':jme3-core')
     compile project(':jme3-android')

+ 5 - 5
jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java

@@ -62,15 +62,15 @@ public class JmeFragment extends AndroidHarnessFragment {
     public void onCreate(Bundle savedInstanceState) {
         Bundle bundle=getArguments();
 
-        appClass = bundle.getString(SELECTED_APP_CLASS);
+        appClass = bundle.getString(MainActivity.SELECTED_APP_CLASS);
 //        Log.d(this.getClass().getSimpleName(), "AppClass: " + appClass);
-        joystickEventsEnabled = bundle.getBoolean(ENABLE_JOYSTICK_EVENTS);
+        joystickEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_JOYSTICK_EVENTS);
 //        Log.d(this.getClass().getSimpleName(), "JoystickEventsEnabled: " + joystickEventsEnabled);
-        keyEventsEnabled = bundle.getBoolean(ENABLE_KEY_EVENTS);
+        keyEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_KEY_EVENTS);
 //        Log.d(this.getClass().getSimpleName(), "KeyEventsEnabled: " + keyEventsEnabled);
-        mouseEventsEnabled = bundle.getBoolean(ENABLE_MOUSE_EVENTS);
+        mouseEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_MOUSE_EVENTS);
 //        Log.d(this.getClass().getSimpleName(), "MouseEventsEnabled: " + mouseEventsEnabled);
-        boolean verboseLogging = bundle.getBoolean(VERBOSE_LOGGING);
+        boolean verboseLogging = bundle.getBoolean(MainActivity.VERBOSE_LOGGING);
 //        Log.d(this.getClass().getSimpleName(), "VerboseLogging: " + verboseLogging);
         if (verboseLogging) {
             // Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info)

+ 2 - 0
jme3-android-native/.gitignore

@@ -0,0 +1,2 @@
+# The headers are autogenerated and nobody should try to commit them...
+src/native/headers

+ 1 - 1
jme3-android-native/build.gradle

@@ -46,4 +46,4 @@ ext {
 apply from: file('openalsoft.gradle')
 // apply from: file('stb_image.gradle')
 // apply from: file('tremor.gradle')
-apply from: file('decode.gradle')
+apply from: file('decode.gradle')

+ 7 - 16
jme3-android-native/decode.gradle

@@ -8,10 +8,12 @@ String decodeBuildJniDir = decodeBuildDir + File.separator + 'jni'
 String decodeBuildLibsDir = decodeBuildDir + File.separator + 'libs'
 
 // Pre-compiled libs directory
-String decodePreCompiledLibsDir = 'libs' + File.separator + 'decode'
+def rootPath = rootProject.projectDir.absolutePath
+String decodePreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator +  'android' + File.separator + 'decode'
 
 // jME Android Native source files path
 String decodeSourceDir = 'src/native/jme_decode'
+String jmeHeaders = 'src/native/headers'
 
 task downloadStbImage(type: MyDownload) {
     sourceUrl = stbiUrl
@@ -45,24 +47,13 @@ task copyTremorFiles(type: Copy) {
     into outputDir
 }
 
-// Generate headers via javac -h
-task generateJavahHeaders(type: Exec) {
-    def files0 = fileTree("src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files1 = fileTree("src/common/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files2 = fileTree("../jme3-core/src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files3 = fileTree("../jme3-core/src/plugins/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files4 = fileTree("../jme3-core/src/tools/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files5 = fileTree("../jme3-terrain/src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def filesList = "\"" + files0.join("\"\n\"") + "\"\n\"" + files1.join("\"\n\"") + "\"\n\"" + files2.join("\"\n\"") + "\"\n\"" + files3.join("\"\n\"") + "\"\n\"" + files4.join("\"\n\"") + "\"\n\"" + files5.join("\"\n\"") + "\""
-	new File("$projectDir/java_classes.jtxt").text = filesList.replaceAll(java.util.regex.Pattern.quote("\\"), java.util.regex.Matcher.quoteReplacement("/"))
-    executable org.gradle.internal.jvm.Jvm.current().getExecutable('javac')
-    args '-h', decodeSourceDir
-    args "@$projectDir/java_classes.jtxt"
-    args '-d', decodeClassesBuildDir
+task copyJmeHeadersDecode(type: Copy) {
+    from file(jmeHeaders)
+    into file(decodeBuildJniDir + File.separator + "headers")
 }
 
 // Copy jME Android native files to jni directory
-task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, generateJavahHeaders]) {
+task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, copyJmeHeadersDecode]) {
     def sourceDir = file(decodeSourceDir)
     def outputDir = file(decodeBuildJniDir)
 

BIN
jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so


BIN
jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so


BIN
jme3-android-native/libs/decode/armeabi/libdecodejme.so


BIN
jme3-android-native/libs/decode/mips/libdecodejme.so


BIN
jme3-android-native/libs/decode/mips64/libdecodejme.so


BIN
jme3-android-native/libs/decode/x86/libdecodejme.so


BIN
jme3-android-native/libs/decode/x86_64/libdecodejme.so


BIN
jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so


+ 11 - 18
jme3-android-native/openalsoft.gradle

@@ -14,10 +14,12 @@ String openalsoftBuildJniDir = openalsoftBuildDir + File.separator + 'jni'
 String openalsoftBuildLibsDir = openalsoftBuildDir + File.separator + 'libs'
 
 //Pre-compiled libs directory
-String openalsoftPreCompiledLibsDir = 'libs' + File.separator + 'openalsoft'
+def rootPath = rootProject.projectDir.absolutePath
+String openalsoftPreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator +  'android' + File.separator + 'openalsoft'
 
 // jME Android Native source files path
 String openalsoftJmeAndroidPath = 'src/native/jme_openalsoft'
+String jmeHeaders = 'src/native/headers'
 
 // Download external source files if not available
 task downloadOpenALSoft(type: MyDownload) {
@@ -62,8 +64,14 @@ copyOpenALSoft.dependsOn {
     }
 }
 
+// Copy JME Headers to jni directory
+task copyJmeHeadersOpenAL(type: Copy) {
+    from file(jmeHeaders)
+    into file(openalsoftBuildJniDir + File.separator + "headers")
+}
+
 // Copy jME Android native files to jni directory
-task copyJmeOpenALSoft(type: Copy, dependsOn:copyOpenALSoft) {
+task copyJmeOpenALSoft(type: Copy, dependsOn: [copyOpenALSoft, copyJmeHeadersOpenAL]) {
     def sourceDir = file(openalsoftJmeAndroidPath)
     def outputDir = file(openalsoftBuildJniDir)
 //    println "copyJmeOpenALSoft sourceDir: " + sourceDir
@@ -73,22 +81,7 @@ task copyJmeOpenALSoft(type: Copy, dependsOn:copyOpenALSoft) {
     into outputDir
 }
 
-task generateOpenAlSoftHeaders(type:Exec, dependsOn: copyJmeOpenALSoft) {
-    def files0 = fileTree("src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files1 = fileTree("src/common/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files2 = fileTree("../jme3-core/src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files3 = fileTree("../jme3-core/src/plugins/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files4 = fileTree("../jme3-core/src/tools/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files5 = fileTree("../jme3-terrain/src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def filesList = "\"" + files0.join("\"\n\"") + "\"\n\"" + files1.join("\"\n\"") + "\"\n\"" + files2.join("\"\n\"") + "\"\n\"" + files3.join("\"\n\"") + "\"\n\"" + files4.join("\"\n\"") + "\"\n\"" + files5.join("\"\n\"") + "\""
-	new File("$projectDir/java_classes.jtxt").text = filesList.replaceAll(java.util.regex.Pattern.quote("\\"), java.util.regex.Matcher.quoteReplacement("/"))
-    executable org.gradle.internal.jvm.Jvm.current().getExecutable('javac')
-    args '-h', openalsoftJmeAndroidPath
-    args "@$projectDir/java_classes.jtxt"
-    args '-d', openalsoftClassesBuildDir
-}
-
-task buildOpenAlSoftNativeLib(type: Exec, dependsOn: generateOpenAlSoftHeaders) {
+task buildOpenAlSoftNativeLib(type: Exec, dependsOn: copyJmeOpenALSoft) {
 //    println "openalsoft build dir: " + openalsoftBuildDir
 //    println "ndkCommandPath: " + project.ndkCommandPath
     workingDir openalsoftBuildDir

+ 2 - 2
jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.c

@@ -4,7 +4,7 @@
 
 #include "Tremor/ivorbisfile.h"
 
-#include "com_jme3_audio_plugins_NativeVorbisFile.h"
+#include "../headers/com_jme3_audio_plugins_NativeVorbisFile.h"
 
 #ifndef NDEBUG
 #include <android/log.h>
@@ -345,4 +345,4 @@ JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_close
     free(wrapper);
     free(ovf);
     (*env)->SetObjectField(env, nvf, nvf_field_ovf, NULL);
-}
+}

+ 1 - 1
jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.c

@@ -1,4 +1,4 @@
-#include "com_jme3_texture_plugins_AndroidNativeImageLoader.h"
+#include "../headers/com_jme3_texture_plugins_AndroidNativeImageLoader.h"
 #include <assert.h>
 
 #ifndef NDEBUG

+ 1 - 1
jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.c

@@ -1,4 +1,4 @@
-#include "com_jme3_audio_android_AndroidAL.h"
+#include "../headers/com_jme3_audio_android_AndroidAL.h"
 #include "AL/al.h"
 #include "AL/alext.h"
 

+ 0 - 173
jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.h

@@ -1,173 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_jme3_audio_android_AndroidAL */
-
-#ifndef _Included_com_jme3_audio_android_AndroidAL
-#define _Included_com_jme3_audio_android_AndroidAL
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alGetString
- * Signature: (I)Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidAL_alGetString
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alGenSources
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGenSources
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alGetError
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGetError
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alDeleteSources
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alDeleteSources
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alGenBuffers
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alGenBuffers
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alDeleteBuffers
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alDeleteBuffers
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourceStop
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceStop
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourcei
- * Signature: (III)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcei
-  (JNIEnv *, jobject, jint, jint, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alBufferData
- * Signature: (IILjava/nio/ByteBuffer;II)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alBufferData
-  (JNIEnv *, jobject, jint, jint, jobject, jint, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourcePlay
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcePlay
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourcePause
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcePause
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourcef
- * Signature: (IIF)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcef
-  (JNIEnv *, jobject, jint, jint, jfloat);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSource3f
- * Signature: (IIFFF)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSource3f
-  (JNIEnv *, jobject, jint, jint, jfloat, jfloat, jfloat);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alGetSourcei
- * Signature: (II)I
- */
-JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGetSourcei
-  (JNIEnv *, jobject, jint, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourceUnqueueBuffers
- * Signature: (IILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceUnqueueBuffers
-  (JNIEnv *, jobject, jint, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSourceQueueBuffers
- * Signature: (IILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceQueueBuffers
-  (JNIEnv *, jobject, jint, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alListener
- * Signature: (ILjava/nio/FloatBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListener
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alListenerf
- * Signature: (IF)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListenerf
-  (JNIEnv *, jobject, jint, jfloat);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alListener3f
- * Signature: (IFFF)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListener3f
-  (JNIEnv *, jobject, jint, jfloat, jfloat, jfloat);
-
-/*
- * Class:     com_jme3_audio_android_AndroidAL
- * Method:    alSource3i
- * Signature: (IIIII)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSource3i
-  (JNIEnv *, jobject, jint, jint, jint, jint, jint);
-
-#ifdef __cplusplus
-}
-#endif
-#endif

+ 2 - 2
jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.c

@@ -1,5 +1,5 @@
 #include "util.h"
-#include "com_jme3_audio_android_AndroidALC.h"
+#include "../headers/com_jme3_audio_android_AndroidALC.h"
 #include "AL/alc.h"
 #include "AL/alext.h"
 
@@ -171,4 +171,4 @@ JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDeviceResumeSOF
     if (device == NULL) return;
     
     alcDeviceResumeSOFT(device);
-}
+}

+ 0 - 77
jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.h

@@ -1,77 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_jme3_audio_android_AndroidALC */
-
-#ifndef _Included_com_jme3_audio_android_AndroidALC
-#define _Included_com_jme3_audio_android_AndroidALC
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    createALC
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_createALC
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    destroyALC
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_destroyALC
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    isCreated
- * Signature: ()Z
- */
-JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidALC_isCreated
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    alcGetString
- * Signature: (I)Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidALC_alcGetString
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    alcIsExtensionPresent
- * Signature: (Ljava/lang/String;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidALC_alcIsExtensionPresent
-  (JNIEnv *, jobject, jstring);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    alcGetInteger
- * Signature: (ILjava/nio/IntBuffer;I)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcGetInteger
-  (JNIEnv *, jobject, jint, jobject, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    alcDevicePauseSOFT
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDevicePauseSOFT
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidALC
- * Method:    alcDeviceResumeSOFT
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDeviceResumeSOFT
-  (JNIEnv *, jobject);
-
-#ifdef __cplusplus
-}
-#endif
-#endif

+ 2 - 2
jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.c

@@ -1,5 +1,5 @@
 #include "util.h"
-#include "com_jme3_audio_android_AndroidEFX.h"
+#include "../headers/com_jme3_audio_android_AndroidEFX.h"
 #include "AL/alext.h"
 
 JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots
@@ -72,4 +72,4 @@ JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffectf
   (JNIEnv* env, jobject obj, jint effect, jint param, jfloat value)
 {
     alEffectf((ALuint)effect, (ALenum)param, (ALfloat)value);
-}
+}

+ 0 - 101
jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.h

@@ -1,101 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_jme3_audio_android_AndroidEFX */
-
-#ifndef _Included_com_jme3_audio_android_AndroidEFX
-#define _Included_com_jme3_audio_android_AndroidEFX
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alGenAuxiliaryEffectSlots
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alGenEffects
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenEffects
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alEffecti
- * Signature: (III)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffecti
-  (JNIEnv *, jobject, jint, jint, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alAuxiliaryEffectSloti
- * Signature: (III)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alAuxiliaryEffectSloti
-  (JNIEnv *, jobject, jint, jint, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alDeleteEffects
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteEffects
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alDeleteAuxiliaryEffectSlots
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteAuxiliaryEffectSlots
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alGenFilters
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenFilters
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alFilteri
- * Signature: (III)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alFilteri
-  (JNIEnv *, jobject, jint, jint, jint);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alFilterf
- * Signature: (IIF)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alFilterf
-  (JNIEnv *, jobject, jint, jint, jfloat);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alDeleteFilters
- * Signature: (ILjava/nio/IntBuffer;)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteFilters
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     com_jme3_audio_android_AndroidEFX
- * Method:    alEffectf
- * Signature: (IIF)V
- */
-JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffectf
-  (JNIEnv *, jobject, jint, jint, jfloat);
-
-#ifdef __cplusplus
-}
-#endif
-#endif

+ 7 - 0
jme3-android/build.gradle

@@ -1,3 +1,5 @@
+apply plugin: 'java'
+
 if (!hasProperty('mainClass')) {
     ext.mainClass = ''
 }
@@ -7,3 +9,8 @@ dependencies {
     compile project(':jme3-plugins')
     compileOnly 'android:android'
 }
+
+compileJava {
+    // The Android-Native Project requires the jni headers to be generated, so we do that here
+    options.compilerArgs += ["-h", "${project.rootDir}/jme3-android-native/src/native/headers"]
+}

+ 1 - 1
jme3-android/src/main/java/com/jme3/app/AndroidHarness.java

@@ -88,7 +88,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
     protected int eglStencilBits = 0;
 
     /**
-     * Set the desired frame rate.  If frameRate > 0, the application
+     * Set the desired frame rate.  If frameRate higher than 0, the application
      * will be capped at the desired frame rate.
      * (default = -1, no frame rate cap)
      */

+ 2 - 2
jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java

@@ -116,7 +116,7 @@ public class AndroidHarnessFragment extends Fragment implements
     protected int eglStencilBits = 0;
 
     /**
-     * Set the desired frame rate.  If frameRate > 0, the application
+     * Set the desired frame rate.  If frameRate higher than 0, the application
      * will be capped at the desired frame rate.
      * (default = -1, no frame rate cap)
      */
@@ -129,7 +129,7 @@ public class AndroidHarnessFragment extends Fragment implements
      * will have the resolution set to a maximum of maxResolutionDimension.
      * The other direction will be set to a value that maintains the aspect
      * ratio of the surfaceview. </br>
-     * Any value < 0 (default = -1) will result in the surfaceview having the
+     * Any value less than 0 (default = -1) will result in the surfaceview having the
      * same resolution as the view layout (ie. no max resolution).
      */
     protected int maxResolutionDimension = -1;

+ 1 - 1
jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java

@@ -63,7 +63,7 @@ import com.jme3.renderer.queue.RenderQueue;
  *
  *  <p>This profiler uses the Android Trace class which is only supported
  *  on Android SDK rev 18 and higher (ver 4.3 and higher).  If the
- *  device is running a version < rev 18, the logging will
+ *  device is running a version less than rev 18, the logging will
  *  be skipped.</p>
  *
  *  <p>In the MainActivity class, add the following:</p>

+ 2 - 1
jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java

@@ -71,10 +71,11 @@ import java.util.logging.Logger;
  * This is done to allow for battery conservation when sensor data or gamepads
  * are not required by the application.
  *
+ * {@code
  * To use the joystick rumble feature, the following line needs to be
  * added to the Android Manifest File
  *     <uses-permission android:name="android.permission.VIBRATE"/>
- *
+ * }
  * @author iwgeric
  */
 public class AndroidJoyInput implements JoyInput {

+ 57 - 11
jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java

@@ -34,6 +34,7 @@ package com.jme3.renderer.android;
 import android.opengl.*;
 import com.jme3.renderer.RendererException;
 import com.jme3.renderer.opengl.*;
+import com.jme3.util.BufferUtils;
 
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
@@ -41,7 +42,9 @@ import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
 
-public class AndroidGL implements GL, GLExt, GLFbo {
+public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
+
+    IntBuffer tmpBuff = BufferUtils.createIntBuffer(1);
 
     public void resetStats() {
     }
@@ -361,7 +364,7 @@ public class AndroidGL implements GL, GLExt, GLFbo {
     }
 
     public void glTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer data) {
-        GLES20.glTexImage2D(target, level, format, width, height, 0, format, type, data);
+        GLES20.glTexImage2D(target, level, internalFormat, width, height, 0, format, type, data);
     }
 
     public void glTexParameterf(int target, int pname, float param) {
@@ -449,7 +452,7 @@ public class AndroidGL implements GL, GLExt, GLFbo {
     }
 
     public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
-        throw new UnsupportedOperationException("FBO blit not available on Android");
+        GLES30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
     }
 
     public void glBufferData(int target, IntBuffer data, int usage) {
@@ -461,31 +464,31 @@ public class AndroidGL implements GL, GLExt, GLFbo {
     }
 
     public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
-        throw new UnsupportedOperationException("Instancing not available on Android");
+        GLES30.glDrawArraysInstanced(mode, first, count, primcount);
     }
 
     public void glDrawBuffers(IntBuffer bufs) {
-        throw new UnsupportedOperationException("MRT not available on Android");
+        GLES30.glDrawBuffers(bufs.limit(), bufs);
     }
 
     public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
-        throw new UnsupportedOperationException("Instancing not available on Android");
+        GLES30.glDrawElementsInstanced(mode, indices_count, type, (int)indices_buffer_offset, primcount);
     }
 
     public void glGetMultisample(int pname, int index, FloatBuffer val) {
-        throw new UnsupportedOperationException("Multisample renderbuffers not available on Android");
+        GLES31.glGetMultisamplefv(pname, index, val);
     }
 
     public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
-        throw new UnsupportedOperationException("Multisample renderbuffers not available on Android");
+        GLES30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
     }
 
     public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
-        throw new UnsupportedOperationException("Multisample textures not available on Android");
+        GLES31.glTexStorage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
     }
 
     public void glVertexAttribDivisorARB(int index, int divisor) {
-        throw new UnsupportedOperationException("Instancing not available on Android");
+        GLES30.glVertexAttribDivisor(index, divisor);
     }
 
     public void glBindFramebufferEXT(int param1, int param2) {
@@ -564,6 +567,49 @@ public class AndroidGL implements GL, GLExt, GLFbo {
     
     @Override
     public void glFramebufferTextureLayerEXT(int target, int attachment, int texture, int level, int layer) {
-        throw new UnsupportedOperationException("OpenGL ES 2 does not support texture arrays");
+        GLES30.glFramebufferTextureLayer(target, attachment, texture, level, layer);
+    }
+
+    public void glAlphaFunc(int func, float ref) {
+    }
+    
+    public void glPointSize(float size) {
+    }
+
+    public void glPolygonMode(int face, int mode) {
+    }
+
+    // Wrapper to DrawBuffers as there's no DrawBuffer method in GLES
+    public void glDrawBuffer(int mode) {
+        tmpBuff.clear();
+        tmpBuff.put(0, mode);
+        tmpBuff.rewind();
+        glDrawBuffers(tmpBuff);
+    }
+
+    public void glReadBuffer(int mode) {
+        GLES30.glReadBuffer(mode);
     }
+
+    public void glCompressedTexImage3D(int target, int level, int internalFormat, int width, int height, int depth,
+                                           int border, ByteBuffer data) {
+        GLES30.glCompressedTexImage3D(target, level, internalFormat, width, height, depth, border, getLimitBytes(data), data);
+    }
+
+    public void glCompressedTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width,
+                                              int height, int depth, int format, ByteBuffer data) {
+        GLES30.glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, getLimitBytes(data), data);
+    }
+
+    public void glTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border,
+                                 int format, int type, ByteBuffer data) {
+        GLES30.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, data);
+    }
+
+    public void glTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height,
+                                    int depth, int format, int type, ByteBuffer data) {
+        GLES30.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data);
+    }
+
 }
+

+ 22 - 4
jme3-android/src/main/java/com/jme3/system/android/AndroidConfigChooser.java

@@ -19,6 +19,7 @@ public class AndroidConfigChooser implements EGLConfigChooser {
     private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
     protected AppSettings settings;
     private final static int EGL_OPENGL_ES2_BIT = 4;
+    private final static int EGL_OPENGL_ES3_BIT = 0x40;
 
     public AndroidConfigChooser(AppSettings settings) {
         this.settings = settings;
@@ -140,12 +141,29 @@ public class AndroidConfigChooser implements EGLConfigChooser {
 
         int[] num_config = new int[1];
         int[] configSpec = new int[]{
-            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
             EGL10.EGL_NONE};
+        boolean gles3=true;
 
-        if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
-            RendererUtil.checkEGLError(egl);
-            throw new AssertionError();
+        // Try openGL ES 3
+        try {
+            if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
+                RendererUtil.checkEGLError(egl);
+                gles3=false;
+            }
+        } catch (com.jme3.renderer.RendererException re) { 
+            // it's just the device not supporting GLES3. Fallback to GLES2
+            gles3=false;
+        } 
+
+        if(!gles3)
+        {
+            // Get back to openGL ES 2
+            configSpec[1]=EGL_OPENGL_ES2_BIT;
+            if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
+                RendererUtil.checkEGLError(egl);
+                throw new AssertionError();
+            }
         }
 
         int numConfigs = num_config[0];

+ 17 - 5
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@@ -53,12 +53,15 @@ import com.jme3.input.dummy.DummyKeyInput;
 import com.jme3.input.dummy.DummyMouseInput;
 import com.jme3.renderer.android.AndroidGL;
 import com.jme3.renderer.opengl.GL;
+import com.jme3.renderer.opengl.GLES_30;
 import com.jme3.renderer.opengl.GLDebugES;
 import com.jme3.renderer.opengl.GLExt;
 import com.jme3.renderer.opengl.GLFbo;
 import com.jme3.renderer.opengl.GLRenderer;
 import com.jme3.renderer.opengl.GLTracer;
 import com.jme3.system.*;
+import com.jme3.util.AndroidBufferAllocator;
+import com.jme3.util.BufferAllocatorFactory;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -81,6 +84,14 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     protected long minFrameDuration = 0;                   // No FPS cap
     protected long lastUpdateTime = 0;
 
+    static {
+        final String implementation = BufferAllocatorFactory.PROPERTY_BUFFER_ALLOCATOR_IMPLEMENTATION;
+
+        if (System.getProperty(implementation) == null) {
+            System.setProperty(implementation, AndroidBufferAllocator.class.getName());
+        }
+    }
+
     public OGLESContext() {
     }
 
@@ -99,13 +110,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
      * @return GLSurfaceView The newly created view
      */
     public GLSurfaceView createView(Context context) {
+        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        ConfigurationInfo info = am.getDeviceConfigurationInfo();
         // NOTE: We assume all ICS devices have OpenGL ES 2.0.
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
             // below 4.0, check OpenGL ES 2.0 support.
-            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-            ConfigurationInfo info = am.getDeviceConfigurationInfo();
             if (info.reqGlEsVersion < 0x20000) {
-                throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device");
+                throw new UnsupportedOperationException("OpenGL ES 2.0 or better is not supported on this device");
             }
         } else if (Build.VERSION.SDK_INT < 9){
             throw new UnsupportedOperationException("jME3 requires Android 2.3 or later");
@@ -126,7 +137,8 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
 
         // setEGLContextClientVersion must be set before calling setRenderer
         // this means it cannot be set in AndroidConfigChooser (too late)
-        view.setEGLContextClientVersion(2);
+        // use proper openGL ES version
+        view.setEGLContextClientVersion(info.reqGlEsVersion>>16);
 
         view.setFocusableInTouchMode(true);
         view.setFocusable(true);
@@ -201,7 +213,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
             gl = new GLDebugES((GL) gl, (GLExt) gl, (GLFbo) gl);
         }
         if (settings.getBoolean("GraphicsTrace")) {
-            gl = GLTracer.createGlesTracer(gl, GL.class, GLFbo.class, GLExt.class);
+            gl = GLTracer.createGlesTracer(gl, GL.class, GLES_30.class, GLFbo.class, GLExt.class);
         }
         renderer = new GLRenderer((GL)gl, (GLExt)gl, (GLFbo)gl);
         renderer.initialize();

+ 114 - 0
jme3-android/src/main/java/com/jme3/util/AndroidBufferAllocator.java

@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2019 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.util;
+
+import java.lang.reflect.Field;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Jesus Oliver
+ */
+public class AndroidBufferAllocator implements BufferAllocator {
+
+    // We make use of the ReflectionAllocator to remove the inner buffer
+    private static final ReflectionAllocator reflectionAllocator = new ReflectionAllocator();
+
+    private static final String[] wrapperClassNames = {
+            "java.nio.ByteBufferAsFloatBuffer",
+            "java.nio.ByteBufferAsIntBuffer",
+            "java.nio.ByteBufferAsDoubleBuffer",
+            "java.nio.ByteBufferAsShortBuffer",
+            "java.nio.ByteBufferAsLongBuffer",
+            "java.nio.ByteBufferAsCharBuffer",
+    };
+    private static final String[] possibleBufferFieldNames = {"bb", "byteBuffer"};
+
+    // Keep track of ByteBuffer field by the wrapper class
+    private static final Map<Class, Field> fieldIndex = new HashMap<>();
+
+    static {
+        for (String className : wrapperClassNames) {
+            try {
+                Class clazz = Class.forName(className);
+
+                // loop for all possible field names in android
+                for (String fieldName : possibleBufferFieldNames) {
+                    try {
+                        Field field = clazz.getDeclaredField(fieldName);
+                        field.setAccessible(true);
+                        fieldIndex.put(clazz, field);
+                        break;
+                    } catch (NoSuchFieldException e) {
+                    }
+                }
+            } catch (ClassNotFoundException ex) {
+            }
+        }
+    }
+
+    @Override
+    /**
+     * This function search the inner direct buffer of the android specific wrapped buffer classes
+     * and destroys it using the reflection allocator method.
+     *
+     * @param toBeDestroyed The direct buffer that will be "cleaned".
+     *
+     */
+    public void destroyDirectBuffer(Buffer toBeDestroyed) {
+        // If it is a wrapped buffer, get it's inner direct buffer field and destroy it
+        Field field = fieldIndex.get(toBeDestroyed.getClass());
+        if (field != null) {
+            try {
+                ByteBuffer innerBuffer = (ByteBuffer) field.get(toBeDestroyed);
+                if (innerBuffer != null) {
+                    // Destroy it using the reflection method
+                    reflectionAllocator.destroyDirectBuffer(innerBuffer);
+                }
+            } catch (IllegalAccessException ex) {
+            }
+
+        } else {
+            // It is not a wrapped buffer, use default reflection allocator to remove it instead.
+            reflectionAllocator.destroyDirectBuffer(toBeDestroyed);
+        }
+    }
+
+    @Override
+    public ByteBuffer allocate(int size) {
+        return ByteBuffer.allocateDirect(size);
+    }
+}
+

+ 1 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java

@@ -151,7 +151,7 @@ public abstract class ConstraintDefinition {
      * @param targetTransform
      *            the target transform used by some of the constraints
      * @param influence
-     *            the influence of the constraint (from range <0; 1>)
+     *            the influence of the constraint from range [0; 1]
      */
     public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
 

+ 1 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java

@@ -313,7 +313,7 @@ public class TextureHelper extends AbstractBlenderHelper {
      * This method returns the proper pixel position on the image.
      * 
      * @param pos
-     *            the relative position (value of range <0, 1> (both inclusive))
+     *            the relative position (value of range [0, 1] (both inclusive))
      * @param size
      *            the size of the line the pixel lies on (width, height or
      *            depth)

+ 2 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java

@@ -118,7 +118,7 @@ public class TexturePixel implements Cloneable {
 
     /**
      * This method sets the alpha value (converts it to float number from range
-     * <0, 1>).
+     * [0, 1]).
      * 
      * @param alpha
      *            the alpha value
@@ -129,7 +129,7 @@ public class TexturePixel implements Cloneable {
 
     /**
      * This method sets the alpha value (converts it to float number from range
-     * <0, 1>).
+     * [0, 1]).
      * 
      * @param alpha
      *            the alpha value

+ 9 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java

@@ -142,8 +142,15 @@ public class TextureBlenderAWT extends AbstractTextureBlender {
             }
             dataArray.add(newData);
         }
-
-        Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, ColorSpace.Linear) : new Image(Format.RGBA8, width, height, dataArray.get(0), ColorSpace.Linear);
+        
+        ColorSpace colorSpace;
+        if (baseImage != null) {
+            colorSpace = baseImage.getColorSpace() != null ? baseImage.getColorSpace() : ColorSpace.Linear;
+        } else {
+            colorSpace = image.getColorSpace();
+        }
+        
+        Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, colorSpace) : new Image(Format.RGBA8, width, height, dataArray.get(0), colorSpace);
         if (image.getMipMapSizes() != null) {
             result.setMipMapSizes(image.getMipMapSizes().clone());
         }

+ 25 - 59
jme3-bullet-native-android/build.gradle

@@ -1,4 +1,4 @@
-String jmeBulletNativeProjectPath = '../jme3-bullet-native'
+String jmeBulletNativeProjectPath = project(":jme3-bullet-native").projectDir
 
 String localUnzipPath = jmeBulletNativeProjectPath
 String localZipFile = jmeBulletNativeProjectPath + File.separator + bulletZipFile
@@ -14,7 +14,8 @@ String jniPath = ndkWorkingPath + '/jni'
 String ndkOutputPath = ndkWorkingPath + '/libs'
 
 //Pre-compiled libs directory
-String bulletPreCompiledLibsDir = 'libs'
+def rootPath = rootProject.projectDir.absolutePath
+String bulletPreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator +  'android' + File.separator + 'bullet'
 
 if (!hasProperty('mainClass')) {
     ext.mainClass = ''
@@ -24,7 +25,7 @@ dependencies {
     compile project(':jme3-bullet')
 }
 
-// Java source sets for IDE acces and source jar bundling / mavenization
+// Java source sets for IDE access and source jar bundling / mavenization
 sourceSets {
     main {
         java {
@@ -42,70 +43,41 @@ task downloadBullet(type: MyDownload) {
 
 // Unzip bullet if not available
 task unzipBullet(type: Copy) {
-    def zipFile = file(localZipFile)
-    def outputDir = file(localUnzipPath)
-//    println "unzipBullet zipFile = " + zipFile.absolutePath
-//    println "unzipBullet outputDir = " + outputDir.absolutePath
-
-    from zipTree(zipFile)
-    into outputDir
+    from zipTree(localZipFile)
+    into file(localUnzipPath)
 }
 
 unzipBullet.dependsOn {
-    def zipFile = file(localZipFile)
-//    println "zipFile path: " + zipFile.absolutePath
-//    println "zipFile exists: " + zipFile.exists()
-
-    if (!zipFile.exists()) {
+    if (!file(localZipFile).exists()) {
         downloadBullet
     }
 }
 
 // Copy Bullet files to jni directory
 task copyBullet(type: Copy) {
-    def sourceDir = file(bulletSrcPath)
-    def outputDir = new File(jniPath)
-//    println "copyBullet sourceDir = " + sourceDir
-//    println "copyBullet outputDir = " + outputDir
-
-    from sourceDir
-    into outputDir
+    from file(bulletSrcPath)
+    into file(jniPath)
 }
 
 copyBullet.dependsOn {
-    def bulletUnzipDir = file(localZipFolder)
-//    println "bulletUnzipDir: " + bulletUnzipDir.absolutePath
-//    println "bulletUnzipDir exists: " + bulletUnzipDir.exists()
-//    println "bulletUnzipDir isDirectory: " + bulletUnzipDir.isDirectory()
-    if (!bulletUnzipDir.isDirectory()) {
+    if (!file(localZipFolder).isDirectory()) {
         unzipBullet
     }
 }
 
 // Copy jME cpp native files to jni directory
 task copyJmeCpp(type: Copy) {
-    def sourceDir = new File(jmeCppPath)
-    def outputDir = new File(jniPath)
-//    println "copyJmeCpp sourceDir = " + sourceDir
-//    println "copyJmeCpp outputDir = " + outputDir
-
-    from sourceDir
-    into outputDir
+    from file(jmeCppPath)
+    into file(jniPath)
 }
 
 // Copy jME android native files to jni directory
 task copyJmeAndroid(type: Copy) {
-    def sourceDir = new File(jmeAndroidPath)
-    def outputDir = new File(jniPath)
-//    println "copyJmeAndroid sourceDir = " + sourceDir
-//    println "copyJmeAndroid outputDir = " + outputDir
-
-    from sourceDir
-    into outputDir
+    from file(jmeAndroidPath)
+    into file(jniPath)
 }
 
-//dependsOn ':jme3-bullet:generateNativeHeaders'
-task buildBulletNativeLib(type: Exec, dependsOn: [copyJmeAndroid, ':jme3-bullet:generateNativeHeaders', copyJmeCpp, copyBullet]) {
+task buildBulletNativeLib(type: Exec, dependsOn: [copyJmeAndroid, ':jme3-bullet:compileJava', copyJmeCpp, copyBullet]) {
 //    args 'TARGET_PLATFORM=android-9'
 //    println "buildBulletNativeLib ndkWorkingPath: " + ndkWorkingPath
 //    println "buildBulletNativeLib rootProject.ndkCommandPath: " + rootProject.ndkCommandPath
@@ -114,26 +86,20 @@ task buildBulletNativeLib(type: Exec, dependsOn: [copyJmeAndroid, ':jme3-bullet:
     args "-j" + Runtime.runtime.availableProcessors()
 }
 
-//task updatePreCompiledBulletLibs(type: Copy, dependsOn: generateNativeHeaders) {
-task updatePreCompiledBulletLibs(type: Copy, dependsOn: buildBulletNativeLib) {
-    def sourceDir = new File(ndkOutputPath)
-    def outputDir = new File(bulletPreCompiledLibsDir)
-//    println "updatePreCompiledBulletLibs sourceDir: " + sourceDir
-//    println "updatePreCompiledBulletLibs outputDir: " + outputDir
+/* The following two tasks: We store a prebuilt version in the repository, so nobody has to build
+ * natives in order to build the engine. When building these natives however, the prebuilt libraries
+ * can be updated (which is what the CI does). That's what the following two tasks do
+ */
 
-    from sourceDir
-    into outputDir
+task updatePreCompiledBulletLibs(type: Copy, dependsOn: buildBulletNativeLib) {
+    from file(ndkOutputPath)
+    into file(bulletPreCompiledLibsDir)
 }
 
 // Copy pre-compiled libs to build directory (when not building new libs)
 task copyPreCompiledBulletLibs(type: Copy) {
-    def sourceDir = new File(bulletPreCompiledLibsDir)
-    def outputDir = new File(ndkOutputPath)
-//    println "copyPreCompiledBulletLibs sourceDir: " + sourceDir
-//    println "copyPreCompiledBulletLibs outputDir: " + outputDir
-
-    from sourceDir
-    into outputDir
+    from file(bulletPreCompiledLibsDir)
+    into file(ndkOutputPath)
 }
 
 // ndkExists is a boolean from the build.gradle in the root project
@@ -149,7 +115,7 @@ if (ndkExists && buildNativeProjects == "true") {
 jar.into("lib") { from ndkOutputPath }
 
 
-// Helper class to wrap ant dowload task
+// Helper class to wrap ant download task
 class MyDownload extends DefaultTask {
     @Input
     String sourceUrl

BIN
jme3-bullet-native-android/libs/arm64-v8a/libbulletjme.so


BIN
jme3-bullet-native-android/libs/armeabi-v7a/libbulletjme.so


BIN
jme3-bullet-native-android/libs/armeabi/libbulletjme.so


BIN
jme3-bullet-native-android/libs/mips/libbulletjme.so


BIN
jme3-bullet-native-android/libs/mips64/libbulletjme.so


BIN
jme3-bullet-native-android/libs/x86/libbulletjme.so


BIN
jme3-bullet-native-android/libs/x86_64/libbulletjme.so


+ 1 - 0
jme3-bullet-native-android/src/native/android/Android.mk

@@ -71,6 +71,7 @@ FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
 FILE_LIST += $(wildcard $(LOCAL_PATH)/**/*.cpp)
 FILE_LIST += $(wildcard $(LOCAL_PATH)/**/**/*.cpp)
 FILE_LIST := $(filter-out $(wildcard $(LOCAL_PATH)/Bullet3OpenCL/**/*.cpp), $(FILE_LIST))
+FILE_LIST := $(filter-out $(wildcard $(LOCAL_PATH)/*All.cpp), $(FILE_LIST))
 LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
 
 include $(BUILD_SHARED_LIBRARY)

+ 2 - 2
jme3-bullet-native-android/src/native/android/Application.mk

@@ -1,7 +1,7 @@
 APP_OPTIM := release
 APP_ABI := all
-APP_STL := stlport_static
-# gnustl_static or stlport_static
+# Used to be stlport_static, but that has been removed.
+APP_STL := c++_static
 APP_MODULES      := bulletjme
 APP_CFLAGS += -funroll-loops -Ofast
 

+ 119 - 83
jme3-bullet-native/build.gradle

@@ -2,6 +2,8 @@ apply plugin: 'cpp'
 
 import java.nio.file.Paths
 
+def rootPath = rootProject.projectDir.absolutePath
+
 String bulletSrcPath = bulletFolder + '/src'
 
 if (!hasProperty('mainClass')) {
@@ -29,12 +31,33 @@ task cleanZipFile(type: Delete) {
 model {
     components {
         bulletjme(NativeLibrarySpec) {
-            targetPlatform 'Windows64'
-            targetPlatform 'Windows32'
-            targetPlatform 'Mac64'
-            targetPlatform 'Mac32'
-            targetPlatform 'Linux64'
-            targetPlatform 'Linux32'
+
+
+            String[] targets=[
+                "Windows64",
+                "Windows32",
+                "Mac64",
+                "Linux64",
+                "Linux32",
+                "LinuxArm",
+                "LinuxArmHF",
+                "LinuxArm64"    
+            ];
+          
+            String[] filter=gradle.rootProject.ext.usePrebuildNatives==true?null:buildForPlatforms.split(",");
+            if(filter==null)println("No filter set. build for all");
+            for(String target:targets){
+                if(filter==null){
+                    targetPlatform(target);
+                }else{
+                    for(String f:filter){
+                        if(f.equals(target)){
+                            targetPlatform(target);
+                            break;
+                        }
+                    }
+                }
+            }
 
             sources {
                 cpp {
@@ -47,6 +70,7 @@ model {
                         exclude 'Bullet3OpenCL/**'
                         exclude 'Bullet3Serialize/**'
                         include '**/*.cpp'
+                        exclude '**/*All.cpp'
                     }
                     exportedHeaders {
                         srcDir 'src/native/cpp'
@@ -63,9 +87,41 @@ model {
         }
     }
 
+     
+    toolChains {
+        visualCpp(VisualCpp)
+        gcc(Gcc)
+        clang(Clang)
+        gccArm(Gcc) {
+            // Fun Fact: Gradle uses gcc as linker frontend, so we don't specify ld directly here
+            target("LinuxArm"){
+                path "/usr/bin"
+                cCompiler.executable = "arm-linux-gnueabi-gcc-8"
+                cppCompiler.executable = "arm-linux-gnueabi-g++-8"
+                linker.executable = "arm-linux-gnueabi-gcc-8"
+                assembler.executable = "arm-linux-gnueabi-as"
+            }
+
+            target("LinuxArmHF"){
+                path "/usr/bin"
+                cCompiler.executable = "arm-linux-gnueabihf-gcc-8"
+                cppCompiler.executable = "arm-linux-gnueabihf-g++-8"
+                linker.executable = "arm-linux-gnueabihf-gcc-8"
+                assembler.executable = "arm-linux-gnueabihf-as"
+            }
+
+            target("LinuxArm64"){
+                path "/usr/bin"
+                cCompiler.executable = "aarch64-linux-gnu-gcc-8"
+                cppCompiler.executable = "aarch64-linux-gnu-g++-8"
+                linker.executable = "aarch64-linux-gnu-gcc-8"
+                assembler.executable = "aarch64-linux-gnu-as"
+            }
+        }  
+    }
+
     binaries {
         withType(SharedLibraryBinarySpec) {
-            def projectPath = project.projectDir.absolutePath
             def javaHome = org.gradle.internal.jvm.Jvm.current().javaHome
             def os = targetPlatform.operatingSystem.name
             def arch = targetPlatform.architecture.name
@@ -75,88 +131,57 @@ model {
             arch = arch.replaceAll('-', '_')
 
             // For all binaries that can't be built on the current system
-            if (buildNativeProjects != "true") {
-                buildable = false
-            }
+            if (buildNativeProjects != "true")  buildable = false
 
-            if (!buildable) {
-                if (sharedLibraryFile.exists()) {
-                    // Add binary to jar file if the binary exists in the build folder already,
-                    // e.g. when the build of jme3-bullet-native has been run on a virtual box
-                    // and the project hasn't been cleaned yet.
-                    jar.into("native/${os}/${arch}") {
-                        from sharedLibraryFile
-                    }
-                } else {
-                    // Get from libs folder if no fresh build is available in the build folder and add to jar file
-                    def precompiledFile = Paths.get(projectPath, 'libs', 'native', os, arch, fileName).toFile()
-                    if (precompiledFile.exists()) {
-                        jar.into("native/${os}/${arch}") {
-                            from precompiledFile
-                        }
-                    }
+            if (buildable) {            
+                cppCompiler.define('BT_NO_PROFILE')
+                if (toolChain in VisualCpp) {
+                    cppCompiler.args "/I$javaHome\\include"
+                } else{
+                    cppCompiler.args '-I', "$javaHome/include"
                 }
-                return
-            }
 
-            cppCompiler.define('BT_NO_PROFILE')
-            if (toolChain in VisualCpp) {
-                cppCompiler.args "/I$javaHome\\include"
-            } else{
-                cppCompiler.args '-I', "$javaHome/include"
-            }
-
-            if (os == "osx") {
-                cppCompiler.args '-I', "$javaHome/include/darwin"
-                cppCompiler.args "-Ofast"
-                cppCompiler.args "-U_FORTIFY_SOURCE"
-            } else if (os == "linux") {
-                cppCompiler.args "-fvisibility=hidden"
-                cppCompiler.args '-I', "$javaHome/include/linux"
-                cppCompiler.args "-fPIC"
-                cppCompiler.args "-Ofast"
-                cppCompiler.args "-U_FORTIFY_SOURCE"
-                cppCompiler.args "-fpermissive"
-                linker.args "-fvisibility=hidden"
-            } else if (os == "windows") {
-                if (toolChain in Gcc) {
-                    if (toolChain.name.startsWith('mingw')) {
-                        cppCompiler.args '-I', "$projectDir/src/native/cpp/fake_win32"
-                    } else {
-                        cppCompiler.args '-I', "$javaHome/include/win32"
-                    }
-                    cppCompiler.args "-fpermissive"
-                    cppCompiler.args "-static"
-                    cppCompiler.args "-Ofast"
+                if (os == "osx") {
+                    cppCompiler.args '-I', "$javaHome/include/darwin"
+                    cppCompiler.args "-O3"
+                    cppCompiler.args "-U_FORTIFY_SOURCE"
+                } else if (os == "linux") {
+                    cppCompiler.args "-fvisibility=hidden"
+                    cppCompiler.args '-I', "$javaHome/include/linux"
+                    cppCompiler.args "-fPIC"
+                    cppCompiler.args "-O3"
                     cppCompiler.args "-U_FORTIFY_SOURCE"
-                    linker.args "-static"
-                    linker.args "-Wl,--exclude-all-symbols"
+                    cppCompiler.args "-fpermissive"
+                    linker.args "-fvisibility=hidden"
+                } else if (os == "windows") {
+                    if (toolChain in Gcc) {
+                        if (toolChain.name.startsWith('mingw'))  cppCompiler.args '-I', "$projectDir/src/native/cpp/fake_win32"
+                        else  cppCompiler.args '-I', "$javaHome/include/win32"                        
+                        cppCompiler.args "-fpermissive"
+                        cppCompiler.args "-static"
+                        cppCompiler.args "-O3"
+                        cppCompiler.args "-U_FORTIFY_SOURCE"
+                        linker.args "-static"
+                        linker.args "-Wl,--exclude-all-symbols"
+                    } else if (toolChain in VisualCpp) {
+                        cppCompiler.args "/I$javaHome\\include\\win32"
+                    }
+                    cppCompiler.define('WIN32')
                 }
-                else if (toolChain in VisualCpp) {
-                    cppCompiler.args "/I$javaHome\\include\\win32"
+                tasks.all { 
+                    dependsOn unzipBulletIfNeeded
+                    dependsOn ':jme3-bullet:compileJava'
                 }
-                cppCompiler.define('WIN32')
-            }
 
-            tasks.all { 
-                dependsOn unzipBulletIfNeeded
-                dependsOn ':jme3-bullet:generateNativeHeaders'
-            }
+                task "copyBinaryToLibs${targetPlatform.name}"(type: Copy, dependsOn: tasks) {
+                    from sharedLibraryFile
+                    into "${rootPath}/build/native/bullet/native/${os}/${arch}"
+                } 
 
-            // Add output to jar file
-            jar.into("native/${os}/${arch}") {
-                from sharedLibraryFile
-            }
+                // Add depend on copy
+                jar.dependsOn("copyBinaryToLibs${targetPlatform.name}")
 
-            // Add depend on build
-            jar.dependsOn tasks
-            // Add output to libs folder
-            task "copyBinaryToLibs${targetPlatform.name}"(type: Copy, dependsOn: tasks) {
-                from sharedLibraryFile
-                into "libs/native/${os}/${arch}"
             }
-            // Add depend on copy
-            jar.dependsOn("copyBinaryToLibs${targetPlatform.name}")
         }
         withType(StaticLibraryBinarySpec) {
             buildable = false
@@ -172,10 +197,6 @@ model {
             architecture "x86_64"
             operatingSystem "windows"
         }
-        Mac32 {
-            architecture "x86"
-            operatingSystem "osx"
-        }
         Mac64 {
             architecture "x86_64"
             operatingSystem "osx"
@@ -188,6 +209,18 @@ model {
             architecture "x86_64"
             operatingSystem "linux"
         }
+        LinuxArm {
+            architecture "arm"
+            operatingSystem "linux"
+        }
+        LinuxArmHF {
+            architecture "armhf"
+            operatingSystem "linux"        
+        }
+        LinuxArm64 {
+            architecture "aarch64"
+            operatingSystem "linux"
+        }
     }
 }
 
@@ -197,6 +230,9 @@ sourceSets {
         java {
             srcDir 'src/native/cpp'
         }
+        resources {
+            srcDir file(Paths.get(rootPath, 'build', 'native', 'bullet'))
+        }
     }
 }
 

BIN
jme3-bullet-native/libs/native/linux/x86/libbulletjme.so


BIN
jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so


BIN
jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib


BIN
jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib


BIN
jme3-bullet-native/libs/native/windows/x86/bulletjme.dll


BIN
jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll


+ 18 - 0
jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_PhysicsCollisionObject.cpp

@@ -178,6 +178,24 @@ extern "C" {
         collisionObject->setCollisionFlags(desiredFlags);
     }
 
+    /*
+     * Class:     com_jme3_bullet_collision_PhysicsCollisionObject
+     * Method:    getDeactivationTime
+     * Signature: (J)F
+     */
+    JNIEXPORT jfloat JNICALL Java_com_jme3_bullet_collision_PhysicsCollisionObject_getDeactivationTime
+    (JNIEnv *env, jobject object, jlong pcoId) {
+        btCollisionObject *pCollisionObject
+                = reinterpret_cast<btCollisionObject *> (pcoId);
+        if (pCollisionObject == NULL) {
+            jclass newExc = env->FindClass("java/lang/NullPointerException");
+            env->ThrowNew(newExc, "The native object does not exist.");
+            return 0;
+        }
+
+        jfloat result = pCollisionObject->getDeactivationTime();
+        return result;
+    }    
 
 #ifdef __cplusplus
 }

+ 19 - 0
jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_CollisionShape.cpp

@@ -105,6 +105,25 @@ extern "C" {
         }
         delete(shape);
     }
+
+    /*
+     * Class:     com_jme3_bullet_collision_shapes_CollisionShape
+     * Method:    isNonMoving
+     * Signature: (J)Z
+     */
+    JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_collision_shapes_CollisionShape_isNonMoving
+    (JNIEnv *env, jobject object, jlong shapeId) {
+        btCollisionShape *pShape
+                = reinterpret_cast<btCollisionShape *> (shapeId);
+        if (pShape == NULL) {
+            jclass newExc = env->FindClass("java/lang/NullPointerException");
+            env->ThrowNew(newExc, "The native object does not exist.");
+            return false;
+        }
+
+        return pShape->isNonMoving();
+    }
+
 #ifdef __cplusplus
 }
 #endif

+ 13 - 1
jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp

@@ -55,6 +55,18 @@ extern "C" {
         return reinterpret_cast<jlong>(shape);
     }
 
+    /*
+     * Class:     com_jme3_bullet_collision_shapes_GImpactCollisionShape
+     * Method:    recalcAabb
+     * Signature: (J)V
+     */
+    JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_GImpactCollisionShape_recalcAabb
+    (JNIEnv *env, jobject object, jlong shapeId) {
+        btGImpactMeshShape *pShape
+                = reinterpret_cast<btGImpactMeshShape *> (shapeId);
+        pShape->updateBound();
+    }
+
     /*
      * Class:     com_jme3_bullet_collision_shapes_GImpactCollisionShape
      * Method:    finalizeNative
@@ -65,7 +77,7 @@ extern "C" {
         btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*> (meshId);
         delete(array);
     }
-    
+
 #ifdef __cplusplus
 }
 #endif

+ 24 - 0
jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_SixDofJoint.cpp

@@ -165,6 +165,30 @@ extern "C" {
         btGeneric6DofConstraint* joint = new btGeneric6DofConstraint(*bodyA, *bodyB, transA, transB, useLinearReferenceFrameA);
         return reinterpret_cast<jlong>(joint);
     }
+
+    /*
+     * Class:     com_jme3_bullet_joints_SixDofJoint
+     * Method:    getAngles
+     * Signature: (JLcom/jme3/math/Vector3f;)V
+     */
+    JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_SixDofJoint_getAngles
+    (JNIEnv *env, jobject object, jlong jointId, jobject storeVector) {
+        btGeneric6DofConstraint *pJoint
+                = reinterpret_cast<btGeneric6DofConstraint *> (jointId);
+        if (pJoint == NULL) {
+            jclass newExc = env->FindClass("java/lang/NullPointerException");
+            env->ThrowNew(newExc, "The native object does not exist.");
+            return;
+        }
+
+        pJoint->calculateTransforms();
+        btScalar x = pJoint->getAngle(0);
+        btScalar y = pJoint->getAngle(1);
+        btScalar z = pJoint->getAngle(2);
+        const btVector3& angles = btVector3(x, y, z);
+        jmeBulletUtil::convert(env, &angles, storeVector);
+    }
+
 #ifdef __cplusplus
 }
 #endif

+ 38 - 1
jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2019 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -232,6 +232,43 @@ extern "C" {
         motor->m_restitution = value;
     }
 
+    /*
+     * Class:     com_jme3_bullet_joints_motors_TranslationalLimitMotor
+     * Method:    setEnabled
+     * Signature: (JIZ)V
+     */
+    JNIEXPORT void JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_setEnabled
+    (JNIEnv *env, jobject object, jlong motorId, jint axisIndex, jboolean newSetting) {
+        btTranslationalLimitMotor *pMotor
+                = reinterpret_cast<btTranslationalLimitMotor *> (motorId);
+        if (pMotor == NULL) {
+            jclass newExc = env->FindClass("java/lang/NullPointerException");
+            env->ThrowNew(newExc, "The native object does not exist.");
+            return;
+        }
+
+        pMotor->m_enableMotor[axisIndex] = (bool)newSetting;
+    }
+
+    /*
+     * Class:     com_jme3_bullet_joints_motors_TranslationalLimitMotor
+     * Method:    isEnabled
+     * Signature: (JI)Z
+     */
+    JNIEXPORT jboolean JNICALL Java_com_jme3_bullet_joints_motors_TranslationalLimitMotor_isEnabled
+    (JNIEnv *env, jobject object, jlong motorId, jint axisIndex) {
+        btTranslationalLimitMotor *pMotor
+                = reinterpret_cast<btTranslationalLimitMotor *> (motorId);
+        if (pMotor == NULL) {
+            jclass newExc = env->FindClass("java/lang/NullPointerException");
+            env->ThrowNew(newExc, "The native object does not exist.");
+            return 0;
+        }
+
+        bool result = pMotor->m_enableMotor[axisIndex];
+        return (jboolean) result;
+    }
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 3
jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp

@@ -52,9 +52,7 @@ extern "C" {
         btMotionState* motionState = reinterpret_cast<btMotionState*>(motionstatId);
         btCollisionShape* shape = reinterpret_cast<btCollisionShape*>(shapeId);
         btVector3 localInertia = btVector3();
-	if(mass > 0){
-	        shape->calculateLocalInertia(mass, localInertia);
-	}
+        shape->calculateLocalInertia(mass, localInertia);
         btRigidBody* body = new btRigidBody(mass, motionState, shape, localInertia);
         body->setUserPointer(NULL);
         return reinterpret_cast<jlong>(body);

+ 6 - 18
jme3-bullet/build.gradle

@@ -1,8 +1,11 @@
+apply plugin: 'java'
+
 if (!hasProperty('mainClass')) {
     ext.mainClass = ''
 }
 
 String classBuildDir = "${buildDir}" + File.separator + 'classes'
+def nativeIncludes = new File(project(":jme3-bullet-native").projectDir, "src/native/cpp")
 
 sourceSets {
     main {
@@ -18,22 +21,7 @@ dependencies {
     compile project(':jme3-terrain')
 }
 
-task generateNativeHeaders(type: Exec, dependsOn: classes) {
-    def files0 = fileTree("src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files1 = fileTree("src/common/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files2 = fileTree("../jme3-core/src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files3 = fileTree("../jme3-core/src/plugins/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files4 = fileTree("../jme3-core/src/tools/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def files5 = fileTree("../jme3-terrain/src/main/java/").filter { it.isFile() && it.getName().endsWith(".java") }.files
-    def classpath = sourceSets.main.runtimeClasspath.asPath
-    def nativeIncludes = new File(project(":jme3-bullet-native").projectDir, "src/native/cpp")
-	def filesList = "\"" + files0.join("\"\n\"") + "\"\n\"" + files1.join("\"\n\"") + "\"\n\"" + files2.join("\"\n\"") + "\"\n\"" + files3.join("\"\n\"") + "\"\n\"" + files4.join("\"\n\"") + "\"\n\"" + files5.join("\"\n\"") + "\""
-	new File("$projectDir/java_classes.jtxt").text = filesList.replaceAll(java.util.regex.Pattern.quote("\\"), java.util.regex.Matcher.quoteReplacement("/"))
-    executable org.gradle.internal.jvm.Jvm.current().getExecutable('javac')
-    args "-h", nativeIncludes
-    args "@$projectDir/java_classes.jtxt"
-    args '-d', classBuildDir
-    args "-encoding", "UTF-8"
+compileJava {
+    // The Android-Native Project requires the jni headers to be generated, so we do that here
+    options.compilerArgs += ["-h", nativeIncludes]
 }
-
-assemble.dependsOn(generateNativeHeaders)

+ 23 - 46
jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2018 jMonkeyEngine
+ * Copyright (c) 2009-2019 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
 package com.jme3.bullet;
 
 import com.jme3.app.Application;
-import com.jme3.app.state.AppState;
+import com.jme3.app.state.AbstractAppState;
 import com.jme3.app.state.AppStateManager;
 import com.jme3.bullet.PhysicsSpace.BroadphaseType;
 import com.jme3.bullet.debug.BulletDebugAppState;
@@ -49,13 +49,15 @@ import java.util.logging.Logger;
  *
  * @author normenhansen
  */
-public class BulletAppState implements AppState, PhysicsTickListener {
+public class BulletAppState
+        extends AbstractAppState
+        implements PhysicsTickListener {
 
     /**
      * true if-and-only-if the physics simulation is running (started but not
      * yet stopped)
      */
-    protected boolean initialized = false;
+    protected volatile boolean isRunning = false;
     protected Application app;
     /**
      * manager that manages this state, set during attach
@@ -232,7 +234,7 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      * sooner, invoke this method.
      */
     public void startPhysics() {
-        if (initialized) {
+        if (isRunning) {
             return;
         }
 
@@ -252,14 +254,14 @@ public class BulletAppState implements AppState, PhysicsTickListener {
                 throw new IllegalStateException(threadingType.toString());
         }
 
-        initialized = true;
+        isRunning = true;
     }
 
     /**
      * Stop physics after this state is detached.
      */
     public void stopPhysics() {
-        if(!initialized){
+        if (!isRunning) {
             return;
         }
         if (executor != null) {
@@ -268,7 +270,7 @@ public class BulletAppState implements AppState, PhysicsTickListener {
         }
         pSpace.removeTickListener(this);
         pSpace.destroy();
-        initialized = false;
+        isRunning = false;
     }
 
     /**
@@ -278,40 +280,14 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      * @param stateManager the manager for this state (not null)
      * @param app the application which owns this state (not null)
      */
+    @Override
     public void initialize(AppStateManager stateManager, Application app) {
+        super.initialize(stateManager, app);
         this.app = app;
         this.stateManager = stateManager;
         startPhysics();
     }
 
-    /**
-     * Test whether the physics simulation is running (started but not yet
-     * stopped).
-     *
-     * @return true if running, otherwise false
-     */
-    public boolean isInitialized() {
-        return initialized;
-    }
-
-    /**
-     * Enable or disable this state.
-     *
-     * @param enabled true &rarr; enable, false &rarr; disable
-     */
-    public void setEnabled(boolean enabled) {
-        this.active = enabled;
-    }
-
-    /**
-     * Test whether this state is enabled.
-     *
-     * @return true if enabled, otherwise false
-     */
-    public boolean isEnabled() {
-        return active;
-    }
-
     /**
      * Alter whether debug visualization is enabled.
      *
@@ -337,8 +313,10 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      *
      * @param stateManager (not null)
      */
+    @Override
     public void stateAttached(AppStateManager stateManager) {
-        if (!initialized) {
+        super.stateAttached(stateManager);
+        if (!isRunning) {
             startPhysics();
         }
         if (threadingType == ThreadingType.PARALLEL) {
@@ -350,15 +328,6 @@ public class BulletAppState implements AppState, PhysicsTickListener {
         }
     }
 
-    /**
-     * Transition this state from running to terminating. Should be invoked only
-     * by a subclass or by the AppStateManager.
-     *
-     * @param stateManager (not null)
-     */
-    public void stateDetached(AppStateManager stateManager) {
-    }
-
     /**
      * Update this state prior to rendering. Should be invoked only by a
      * subclass or by the AppStateManager. Invoked once per frame, provided the
@@ -366,7 +335,9 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      *
      * @param tpf the time interval between frames (in seconds, &ge;0)
      */
+    @Override
     public void update(float tpf) {
+        super.update(tpf);
         if (debugEnabled && debugAppState == null && pSpace != null) {
             debugAppState = new BulletDebugAppState(pSpace);
             stateManager.attach(debugAppState);
@@ -388,7 +359,9 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      *
      * @param rm the render manager (not null)
      */
+    @Override
     public void render(RenderManager rm) {
+        super.render(rm);
         if (!active) {
             return;
         }
@@ -405,7 +378,9 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      * invoked only by a subclass or by the AppStateManager. Invoked once per
      * frame, provided the state is attached and enabled.
      */
+    @Override
     public void postRender() {
+        super.postRender();
         if (physicsFuture != null) {
             try {
                 physicsFuture.get();
@@ -424,12 +399,14 @@ public class BulletAppState implements AppState, PhysicsTickListener {
      * {@link #initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application)}
      * is invoked.
      */
+    @Override
     public void cleanup() {
         if (debugAppState != null) {
             stateManager.detach(debugAppState);
             debugAppState = null;
         }
         stopPhysics();
+        super.cleanup();
     }
 
     /**

+ 12 - 0
jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java

@@ -158,6 +158,18 @@ public abstract class PhysicsCollisionObject implements Savable {
         return collisionShape;
     }
 
+    /**
+     * Read the deactivation time.
+     *
+     * @return the time (in seconds)
+     */
+    public float getDeactivationTime() {
+        float time = getDeactivationTime(objectId);
+        return time;
+    }
+
+    native private float getDeactivationTime(long objectId);
+
     /**
      * Read the collision group for this physics object.
      *

+ 17 - 4
jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java

@@ -87,7 +87,7 @@ public abstract class CollisionShape implements Savable {
 ////            objectId.calculateLocalInertia(mass, vector);
 ////        }
 //    }
-//    
+//
 //    private native void calculateLocalInertia(long objectId, long shapeId, float mass);
 
     /**
@@ -129,6 +129,19 @@ public abstract class CollisionShape implements Savable {
         return scale;
     }
 
+    /**
+     * Test whether this shape can be applied to a dynamic rigid body. The only
+     * non-moving shapes are the heightfield, mesh, and plane shapes.
+     *
+     * @return true if non-moving, false otherwise
+     */
+    public boolean isNonMoving() {
+        boolean result = isNonMoving(objectId);
+        return result;
+    }
+
+    native private boolean isNonMoving(long objectId);
+
     /**
      * Read the collision margin for this shape.
      *
@@ -137,7 +150,7 @@ public abstract class CollisionShape implements Savable {
     public float getMargin() {
         return getMargin(objectId);
     }
-    
+
     private native float getMargin(long objectId);
 
     /**
@@ -177,9 +190,9 @@ public abstract class CollisionShape implements Savable {
         setMargin(objectId, margin);
         this.margin = margin;
     }
-    
+
     private native void setLocalScaling(long obectId, Vector3f scale);
-    
+
     private native void setMargin(long objectId, float margin);
 
     /**

+ 18 - 0
jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java

@@ -36,6 +36,7 @@ import com.jme3.export.InputCapsule;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.mesh.IndexBuffer;
@@ -153,6 +154,23 @@ public class GImpactCollisionShape extends CollisionShape {
         vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0]));
         createShape();
     }
+    
+    /**
+     * Alter the scaling factors of this shape.
+     * <p>
+     * Note that if the shape is shared (between collision objects and/or
+     * compound shapes) changes can have unintended consequences.
+     *
+     * @param scale the desired scaling factor for each local axis (not null, no
+     * negative component, unaffected, default=(1,1,1))
+     */
+    @Override
+    public void setScale(Vector3f scale) {
+        super.setScale(scale);
+        recalcAabb(objectId);
+    }
+
+    native private void recalcAabb(long shapeId);
 
     /**
      * Instantiate the configured shape in Bullet.

+ 1 - 1
jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java

@@ -147,7 +147,7 @@ public class HeightfieldCollisionShape extends CollisionShape {
         this.maxHeight = max;
 
         this.upAxis = 1;
-        this.flipQuadEdges = false;
+        flipQuadEdges = true;
 
         heightStickWidth = (int) FastMath.sqrt(heightfieldData.length);
         heightStickLength = heightStickWidth;

+ 28 - 0
jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java

@@ -166,6 +166,24 @@ public class SixDofJoint extends PhysicsJoint {
         translationalMotor = new TranslationalLimitMotor(getTranslationalLimitMotor(objectId));
     }
 
+    native private void getAngles(long jointId, Vector3f storeVector);
+
+    /**
+     * Copy the joint's rotation angles.
+     *
+     * @param storeResult storage for the result (modified if not null)
+     * @return the rotation angle for each local axis (in radians, either
+     * storeResult or a new vector, not null)
+     */
+    public Vector3f getAngles(Vector3f storeResult) {
+        Vector3f result = (storeResult == null) ? new Vector3f() : storeResult;
+
+        long constraintId = getObjectId();
+        getAngles(constraintId, result);
+
+        return result;
+    }
+
     private native long getRotationalLimitMotor(long objectId, int index);
 
     private native long getTranslationalLimitMotor(long objectId);
@@ -281,6 +299,11 @@ public class SixDofJoint extends PhysicsJoint {
         getTranslationalLimitMotor().setLowerLimit((Vector3f) capsule.readSavable("transMotor_LowerLimit", Vector3f.ZERO));
         getTranslationalLimitMotor().setRestitution(capsule.readFloat("transMotor_Restitution", 0.5f));
         getTranslationalLimitMotor().setUpperLimit((Vector3f) capsule.readSavable("transMotor_UpperLimit", Vector3f.ZERO));
+
+        for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
+            translationalMotor.setEnabled(axisIndex, capsule.readBoolean(
+                    "transMotor_Enable" + axisIndex, false));
+        }
     }
 
     /**
@@ -318,5 +341,10 @@ public class SixDofJoint extends PhysicsJoint {
         capsule.write(getTranslationalLimitMotor().getLowerLimit(), "transMotor_LowerLimit", Vector3f.ZERO);
         capsule.write(getTranslationalLimitMotor().getRestitution(), "transMotor_Restitution", 0.5f);
         capsule.write(getTranslationalLimitMotor().getUpperLimit(), "transMotor_UpperLimit", Vector3f.ZERO);
+
+        for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
+            capsule.write(translationalMotor.isEnabled(axisIndex),
+                    "transMotor_Enable" + axisIndex, false);
+        }
     }
 }

+ 27 - 1
jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2018 jMonkeyEngine
+ * Copyright (c) 2009-2019 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -204,4 +204,30 @@ public class TranslationalLimitMotor {
     }
 
     private native void setRestitution(long motorId, float restitution);
+
+    /**
+     * Enable or disable the indexed axis.
+     *
+     * @param axisIndex which axis: 0&rarr;X, 1&rarr;Y, 2&rarr;Z
+     * @param enableMotor true&rarr;enable, false&rarr;disable (default=false)
+     */
+    public void setEnabled(int axisIndex, boolean enableMotor) {
+        setEnabled(motorId, axisIndex, enableMotor);
+    }
+
+    native private void setEnabled(long motorId, int axisIndex,
+            boolean enableMotor);
+
+    /**
+     * Test whether the indexed axis is enabled.
+     *
+     * @param axisIndex which axis: 0&rarr;X, 1&rarr;Y, 2&rarr;Z
+     * @return true if enabled, otherwise false
+     */
+    public boolean isEnabled(int axisIndex) {
+        boolean result = isEnabled(motorId, axisIndex);
+        return result;
+    }
+
+    native private boolean isEnabled(long motorId, int axisIndex);
 }

+ 34 - 0
jme3-core/src/main/java/com/jme3/anim/AnimComposer.java

@@ -32,6 +32,10 @@ public class AnimComposer extends AbstractControl {
         layers.put(DEFAULT_LAYER, new Layer(this));
     }
 
+    public boolean hasAnimClip(String name) {
+        return animClipMap.containsKey(name);
+    }
+
     /**
      * Retrieve an animation from the list of animations.
      *
@@ -104,6 +108,36 @@ public class AnimComposer extends AbstractControl {
         l.time = 0;
         l.currentAction = null;
     }
+    
+    /**
+     * Returns current time of the specified layer.
+     */
+    public double getTime(String layerName) {
+        Layer l = layers.get(layerName);
+        if (l == null) {
+            throw new IllegalArgumentException("Unknown layer " + layerName);
+        }
+        return l.time;
+    }
+
+    /**
+     * Sets current time on the specified layer. 
+     */
+    public void setTime(String layerName, double time) {
+        Layer l = layers.get(layerName);
+        if (l == null) {
+            throw new IllegalArgumentException("Unknown layer " + layerName);
+        }
+        if (l.currentAction == null) {
+            throw new RuntimeException("There is no action running in layer " + layerName);
+        }
+        double length = l.currentAction.getLength();
+        if (time >= 0) {
+            l.time = time % length;
+        } else {
+            l.time = time % length + length;
+        }
+    }
 
     /**
      * 

+ 3 - 0
jme3-core/src/main/java/com/jme3/anim/Joint.java

@@ -268,6 +268,9 @@ public class Joint implements Savable, JmeCloneable, HasLocalTransform {
         return attachedNode;
     }
 
+    public Transform getInitialTransform() {
+        return initialTransform;
+    }
 
     public Transform getLocalTransform() {
         return localTransform;

+ 6 - 4
jme3-core/src/main/java/com/jme3/anim/TransformTrack.java

@@ -233,11 +233,13 @@ public class TransformTrack implements AnimTrack<Transform> {
         int endFrame = 1;
         float blend = 0;
         if (time >= times[lastFrame]) {
+            // extrapolate beyond the final frame of the animation
             startFrame = lastFrame;
 
-            time = time - times[startFrame] + times[startFrame - 1];
-            blend = (time - times[startFrame - 1])
-                    / (times[startFrame] - times[startFrame - 1]);
+            float inferredInterval = times[lastFrame] - times[lastFrame - 1];
+            if (inferredInterval > 0f) {
+                blend = (time - times[startFrame]) / inferredInterval;
+            }
 
         } else {
             // use lastFrame so we never overflow the array
@@ -297,7 +299,7 @@ public class TransformTrack implements AnimTrack<Transform> {
     }
 
     @Override
-    public Object jmeClone() {
+    public TransformTrack jmeClone() {
         try {
             TransformTrack clone = (TransformTrack) super.clone();
             return clone;

+ 1 - 0
jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java

@@ -74,6 +74,7 @@ public abstract class BlendableAction extends Action {
 
     public void setTransitionLength(double transitionLength) {
         this.transitionLength = transitionLength;
+        this.transition.setLength(transitionLength);
     }
 
     protected float getTransitionWeight() {

+ 33 - 1
jme3-core/src/main/java/com/jme3/app/state/AbstractAppState.java

@@ -40,7 +40,7 @@ import com.jme3.renderer.RenderManager;
  * @author Kirill Vainer
  * @see com.jme3.app.state.BaseAppState
  */
-public class AbstractAppState implements AppState {
+public abstract class AbstractAppState implements AppState {
 
     /**
      * <code>initialized</code> is set to true when the method
@@ -50,38 +50,70 @@ public class AbstractAppState implements AppState {
      */
     protected boolean initialized = false;
     private boolean enabled = true;
+    private String id;
+    
+    protected AbstractAppState() {    
+    }
+    
+    protected AbstractAppState( String id ) {
+        this.id = id;
+    }
 
+    @Override
     public void initialize(AppStateManager stateManager, Application app) {
         initialized = true;
     }
 
+    @Override
     public boolean isInitialized() {
         return initialized;
     }
 
+    /**
+     *  Sets the unique ID of this app state.  Note: that setting
+     *  this while an app state is attached to the state manager will
+     *  have no effect on ID-based lookups.
+     */
+    protected void setId( String id ) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
     public void setEnabled(boolean enabled) {
         this.enabled = enabled;
     }
     
+    @Override
     public boolean isEnabled() {
         return enabled;
     }
 
+    @Override
     public void stateAttached(AppStateManager stateManager) {
     }
 
+    @Override
     public void stateDetached(AppStateManager stateManager) {
     }
 
+    @Override
     public void update(float tpf) {
     }
 
+    @Override
     public void render(RenderManager rm) {
     }
 
+    @Override
     public void postRender(){
     }
 
+    @Override
     public void cleanup() {
         initialized = false;
     }

+ 6 - 0
jme3-core/src/main/java/com/jme3/app/state/AppState.java

@@ -90,6 +90,12 @@ public interface AppState {
      */
     public boolean isInitialized();
 
+    /**
+     *  Returns the unique ID for this AppState or null if it has no
+     *  unique ID.
+     */
+    public String getId();
+
     /**
      * Enable or disable the functionality of the <code>AppState</code>.
      * The effect of this call depends on implementation. An 

+ 51 - 2
jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java

@@ -37,6 +37,8 @@ import com.jme3.renderer.RenderManager;
 import com.jme3.util.SafeArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * The <code>AppStateManager</code> holds a list of {@link AppState}s which
@@ -82,6 +84,12 @@ public class AppStateManager {
      *  cleanup.  
      */
     private final SafeArrayList<AppState> terminating = new SafeArrayList<AppState>(AppState.class);
+
+    /**
+     *  Thread-safe index of every state that is currently attached and has
+     *  an ID.
+     */
+    private final ConcurrentMap<String, AppState> stateIndex = new ConcurrentHashMap<>();
  
     // All of the above lists need to be thread safe but access will be
     // synchronized separately.... but always on the states list.  This
@@ -122,7 +130,8 @@ public class AppStateManager {
 
     /**
      * Attach a state to the AppStateManager, the same state cannot be attached
-     * twice.
+     * twice.  Throws an IllegalArgumentException if the state has an ID and that
+     * ID has already been associated with another AppState.                
      *
      * @param state The state to attach
      * @return True if the state was successfully attached, false if the state
@@ -130,11 +139,16 @@ public class AppStateManager {
      */
     public boolean attach(AppState state){
         synchronized (states){
+            if( state.getId() != null && stateIndex.putIfAbsent(state.getId(), state) != null ) {
+                throw new IllegalArgumentException("ID:" + state.getId() 
+                        + " is already being used by another state:" 
+                        + stateIndex.get(state.getId()));
+            }
             if (!states.contains(state) && !initializing.contains(state)){
                 state.stateAttached(this);
                 initializing.add(state);
                 return true;
-            }else{
+            } else {
                 return false;
             }
         }
@@ -175,6 +189,12 @@ public class AppStateManager {
      */
     public boolean detach(AppState state){
         synchronized (states){
+        
+            // Remove it from the index if it exists.
+            // Note: we remove it directly from the values() in case
+            // the state has changed its ID since registered.
+            stateIndex.values().remove(state);
+        
             if (states.contains(state)){
                 state.stateDetached(this);
                 states.remove(state);
@@ -249,6 +269,35 @@ public class AppStateManager {
         return null;
     }
 
+    /**
+     *  Returns the state associated with the specified ID at the time it was
+     *  attached or null if not state was attached with that ID.
+     */
+    public <T extends AppState> T getState( String id, Class<T> stateClass ) {
+        return stateClass.cast(stateIndex.get(id));
+    }
+    
+    /**
+     *  Returns true if there is currently a state associated with the specified
+     *  ID.
+     */
+    public boolean hasState( String id ) {
+        return stateIndex.containsKey(id);
+    }
+ 
+    /**
+     *  Returns the state associated with the specified ID at the time it
+     *  was attached or throws an IllegalArgumentException if the ID was 
+     *  not found.
+     */   
+    public <T extends AppState> T stateForId( String id, Class<T> stateClass ) {
+        T result = getState(id, stateClass);
+        if( result == null ) {
+            throw new IllegalArgumentException("State not found for:" + id);
+        }
+        return stateClass.cast(result);
+    } 
+
     protected void initializePending(){
         AppState[] array = getInitializing();
         if (array.length == 0)

+ 22 - 0
jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java

@@ -78,6 +78,14 @@ public abstract class BaseAppState implements AppState {
     private Application app;
     private boolean initialized;
     private boolean enabled = true;
+    private String id;
+
+    protected BaseAppState() {
+    }
+    
+    protected BaseAppState( String id ) {
+        this.id = id;
+    }
 
     /**
      *  Called during initialization once the app state is
@@ -133,6 +141,20 @@ public abstract class BaseAppState implements AppState {
         return initialized;
     }
 
+    /**
+     *  Sets the unique ID of this app state.  Note: that setting
+     *  this while an app state is attached to the state manager will
+     *  have no effect on ID-based lookups.
+     */
+    protected void setId( String id ) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
     public final Application getApplication() {
         return app;
     }

+ 18 - 0
jme3-core/src/main/java/com/jme3/app/state/RootNodeAppState.java

@@ -85,6 +85,18 @@ public class RootNodeAppState extends AbstractAppState {
         this.rootNode = rootNode;
     }
 
+    /**
+     * Creates the AppState with the given unique ID, ViewPort, and root Node, attaches
+     * the root Node to the ViewPort and updates it.
+     * @param viewPort An existing ViewPort
+     * @param rootNode An existing root Node
+     */
+    public RootNodeAppState( String id, ViewPort viewPort, Node rootNode ) {
+        super(id);
+        this.viewPort = viewPort;
+        this.rootNode = rootNode;
+    }
+
     @Override
     public void initialize(AppStateManager stateManager, Application app) {
         if (rootNode == null) {
@@ -101,6 +113,12 @@ public class RootNodeAppState extends AbstractAppState {
     public void update(float tpf) {
         super.update(tpf);
         rootNode.updateLogicalState(tpf);
+        
+        // FIXME: I'm 99% sure that updateGeometricState() should be
+        // called in render() so that it is done as late as possible.
+        // In complicated app state setups, cross-state chatter could 
+        // cause nodes (or their children) to be updated after this 
+        // app state's update has been called.  -pspeed:2019-09-15 
         rootNode.updateGeometricState();
     }
 

+ 4 - 1
jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java

@@ -41,6 +41,7 @@ import com.jme3.renderer.Caps;
 import com.jme3.scene.Spatial;
 import com.jme3.shader.Glsl100ShaderGenerator;
 import com.jme3.shader.Glsl150ShaderGenerator;
+import com.jme3.shader.Glsl300ShaderGenerator;
 import com.jme3.shader.ShaderGenerator;
 import com.jme3.system.JmeSystem;
 import com.jme3.texture.Texture;
@@ -434,7 +435,9 @@ public class DesktopAssetManager implements AssetManager {
     @Override
     public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps) {
         if (shaderGenerator == null) {
-            if(caps.contains(Caps.GLSL150)){
+            if(caps.contains(Caps.OpenGLES30) && caps.contains(Caps.GLSL300)){
+                shaderGenerator = new Glsl300ShaderGenerator(this);
+            }else if(caps.contains(Caps.GLSL150)) {
                 shaderGenerator = new Glsl150ShaderGenerator(this);
             }else{
                 shaderGenerator = new Glsl100ShaderGenerator(this);

+ 5 - 1
jme3-core/src/main/java/com/jme3/audio/AudioNode.java

@@ -158,6 +158,7 @@ public class AudioNode extends Node implements AudioSource {
      *
      * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead
      */
+    @Deprecated
     public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) {
         this.audioKey = new AudioKey(name, stream, streamCache);
         this.data = (AudioData) assetManager.loadAsset(audioKey);
@@ -173,6 +174,7 @@ public class AudioNode extends Node implements AudioSource {
      *
      * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead
      */
+    @Deprecated
     public AudioNode(AssetManager assetManager, String name, boolean stream) {
         this(assetManager, name, stream, true); // Always streamCached
     }
@@ -186,6 +188,7 @@ public class AudioNode extends Node implements AudioSource {
      *
      * @deprecated AudioRenderer parameter is ignored.
      */
+    @Deprecated
     public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) {
         this(assetManager, name, DataType.Buffer);
     }
@@ -197,6 +200,7 @@ public class AudioNode extends Node implements AudioSource {
      * @param name The filename of the audio file
      * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType) } instead
      */
+    @Deprecated
     public AudioNode(AssetManager assetManager, String name) {
         this(assetManager, name, DataType.Buffer);
     }
@@ -722,7 +726,7 @@ public class AudioNode extends Node implements AudioSource {
     @Override
     public void updateGeometricState() {
         super.updateGeometricState();
-        if (channel < 0 || this.getParent() == null) return;
+        if (channel < 0) return;
         Vector3f currentWorldTranslation = worldTransform.getTranslation();
         if (!previousWorldTranslation.equals(currentWorldTranslation)) {
             getRenderer().updateSourceParam(this, AudioParam.Position);

+ 3 - 3
jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java

@@ -79,9 +79,9 @@ public class BoundingBox extends BoundingVolume {
      * Instantiate a <code>BoundingBox</code> with given center and extents.
      *
      * @param c the coordinates of the center of the box (not null, not altered)
-     * @param x the X-extent of the box (>=0, may be +Infinity)
-     * @param y the Y-extent of the box (>=0, may be +Infinity)
-     * @param z the Z-extent of the box (>=0, may be +Infinity)
+     * @param x the X-extent of the box (0 or greater, may be +Infinity)
+     * @param y the Y-extent of the box (0 or greater, may be +Infinity)
+     * @param z the Z-extent of the box (0 or greater, may be +Infinity)
      */
     public BoundingBox(Vector3f c, float x, float y, float z) {
         this.center.set(c);

+ 5 - 5
jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java

@@ -173,11 +173,11 @@ public class BoundingSphere extends BoundingVolume {
 
     /**
      * Calculates a minimum bounding sphere for the set of points. The algorithm
-     * was originally found in C++ at
-     * <p><a href="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1">
-     * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1</a><br><strong>broken link</strong></p>
-     * <p>and translated to java by Cep21</p>
-     *
+     * was originally found in C++ at <br>
+     * <a href="http://flipcode.com/archives/Smallest_Enclosing_Spheres.shtml">
+     * http://flipcode.com/archives/Smallest_Enclosing_Spheres.shtml</a> <br> 
+     * and translated to java by Cep21
+     *  
      * @param points
      *            The points to calculate the minimum bounds from.
      */

+ 15 - 0
jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java

@@ -99,6 +99,7 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
     private boolean initialized = false;
     private Map<String, Map<Object, Object>> eventsData;
     private float nextEnqueue = 0;
+    private String id;
 
     /**
      * Used for serialization creates a cinematic, don't use this constructor
@@ -291,6 +292,20 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         return initialized;
     }
 
+    /**
+     *  Sets the unique ID of this app state.  Note: that setting
+     *  this while an app state is attached to the state manager will
+     *  have no effect on ID-based lookups.
+     */
+    protected void setId( String id ) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
     /**
      * passing true has the same effect as play() you should use play(),
      * pause(), stop() to handle the cinematic playing state.

+ 1 - 1
jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java

@@ -41,7 +41,7 @@ import java.io.IOException;
 
 /**
  *
- * @author Rickard <neph1 @ github>
+ * @author Rickard (neph1 @ github)
  */
 public class CameraEvent extends AbstractCinematicEvent{
 

+ 1 - 1
jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java

@@ -783,7 +783,7 @@ public class ParticleEmitter extends Geometry {
      */
     public void setParticlesPerSec(float particlesPerSec) {
         this.particlesPerSec = particlesPerSec;
-        timeDifference = 0;
+        timeDifference = Math.min(timeDifference,1f / particlesPerSec); //prevent large accumulated timeDifference from causing a huge number of particles to be emitted
     }
 
     /**

Некоторые файлы не были показаны из-за большого количества измененных файлов