diff --git a/.github/actions/tools/bintray.sh b/.github/actions/tools/bintray.sh new file mode 100644 index 0000000000..0c7d4e6ddf --- /dev/null +++ b/.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" +} diff --git a/.github/actions/tools/uploadToMaven.sh b/.github/actions/tools/uploadToMaven.sh new file mode 100644 index 0000000000..1c817a6152 --- /dev/null +++ b/.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" +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..8782a24e63 --- /dev/null +++ b/.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 "actions@users.noreply.github.com" -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 "actions@users.noreply.github.com" + + 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" git@github.com:${{ 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 "actions@users.noreply.github.com" + + git add . + git commit -m "$version" + + branch="gh-pages" + git push origin "$branch" --force + + fi diff --git a/.gitignore b/.gitignore index 6bed74f10e..1e5bf3f071 100644 --- a/.gitignore +++ b/.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 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c0e3bd6e9b..0000000000 --- a/.travis.yml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index abb84c53c6..47fd3f7002 100644 --- a/CONTRIBUTING.md +++ b/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. diff --git a/README.md b/README.md index 1f47e8aca4..bc63883568 100644 --- a/README.md +++ b/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 3-D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.3 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 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 00c9d5c265..0000000000 --- a/appveyor.yml +++ /dev/null @@ -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 git@github.com:jMonkeyEngine/jmonkeyengine.git diff --git a/build.gradle b/build.gradle index 3ccd202a90..db97fb4756 100644 --- a/build.gradle +++ b/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 diff --git a/common-android-app.gradle b/common-android-app.gradle index ff5dea0b16..35d5ecaf7a 100644 --- a/common-android-app.gradle +++ b/common-android-app.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' group = 'org.jmonkeyengine' -version = jmeVersion + '-' + jmeVersionTag +version = jmeFullVersion sourceCompatibility = '1.6' diff --git a/common.gradle b/common.gradle index 80b22904d7..21ffc4d015 100644 --- a/common.gradle +++ b/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 = "jMonkeyEngine ${jmeMainVersion} ${project.name}" + options.docTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc" + options.windowTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc" + options.header = "jMonkeyEngine ${jmeFullVersion} ${project.name}" options.author = "true" options.use = "true" options.charSet = "UTF-8" diff --git a/gradle.properties b/gradle.properties index 38b86a10f6..82c99b55ac 100644 --- a/gradle.properties +++ b/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 diff --git a/jme3-android-examples/build.gradle b/jme3-android-examples/build.gradle index 023b662f04..17be807ebb 100644 --- a/jme3-android-examples/build.gradle +++ b/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') diff --git a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java index 907dd4e6ac..0e2faeea3a 100644 --- a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java +++ b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java @@ -62,15 +62,15 @@ public JmeFragment() { 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) diff --git a/jme3-android-native/.gitignore b/jme3-android-native/.gitignore new file mode 100644 index 0000000000..9010f892c2 --- /dev/null +++ b/jme3-android-native/.gitignore @@ -0,0 +1,2 @@ +# The headers are autogenerated and nobody should try to commit them... +src/native/headers diff --git a/jme3-android-native/build.gradle b/jme3-android-native/build.gradle index 0ca99df778..50e6f94178 100644 --- a/jme3-android-native/build.gradle +++ b/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') \ No newline at end of file +apply from: file('decode.gradle') diff --git a/jme3-android-native/decode.gradle b/jme3-android-native/decode.gradle index f5ab90a00a..07d93a04af 100644 --- a/jme3-android-native/decode.gradle +++ b/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) diff --git a/jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so b/jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so deleted file mode 100755 index b8aa7b6a41..0000000000 Binary files a/jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so b/jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so deleted file mode 100755 index e7e55030e3..0000000000 Binary files a/jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/decode/armeabi/libdecodejme.so b/jme3-android-native/libs/decode/armeabi/libdecodejme.so deleted file mode 100755 index c6de180093..0000000000 Binary files a/jme3-android-native/libs/decode/armeabi/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/decode/mips/libdecodejme.so b/jme3-android-native/libs/decode/mips/libdecodejme.so deleted file mode 100755 index 230e753a44..0000000000 Binary files a/jme3-android-native/libs/decode/mips/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/decode/mips64/libdecodejme.so b/jme3-android-native/libs/decode/mips64/libdecodejme.so deleted file mode 100755 index e60aa07f54..0000000000 Binary files a/jme3-android-native/libs/decode/mips64/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/decode/x86/libdecodejme.so b/jme3-android-native/libs/decode/x86/libdecodejme.so deleted file mode 100755 index 22256712d6..0000000000 Binary files a/jme3-android-native/libs/decode/x86/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/decode/x86_64/libdecodejme.so b/jme3-android-native/libs/decode/x86_64/libdecodejme.so deleted file mode 100755 index fac6ecd801..0000000000 Binary files a/jme3-android-native/libs/decode/x86_64/libdecodejme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so deleted file mode 100755 index 907bd76ec1..0000000000 Binary files a/jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so deleted file mode 100755 index 7d78532ff4..0000000000 Binary files a/jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so deleted file mode 100755 index ddeef5b405..0000000000 Binary files a/jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so deleted file mode 100755 index 76bcef4a9d..0000000000 Binary files a/jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so deleted file mode 100755 index 3c028aa7f6..0000000000 Binary files a/jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so deleted file mode 100755 index 0664fc7844..0000000000 Binary files a/jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so b/jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so deleted file mode 100755 index 6926d74563..0000000000 Binary files a/jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so and /dev/null differ diff --git a/jme3-android-native/openalsoft.gradle b/jme3-android-native/openalsoft.gradle index ed4eeacfdb..d8e5e836e2 100644 --- a/jme3-android-native/openalsoft.gradle +++ b/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 diff --git a/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.c b/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.c index 0b04cea1c7..2e62dee6b8 100644 --- a/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.c +++ b/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 @@ -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); -} \ No newline at end of file +} diff --git a/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.c b/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.c index b03dd8e7dc..6d8084e6c0 100644 --- a/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.c +++ b/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 #ifndef NDEBUG diff --git a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.c b/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.c index 0b6fb61b77..10ba99755b 100644 --- a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.c +++ b/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" diff --git a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.h b/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.h deleted file mode 100644 index 1fa80bbf4e..0000000000 --- a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.h +++ /dev/null @@ -1,173 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* 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 diff --git a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.c b/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.c index 4438910cc8..c919f951a5 100644 --- a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.c +++ b/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); -} \ No newline at end of file +} diff --git a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.h b/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.h deleted file mode 100644 index e50b7b5113..0000000000 --- a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.h +++ /dev/null @@ -1,77 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* 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 diff --git a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.c b/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.c index ea50c2672f..6d3b623db2 100644 --- a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.c +++ b/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); -} \ No newline at end of file +} diff --git a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.h b/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.h deleted file mode 100644 index 4bdd94f075..0000000000 --- a/jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.h +++ /dev/null @@ -1,101 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* 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 diff --git a/jme3-android/build.gradle b/jme3-android/build.gradle index 4caad528e7..98a2b37a0a 100644 --- a/jme3-android/build.gradle +++ b/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"] +} diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java index 44e931104a..ced4260ea5 100644 --- a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java +++ b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java @@ -34,6 +34,7 @@ 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.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 void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) { } 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 void glViewport(int x, int y, int width, int height) { } 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 void glBufferSubData(int target, long offset, IntBuffer data) { } 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 void glBlendEquationSeparate(int colorMode, int alphaMode) { @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); + } + } + diff --git a/jme3-android/src/main/java/com/jme3/system/android/AndroidConfigChooser.java b/jme3-android/src/main/java/com/jme3/system/android/AndroidConfigChooser.java index ba4c5a42e2..4bccf537aa 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/AndroidConfigChooser.java +++ b/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 @@ private EGLConfig[] getConfigs(EGL10 egl, EGLDisplay display) { 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]; diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java index db185b0026..7ea954eac4 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java +++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java @@ -53,12 +53,15 @@ 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 Type getType() { * @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 GLSurfaceView createView(Context context) { // 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 void uncaughtException(Thread thread, Throwable thrown) { 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(); diff --git a/jme3-android/src/main/java/com/jme3/util/AndroidBufferAllocator.java b/jme3-android/src/main/java/com/jme3/util/AndroidBufferAllocator.java new file mode 100644 index 0000000000..73e203da54 --- /dev/null +++ b/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 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); + } +} + diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java index 88130fa3f8..41e3e9475e 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java @@ -142,8 +142,14 @@ public Image blend(Image image, Image baseImage, BlenderContext blenderContext) } dataArray.add(newData); } - - ColorSpace colorSpace = baseImage.getColorSpace() != null ? baseImage.getColorSpace() : 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()); diff --git a/jme3-bullet-native-android/build.gradle b/jme3-bullet-native-android/build.gradle index 1058ef3225..fecb39b39f 100644 --- a/jme3-bullet-native-android/build.gradle +++ b/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 diff --git a/jme3-bullet-native-android/libs/arm64-v8a/libbulletjme.so b/jme3-bullet-native-android/libs/arm64-v8a/libbulletjme.so deleted file mode 100755 index e64b52ba1b..0000000000 Binary files a/jme3-bullet-native-android/libs/arm64-v8a/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/libs/armeabi-v7a/libbulletjme.so b/jme3-bullet-native-android/libs/armeabi-v7a/libbulletjme.so deleted file mode 100755 index 7e2aad17b7..0000000000 Binary files a/jme3-bullet-native-android/libs/armeabi-v7a/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/libs/armeabi/libbulletjme.so b/jme3-bullet-native-android/libs/armeabi/libbulletjme.so deleted file mode 100755 index f54710e4a7..0000000000 Binary files a/jme3-bullet-native-android/libs/armeabi/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/libs/mips/libbulletjme.so b/jme3-bullet-native-android/libs/mips/libbulletjme.so deleted file mode 100755 index adc7fa1aa7..0000000000 Binary files a/jme3-bullet-native-android/libs/mips/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/libs/mips64/libbulletjme.so b/jme3-bullet-native-android/libs/mips64/libbulletjme.so deleted file mode 100755 index 094ae8478d..0000000000 Binary files a/jme3-bullet-native-android/libs/mips64/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/libs/x86/libbulletjme.so b/jme3-bullet-native-android/libs/x86/libbulletjme.so deleted file mode 100755 index 1089c838e7..0000000000 Binary files a/jme3-bullet-native-android/libs/x86/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/libs/x86_64/libbulletjme.so b/jme3-bullet-native-android/libs/x86_64/libbulletjme.so deleted file mode 100755 index 2f26225794..0000000000 Binary files a/jme3-bullet-native-android/libs/x86_64/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native-android/src/native/android/Android.mk b/jme3-bullet-native-android/src/native/android/Android.mk index 1a1e64b89c..d04478e285 100644 --- a/jme3-bullet-native-android/src/native/android/Android.mk +++ b/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) diff --git a/jme3-bullet-native-android/src/native/android/Application.mk b/jme3-bullet-native-android/src/native/android/Application.mk index 44c5c842e5..6467a5a7f9 100644 --- a/jme3-bullet-native-android/src/native/android/Application.mk +++ b/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 diff --git a/jme3-bullet-native/build.gradle b/jme3-bullet-native/build.gradle index f17dee1831..745630a3b8 100644 --- a/jme3-bullet-native/build.gradle +++ b/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 "-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" - 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" + 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')) + } } } diff --git a/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so b/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so deleted file mode 100755 index 6c4ad2a4fe..0000000000 Binary files a/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so b/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so deleted file mode 100755 index c519ea8e03..0000000000 Binary files a/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so and /dev/null differ diff --git a/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib b/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib deleted file mode 100755 index d6d5c03ad2..0000000000 Binary files a/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib and /dev/null differ diff --git a/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib b/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib deleted file mode 100755 index 07eab46114..0000000000 Binary files a/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib and /dev/null differ diff --git a/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll b/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll deleted file mode 100755 index 8af840f88c..0000000000 Binary files a/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll and /dev/null differ diff --git a/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll b/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll deleted file mode 100755 index 323aa9681c..0000000000 Binary files a/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll and /dev/null differ diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_PhysicsCollisionObject.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_PhysicsCollisionObject.cpp index 0c7f0161ae..15d27849ce 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_PhysicsCollisionObject.cpp +++ b/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 (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 } diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_CollisionShape.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_CollisionShape.cpp index dc48be257a..bfdaa20de6 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_CollisionShape.cpp +++ b/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 (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 diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_SixDofJoint.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_SixDofJoint.cpp index ea55820e0f..24a26c0cfa 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_SixDofJoint.cpp +++ b/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(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 (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 diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp index 39db519e70..14e66933f6 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_joints_motors_TranslationalLimitMotor.cpp +++ b/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 (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 (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 diff --git a/jme3-bullet/build.gradle b/jme3-bullet/build.gradle index 404e6cb002..64185b3442 100644 --- a/jme3-bullet/build.gradle +++ b/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) \ No newline at end of file diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java b/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java index 9fb5b85f1c..2dc46906ce 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java +++ b/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 @@ * * @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 PhysicsSpace getPhysicsSpace() { * sooner, invoke this method. */ public void startPhysics() { - if (initialized) { + if (isRunning) { return; } @@ -252,14 +254,14 @@ public void startPhysics() { 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 void stopPhysics() { } pSpace.removeTickListener(this); pSpace.destroy(); - initialized = false; + isRunning = false; } /** @@ -278,40 +280,14 @@ public void stopPhysics() { * @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 → enable, false → 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 boolean isDebugEnabled() { * * @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 void stateAttached(AppStateManager stateManager) { } } - /** - * 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 void stateDetached(AppStateManager stateManager) { * * @param tpf the time interval between frames (in seconds, ≥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 void update(float tpf) { * * @param rm the render manager (not null) */ + @Override public void render(RenderManager rm) { + super.render(rm); if (!active) { return; } @@ -405,7 +378,9 @@ public void render(RenderManager rm) { * 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 void postRender() { * {@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(); } /** diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java index ea8ced87c8..df4265be5c 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java @@ -158,6 +158,18 @@ public CollisionShape getCollisionShape() { 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. * diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java index acdd6ed04d..94d39973d9 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java @@ -87,7 +87,7 @@ public CollisionShape() { //// objectId.calculateLocalInertia(mass, vector); //// } // } -// +// // private native void calculateLocalInertia(long objectId, long shapeId, float mass); /** @@ -129,6 +129,19 @@ public Vector3f getScale() { 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 Vector3f getScale() { public float getMargin() { return getMargin(objectId); } - + private native float getMargin(long objectId); /** @@ -177,9 +190,9 @@ public void setMargin(float margin) { setMargin(objectId, margin); this.margin = margin; } - + private native void setLocalScaling(long obectId, Vector3f scale); - + private native void setMargin(long objectId, float margin); /** diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java index 660319a18e..27e4c48b93 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java @@ -166,6 +166,24 @@ private void gatherMotors() { 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 void read(JmeImporter im) throws IOException { 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 void write(JmeExporter ex) throws IOException { 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); + } } } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java index 97993edd3a..4fbf0cb896 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java +++ b/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 void setRestitution(float restitution) { } private native void setRestitution(long motorId, float restitution); + + /** + * Enable or disable the indexed axis. + * + * @param axisIndex which axis: 0→X, 1→Y, 2→Z + * @param enableMotor true→enable, false→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→X, 1→Y, 2→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); } diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java b/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java index d997ab39d1..740c4e034e 100644 --- a/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java +++ b/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java @@ -32,6 +32,10 @@ public AnimComposer() { 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 void removeCurrentAction(String layerName) { 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; + } + } /** * diff --git a/jme3-core/src/main/java/com/jme3/anim/Joint.java b/jme3-core/src/main/java/com/jme3/anim/Joint.java index 0c7153d296..1c1699f64e 100644 --- a/jme3-core/src/main/java/com/jme3/anim/Joint.java +++ b/jme3-core/src/main/java/com/jme3/anim/Joint.java @@ -268,6 +268,9 @@ Node getAttachmentsNode(int jointIndex, SafeArrayList targets) { return attachedNode; } + public Transform getInitialTransform() { + return initialTransform; + } public Transform getLocalTransform() { return localTransform; diff --git a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java index 29354fead0..63eba06cfc 100644 --- a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java +++ b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java @@ -233,11 +233,13 @@ public void getDataAtTime(double t, Transform 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 void read(JmeImporter im) throws IOException { } @Override - public Object jmeClone() { + public TransformTrack jmeClone() { try { TransformTrack clone = (TransformTrack) super.clone(); return clone; diff --git a/jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java b/jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java index 4b3b6f9660..4ebf3ede2b 100644 --- a/jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java +++ b/jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java @@ -74,6 +74,7 @@ public double getTransitionLength() { public void setTransitionLength(double transitionLength) { this.transitionLength = transitionLength; + this.transition.setLength(transitionLength); } protected float getTransitionWeight() { diff --git a/jme3-core/src/main/java/com/jme3/app/state/AbstractAppState.java b/jme3-core/src/main/java/com/jme3/app/state/AbstractAppState.java index c2e3642da6..96b7e6b6c0 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/AbstractAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/state/AbstractAppState.java @@ -40,7 +40,7 @@ * @author Kirill Vainer * @see com.jme3.app.state.BaseAppState */ -public class AbstractAppState implements AppState { +public abstract class AbstractAppState implements AppState { /** * initialized 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; } diff --git a/jme3-core/src/main/java/com/jme3/app/state/AppState.java b/jme3-core/src/main/java/com/jme3/app/state/AppState.java index 72988c0efd..33a6d28d5d 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/AppState.java +++ b/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 AppState. * The effect of this call depends on implementation. An diff --git a/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java b/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java index b15b555989..96a7295d35 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java +++ b/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java @@ -37,6 +37,8 @@ import com.jme3.util.SafeArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; /** * The AppStateManager holds a list of {@link AppState}s which @@ -82,6 +84,12 @@ public class AppStateManager { * cleanup. */ private final SafeArrayList terminating = new SafeArrayList(AppState.class); + + /** + * Thread-safe index of every state that is currently attached and has + * an ID. + */ + private final ConcurrentMap 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 @@ protected AppState[] getStates(){ /** * 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 @@ protected AppState[] getStates(){ */ 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 void attachAll(Iterable states){ */ 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 T getState(Class stateClass, boolean failOnMiss){ 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 getState( String id, Class 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 stateForId( String id, Class 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) diff --git a/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java b/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java index cee045355e..a745215613 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java +++ b/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 final 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; + } + public final Application getApplication() { return app; } diff --git a/jme3-core/src/main/java/com/jme3/app/state/RootNodeAppState.java b/jme3-core/src/main/java/com/jme3/app/state/RootNodeAppState.java index 8daa9dc3a7..f7406ba55a 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/RootNodeAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/state/RootNodeAppState.java @@ -85,6 +85,18 @@ public RootNodeAppState(ViewPort viewPort, Node rootNode) { 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 void initialize(AppStateManager stateManager, Application app) { 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(); } diff --git a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java index bf6b41b7ab..0c92e19e91 100644 --- a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java @@ -41,6 +41,7 @@ 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 FilterPostProcessor loadFilter(String name){ @Override public ShaderGenerator getShaderGenerator(EnumSet 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); diff --git a/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java b/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java index d9e9d5b11f..3bcbc17a74 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java +++ b/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> eventsData; private float nextEnqueue = 0; + private String id; /** * Used for serialization creates a cinematic, don't use this constructor @@ -291,6 +292,20 @@ 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; + } + /** * passing true has the same effect as play() you should use play(), * pause(), stop() to handle the cinematic playing state. diff --git a/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java b/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java index d41e6bb925..2e206a416b 100644 --- a/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java +++ b/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java @@ -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 @@ -36,7 +36,9 @@ import com.jme3.material.MatParamTexture; import java.io.IOException; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -161,16 +163,19 @@ public static int getSavedSavableVersion(Object savable, Class loaders) throws InstantiationException, + InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, IOException { if (loaders == null) { return fromName(className); @@ -208,4 +214,25 @@ public static Savable fromName(String className, List loaders) thro return fromName(className); } + + /** + * Use reflection to gain access to the no-arg constructor of the named + * class. + * + * @return the pre-existing constructor (not null) + */ + private static Constructor findNoArgConstructor(String className) + throws ClassNotFoundException, InstantiationException { + Class clazz = Class.forName(className); + Constructor result; + try { + result = clazz.getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + throw new InstantiationException( + "Loading requires a no-arg constructor, but class " + + className + " lacks one."); + } + + return result; + } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/Caps.java b/jme3-core/src/main/java/com/jme3/renderer/Caps.java index d4094f0158..7bda5f4ae8 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Caps.java @@ -403,7 +403,46 @@ public enum Caps { /** * Supporting working with ShaderStorageBufferObjects. */ - ShaderStorageBufferObject; + ShaderStorageBufferObject, + + /** + * Supports OpenGL ES 3.0 + */ + OpenGLES30, + + /** + * Supports GLSL 3.0 + */ + GLSL300, + + /** + * Supports OpenGL ES 3.1 + */ + OpenGLES31, + + /** + * Supports GLSL 3.1 + */ + GLSL310, + + /** + * Supports OpenGL ES 3.2 + */ + OpenGLES32, + + /** + * Supports GLSL 3.2 + */ + GLSL320, + + /** + * Explicit support of depth 24 textures + */ + Depth24, + + + UnpackRowLength + ; /** * Returns true if given the renderer capabilities, the texture diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java index bd3e2ea64f..9f7c5ac309 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java @@ -194,6 +194,7 @@ public interface GL { public static final int GL_VERSION = 0x1F02; public static final int GL_VERTEX_SHADER = 0x8B31; public static final int GL_ZERO = 0x0; + public static final int GL_UNPACK_ROW_LENGTH = 0x0CF2; public void resetStats(); diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java index 8746d57edc..19546f20f2 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java @@ -5,7 +5,7 @@ import java.nio.IntBuffer; import java.nio.ShortBuffer; -public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt { +public class GLDebugES extends GLDebug implements GL, GL2, GLES_30, GLFbo, GLExt { private final GLFbo glfbo; private final GLExt glext; @@ -609,4 +609,54 @@ public void glFramebufferTextureLayerEXT(int param1, int param2, int param3, int glfbo.glFramebufferTextureLayerEXT(param1, param2, param3, param4, param5); checkError(); } + + public void glAlphaFunc(int func, float ref) { + ((GL2)gl).glAlphaFunc(func, ref); + checkError(); + } + + public void glPointSize(float size) { + ((GL2)gl).glPointSize(size); + checkError(); + } + + public void glPolygonMode(int face, int mode) { + ((GL2)gl).glPolygonMode(face, mode); + checkError(); + } + + public void glDrawBuffer(int mode) { + ((GL2)gl).glDrawBuffer(mode); + checkError(); + } + + public void glReadBuffer(int mode) { + ((GL2)gl).glReadBuffer(mode); + checkError(); + } + + public void glCompressedTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, + int border, ByteBuffer data) { + ((GL2)gl).glCompressedTexImage3D(target, level, internalFormat, width, height, depth, border, data); + checkError(); + } + + public void glCompressedTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, + int height, int depth, int format, ByteBuffer data) { + ((GL2)gl).glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, data); + checkError(); + } + + public void glTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, + int format, int type, ByteBuffer data) { + ((GL2)gl).glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, data); + checkError(); + } + + 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) { + ((GL2)gl).glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data); + checkError(); + } + } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java new file mode 100644 index 0000000000..017dae8163 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java @@ -0,0 +1,46 @@ +/* + * 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.renderer.opengl; + +import java.nio.ByteBuffer; + +/** + * GL functions and constants only available on vanilla OpenGL ES 3.0. + * + * @author Jesus Oliver + */ +public interface GLES_30 extends GL { + + public static final int GL_RGB10_A2 = 0x8059; + public static final int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368; + +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java index 52c9a5b3d3..6daee5aff8 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java @@ -166,7 +166,7 @@ public static GLImageFormat[][] getFormatsForCaps(EnumSet caps) { format(formatToGL, Format.Luminance8, GLExt.GL_RGBA8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE); } - format(formatToGL, Format.RGB8, GLExt.GL_RGBA8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE); + format(formatToGL, Format.RGB8, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE); format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); formatSwiz(formatToGL, Format.BGR8, GL2.GL_RGB8, GL2.GL_RGB, GL.GL_UNSIGNED_BYTE); @@ -217,8 +217,28 @@ public static GLImageFormat[][] getFormatsForCaps(EnumSet caps) { format(formatToGL, Format.RGB16F_to_RGB9E5, GLExt.GL_RGB9_E5_EXT, GL.GL_RGB, halfFloatFormat); } } + + // Supported in GLES30 core + if (caps.contains(Caps.OpenGLES30)) { + format(formatToGL, Format.RGB10A2, GLES_30.GL_RGB10_A2, GL.GL_RGBA, GLES_30.GL_UNSIGNED_INT_2_10_10_10_REV); + format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE); + format(formatToGL, Format.Luminance8, GL.GL_LUMINANCE, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); + format(formatToGL, Format.Luminance8Alpha8, GL.GL_LUMINANCE_ALPHA, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE); + + formatSrgb(formatToGL, Format.RGB8, GLExt.GL_SRGB8_EXT, GL.GL_RGB, GL.GL_UNSIGNED_BYTE); + formatSrgb(formatToGL, Format.RGBA8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); + + //Depending on the device could be better to use the previously defined extension based float textures instead of gles3.0 texture formats +// if (!caps.contains(Caps.FloatTexture)) { + format(formatToGL, Format.RGB16F, GLExt.GL_RGB16F_ARB, GL.GL_RGB, GLExt.GL_HALF_FLOAT_ARB); + format(formatToGL, Format.RGB32F, GLExt.GL_RGB32F_ARB, GL.GL_RGB, GL.GL_FLOAT); + format(formatToGL, Format.RGBA16F, GLExt.GL_RGBA16F_ARB, GL.GL_RGBA, GLExt.GL_HALF_FLOAT_ARB); + format(formatToGL, Format.RGBA32F, GLExt.GL_RGBA32F_ARB, GL.GL_RGBA, GL.GL_FLOAT); +// } + format(formatToGL, Format.RGB111110F, GLExt.GL_R11F_G11F_B10F_EXT, GL.GL_RGB, GLExt.GL_UNSIGNED_INT_10F_11F_11F_REV_EXT); + } - // Need to check if Caps.DepthTexture is supported prior to using for textures + // Need to check if Caps.DepthTexture is supported prior to using for textures // But for renderbuffers its OK. format(formatToGL, Format.Depth16, GL.GL_DEPTH_COMPONENT16, GL.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_SHORT); @@ -228,7 +248,7 @@ public static GLImageFormat[][] getFormatsForCaps(EnumSet caps) { } else { format(formatToGL, Format.Depth, GL.GL_DEPTH_COMPONENT, GL.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_BYTE); } - if (caps.contains(Caps.OpenGL20)) { + if (caps.contains(Caps.OpenGL20) || caps.contains(Caps.Depth24)) { format(formatToGL, Format.Depth24, GL2.GL_DEPTH_COMPONENT24, GL.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT); } if (caps.contains(Caps.FloatDepthBuffer)) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 0e5a01d993..d20c78b309 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -52,6 +52,7 @@ import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.Image; import com.jme3.texture.Texture; +import com.jme3.texture.Texture2D; import com.jme3.texture.Texture.ShadowCompareMode; import com.jme3.texture.Texture.WrapAxis; import com.jme3.texture.image.LastTextureState; @@ -71,6 +72,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; + public final class GLRenderer implements Renderer { private static final Logger logger = Logger.getLogger(GLRenderer.class.getName()); @@ -166,9 +168,28 @@ private boolean hasExtension(String extensionName) { } private void loadCapabilitiesES() { + int oglVer = extractVersion(gl.glGetString(GL.GL_VERSION)); caps.add(Caps.GLSL100); caps.add(Caps.OpenGLES20); + caps.add(Caps.Multisample); + + if (oglVer >= 300) { + caps.add(Caps.OpenGLES30); + caps.add(Caps.GLSL300); + // Instancing is core in GLES300 + caps.add(Caps.MeshInstancing); + } + if (oglVer >= 310) { + caps.add(Caps.OpenGLES31); + caps.add(Caps.GLSL310); + } + if (oglVer >= 320) { + caps.add(Caps.OpenGLES32); + caps.add(Caps.GLSL320); + caps.add(Caps.GeometryShader); + caps.add(Caps.TesselationShader); + } // Important: Do not add OpenGL20 - that's the desktop capability! } @@ -293,6 +314,7 @@ private void loadCapabilitiesCommon() { if (hasExtension("GL_ARB_draw_instanced") && hasExtension("GL_ARB_instanced_arrays")) { + // TODO: If there were a way to call the EXT extension for GLES2, should check also (hasExtension("GL_EXT_draw_instanced") && hasExtension("GL_EXT_instanced_arrays")) caps.add(Caps.MeshInstancing); } @@ -330,8 +352,10 @@ private void loadCapabilitiesCommon() { if (hasExtension("GL_OES_depth_texture") || gl2 != null) { caps.add(Caps.DepthTexture); + } - // TODO: GL_OES_depth24 + if (hasExtension("GL_OES_depth24")) { + caps.add(Caps.Depth24); } if (hasExtension("GL_OES_rgb8_rgba8") || @@ -340,7 +364,7 @@ private void loadCapabilitiesCommon() { caps.add(Caps.Rgba8); } - if (caps.contains(Caps.OpenGL30) || hasExtension("GL_OES_packed_depth_stencil")) { + if (caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30) || hasExtension("GL_OES_packed_depth_stencil")) { caps.add(Caps.PackedDepthStencilBuffer); } @@ -350,7 +374,7 @@ private void loadCapabilitiesCommon() { caps.add(Caps.FloatColorBuffer); } - if (hasExtension("GL_ARB_depth_buffer_float")) { + if (caps.contains(Caps.OpenGLES30) || hasExtension("GL_ARB_depth_buffer_float")) { caps.add(Caps.FloatDepthBuffer); } @@ -397,7 +421,7 @@ private void loadCapabilitiesCommon() { caps.add(Caps.PartialNonPowerOfTwoTextures); } - if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30)) { + if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) { caps.add(Caps.TextureArray); } @@ -414,16 +438,16 @@ private void loadCapabilitiesCommon() { limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT)); limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT)); - if (hasExtension("GL_EXT_framebuffer_blit") || caps.contains(Caps.OpenGL30)) { + if (hasExtension("GL_EXT_framebuffer_blit") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) { caps.add(Caps.FrameBufferBlit); } - if (hasExtension("GL_EXT_framebuffer_multisample")) { + if (hasExtension("GL_EXT_framebuffer_multisample") || caps.contains(Caps.OpenGLES30)) { caps.add(Caps.FrameBufferMultisample); limits.put(Limits.FrameBufferSamples, getInteger(GLExt.GL_MAX_SAMPLES_EXT)); } - if (hasExtension("GL_ARB_texture_multisample")) { + if (hasExtension("GL_ARB_texture_multisample") || caps.contains(Caps.OpenGLES31)) { // GLES31 does not fully support it caps.add(Caps.TextureMultisample); limits.put(Limits.ColorTextureSamples, getInteger(GLExt.GL_MAX_COLOR_TEXTURE_SAMPLES)); limits.put(Limits.DepthTextureSamples, getInteger(GLExt.GL_MAX_DEPTH_TEXTURE_SAMPLES)); @@ -432,8 +456,8 @@ private void loadCapabilitiesCommon() { limits.put(Limits.FrameBufferSamples, limits.get(Limits.ColorTextureSamples)); } } - - if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30)) { + + if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) { limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB)); if (limits.get(Limits.FrameBufferMrtAttachments) > 1) { caps.add(Caps.FrameBufferMRT); @@ -443,7 +467,7 @@ private void loadCapabilitiesCommon() { } } - if (hasExtension("GL_ARB_multisample")) { + if (hasExtension("GL_ARB_multisample") /*|| caps.contains(Caps.OpenGLES20)*/) { boolean available = getInteger(GLExt.GL_SAMPLE_BUFFERS_ARB) != 0; int samples = getInteger(GLExt.GL_SAMPLES_ARB); logger.log(Level.FINER, "Samples: {0}", samples); @@ -498,6 +522,18 @@ private void loadCapabilitiesCommon() { limits.put(Limits.UniformBufferObjectMaxVertexBlocks, getInteger(GL3.GL_MAX_VERTEX_UNIFORM_BLOCKS)); } + if (hasExtension("GL_OES_geometry_shader") || hasExtension("GL_EXT_geometry_shader")) { + caps.add(Caps.GeometryShader); + } + + if (hasExtension("GL_OES_tessellation_shader") || hasExtension("GL_EXT_tessellation_shader")) { + caps.add(Caps.TesselationShader); + } + + if(caps.contains(Caps.OpenGL20)){ + caps.add(Caps.UnpackRowLength); + } + // Print context information logger.log(Level.INFO, "OpenGL Renderer Information\n" + " * Vendor: {0}\n" + @@ -539,7 +575,7 @@ private void loadCapabilitiesCommon() { } private void loadCapabilities() { - if (gl2 != null) { + if (gl2 != null && !(gl instanceof GLES_30)) { loadCapabilitiesGL2(); } else { loadCapabilitiesES(); @@ -1327,10 +1363,11 @@ public void updateShaderSourceData(ShaderSource source) { throw new RendererException("Cannot recompile shader source"); } + boolean gles3 = caps.contains(Caps.OpenGLES30); boolean gles2 = caps.contains(Caps.OpenGLES20); String language = source.getLanguage(); - if (gles2 && !language.equals("GLSL100")) { + if (!gles3 && gles2 && !language.equals("GLSL100")) { //avoid this check for gles3 throw new RendererException("This shader cannot run in OpenGL ES 2. " + "Only GLSL 1.00 shaders are supported."); } @@ -1339,24 +1376,25 @@ public void updateShaderSourceData(ShaderSource source) { // Upload shader source. // Merge the defines and source code. stringBuf.setLength(0); + int version = Integer.parseInt(language.substring(4)); if (language.startsWith("GLSL")) { - int version = Integer.parseInt(language.substring(4)); if (version > 100) { stringBuf.append("#version "); stringBuf.append(language.substring(4)); if (version >= 150) { - stringBuf.append(" core"); + if(gles3) { + stringBuf.append(" es"); + } + else { + stringBuf.append(" core"); + } } stringBuf.append("\n"); } else { - if (gles2) { + if (gles2 || gles3) { // request GLSL ES (1.00) when compiling under GLES2. stringBuf.append("#version 100\n"); - if (source.getType() == ShaderType.Fragment) { - // GLES2 requires precision qualifier. - insertPrecision = true; - } } else { // version 100 does not exist in desktop GLSL. // put version 110 in that case to enable strict checking @@ -1364,6 +1402,15 @@ public void updateShaderSourceData(ShaderSource source) { stringBuf.append("#version 110\n"); } } + + if (gles2 || gles3) { + //Inserting precision only to fragment shaders creates some link failures because of different precision between shaders + //But adding the precision to all shaders generates rendering glitches in some devices if not set to highp + if (source.getType() == ShaderType.Fragment) { + // GLES requires precision qualifier. + insertPrecision = true; + } + } } if (linearizeSrgbImages) { @@ -1375,11 +1422,22 @@ public void updateShaderSourceData(ShaderSource source) { stringBuf.append(source.getSource()); if(insertPrecision){ + // default precision could be defined in GLSLCompat.glsllib so final users can use custom defined precision instead // precision token is not a preprocessor dirrective therefore it must be placed after #extension tokens to avoid // Error P0001: Extension directive must occur before any non-preprocessor tokens int idx = stringBuf.lastIndexOf("#extension"); idx = stringBuf.indexOf("\n", idx); - stringBuf.insert(idx + 1, "precision mediump float;\n"); + + if(version>=310) { + stringBuf.insert(idx + 1, "precision highp sampler2DMS;\n"); + } + if(version>=300) { + stringBuf.insert(idx + 1, "precision highp sampler2DArray;\n"); + stringBuf.insert(idx + 1, "precision highp sampler2DShadow;\n"); + stringBuf.insert(idx + 1, "precision highp sampler3D;\n"); + stringBuf.insert(idx + 1, "precision highp sampler2D;\n"); + } + stringBuf.insert(idx + 1, "precision highp float;\n"); } intBuf1.clear(); @@ -1612,6 +1670,7 @@ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) if (copyDepth) { mask |= GL.GL_DEPTH_BUFFER_BIT; } + glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, GL.GL_NEAREST); @@ -1842,7 +1901,7 @@ public void setMainFrameBufferOverride(FrameBuffer fb) { } public void setReadDrawBuffers(FrameBuffer fb) { - if (gl2 == null) { + if (gl2 == null || gl instanceof GLES_30) { return; } @@ -1982,6 +2041,7 @@ private void readFrameBufferWithGLFormat(FrameBuffer fb, ByteBuffer byteBuf, int context.boundReadBuf = rb.getSlot(); } } + } else { setFrameBuffer(null); } @@ -2048,7 +2108,7 @@ private int convertTextureType(Texture.Type type, int samples, int face) { return GLExt.GL_TEXTURE_2D_ARRAY_EXT; } case ThreeDimensional: - if (!caps.contains(Caps.OpenGL20)) { + if (!caps.contains(Caps.OpenGL20) && !caps.contains(Caps.OpenGLES30)) { throw new RendererException("3D textures are not supported" + " by the video hardware."); } @@ -2172,9 +2232,9 @@ private void setupTextureParams(int unit, Texture tex) { switch (tex.getType()) { case ThreeDimensional: case CubeMap: // cubemaps use 3D coords - if (gl2 != null && curState.rWrap != tex.getWrap(WrapAxis.R)) { + if (gl2 != null && (caps.contains(Caps.OpenGL20) || caps.contains(Caps.OpenGLES30)) && curState.rWrap != tex.getWrap(WrapAxis.R)) { bindTextureAndUnit(target, image, unit); - gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); + gl.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); curState.rWrap = tex.getWrap(WrapAxis.R); } //There is no break statement on purpose here @@ -2196,17 +2256,17 @@ private void setupTextureParams(int unit, Texture tex) { } ShadowCompareMode texCompareMode = tex.getShadowCompareMode(); - if (gl2 != null && curState.shadowCompareMode != texCompareMode) { + if ( (gl2 != null || caps.contains(Caps.OpenGLES30)) && curState.shadowCompareMode != texCompareMode) { bindTextureAndUnit(target, image, unit); if (texCompareMode != ShadowCompareMode.Off) { - gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE); + gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE); if (texCompareMode == ShadowCompareMode.GreaterOrEqual) { - gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); + gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); } else { - gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); + gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); } } else { - gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); + gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); } curState.shadowCompareMode = texCompareMode; } @@ -2340,7 +2400,6 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s bindTextureAndUnit(target, img, unit); int imageSamples = img.getMultiSamples(); - if (imageSamples <= 1) { if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { // Image does not have mipmaps, but they are required. @@ -2353,7 +2412,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s // For OpenGL3 and up. // We'll generate mipmaps via glGenerateMipmapEXT (see below) } - } else if (caps.contains(Caps.OpenGL20)) { + } else if (caps.contains(Caps.OpenGL20) || caps.contains(Caps.OpenGLES30)) { if (img.hasMipmaps()) { // Image already has mipmaps, set the max level based on the // number of mipmaps we have. @@ -2483,12 +2542,34 @@ public void setTexture(int unit, Texture tex) { setupTextureParams(unit, tex); } + + /** + * @deprecated Use modifyTexture(Texture2D dest, Image src, int destX, int destY, int srcX, int srcY, int areaW, int areaH) + */ + @Deprecated public void modifyTexture(Texture tex, Image pixels, int x, int y) { setTexture(0, tex); int target = convertTextureType(tex.getType(), pixels.getMultiSamples(), -1); - texUtil.uploadSubTexture(pixels, target, 0, x, y, linearizeSrgbImages); + texUtil.uploadSubTexture(target,pixels, 0, x, y,0,0,pixels.getWidth(),pixels.getHeight(), linearizeSrgbImages); + } + + /** + * Copy a part of an image to a texture 2d. + * @param dest The destination image, where the source will be copied + * @param src The source image that contains the data to copy + * @param destX First pixel of the destination image from where the src image will be drawn (x component) + * @param destY First pixel of the destination image from where the src image will be drawn (y component) + * @param srcX First pixel to copy (x component) + * @param srcY First pixel to copy (y component) + * @param areaW Width of the area to copy + * @param areaH Height of the area to copy + */ + public void modifyTexture(Texture2D dest, Image src, int destX, int destY, int srcX, int srcY, int areaW, int areaH) { + setTexture(0, dest); + int target = convertTextureType(dest.getType(), src.getMultiSamples(), -1); + texUtil.uploadSubTexture(target, src, 0, destX, destY, srcX, srcY, areaW, areaH, linearizeSrgbImages); } - + public void deleteImage(Image image) { int texId = image.getId(); if (texId != -1) { @@ -3136,6 +3217,7 @@ public long getProfilingTime(int taskId) { public boolean isTaskResultAvailable(int taskId) { return gl.glGetQueryObjectiv(taskId, GL.GL_QUERY_RESULT_AVAILABLE) == 1; } + @Override public boolean getAlphaToCoverage() { if (caps.contains(Caps.Multisample)) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java index db5331e110..7086eb0455 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java @@ -54,6 +54,7 @@ final class TextureUtil { private final GL2 gl2; private final GLExt glext; private GLImageFormat[][] formats; + private boolean supportUnpackRowLength; public TextureUtil(GL gl, GL2 gl2, GLExt glext) { this.gl = gl; @@ -62,6 +63,7 @@ public TextureUtil(GL gl, GL2 gl2, GLExt glext) { } public void initialize(EnumSet caps) { + supportUnpackRowLength = caps.contains(Caps.UnpackRowLength); this.formats = GLImageFormats.getFormatsForCaps(caps); if (logger.isLoggable(Level.FINE)) { StringBuilder sb = new StringBuilder(); @@ -142,13 +144,13 @@ private void uploadTextureLevel(GLImageFormat format, int target, int level, int if (target == GL2.GL_TEXTURE_3D) { // For 3D textures, we upload the entire mipmap level. gl2.glCompressedTexImage3D(target, - level, - format.internalFormat, - width, - height, - depth, - 0, - data); + level, + format.internalFormat, + width, + height, + depth, + 0, + data); } else if (target == GLExt.GL_TEXTURE_2D_ARRAY_EXT) { // For texture arrays, only upload 1 slice at a time. // zoffset specifies slice index, and depth is 1 to indicate @@ -298,6 +300,10 @@ public void uploadTexture(Image image, } } + /** + * @deprecated Use uploadSubTexture(int target, Image src, int index,int targetX, int targetY,int srcX,int srcY, int areaWidth,int areaHeight, boolean linearizeSrgb) + */ + @Deprecated public void uploadSubTexture(Image image, int target, int index, int x, int y, boolean linearizeSrgb) { if (target != GL.GL_TEXTURE_2D || image.getDepth() > 1) { throw new UnsupportedOperationException("Updating non-2D texture is not supported"); @@ -338,4 +344,63 @@ public void uploadSubTexture(Image image, int target, int index, int x, int y, b gl.glTexSubImage2D(target, 0, x, y, image.getWidth(), image.getHeight(), oglFormat.format, oglFormat.dataType, data); } + + public void uploadSubTexture(int target, Image src, int index, int targetX, int targetY, int areaX, int areaY, int areaWidth, int areaHeight, boolean linearizeSrgb) { + if (target != GL.GL_TEXTURE_2D || src.getDepth() > 1) { + throw new UnsupportedOperationException("Updating non-2D texture is not supported"); + } + + if (src.getMipMapSizes() != null) { + throw new UnsupportedOperationException("Updating mip-mappped images is not supported"); + } + + if (src.getMultiSamples() > 1) { + throw new UnsupportedOperationException("Updating multisampled images is not supported"); + } + + Image.Format jmeFormat = src.getFormat(); + + if (jmeFormat.isCompressed()) { + throw new UnsupportedOperationException("Updating compressed images is not supported"); + } else if (jmeFormat.isDepthFormat()) { + throw new UnsupportedOperationException("Updating depth images is not supported"); + } + + boolean getSrgbFormat = src.getColorSpace() == ColorSpace.sRGB && linearizeSrgb; + GLImageFormat oglFormat = getImageFormatWithError(jmeFormat, getSrgbFormat); + + ByteBuffer data = src.getData(index); + + if (data == null) { + throw new IndexOutOfBoundsException("The image index " + index + " is not valid for the given image"); + } + + int Bpp = src.getFormat().getBitsPerPixel() / 8; + + int srcWidth = src.getWidth(); + int cpos = data.position(); + int skip = areaX; + skip += areaY * srcWidth; + skip *= Bpp; + + data.position(skip); + + boolean needsStride = srcWidth != areaWidth; + + if (needsStride && (!supportUnpackRowLength)) { // doesn't support stride, copy row by row (slower). + for (int i = 0; i < areaHeight; i++) { + data.position(skip + (srcWidth * Bpp * i)); + gl.glTexSubImage2D(target, 0, targetX, targetY + i, areaWidth, 1, oglFormat.format, oglFormat.dataType, data); + } + } else { + if (needsStride) + gl2.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, srcWidth); + gl.glTexSubImage2D(target, 0, targetX, targetY, areaWidth, areaHeight, oglFormat.format, oglFormat.dataType, data); + if (needsStride) + gl2.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, 0); + } + data.position(cpos); + + } + } diff --git a/jme3-core/src/main/java/com/jme3/scene/SceneGraphVisitorAdapter.java b/jme3-core/src/main/java/com/jme3/scene/SceneGraphVisitorAdapter.java index 8fa419e750..7cde0d266b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/SceneGraphVisitorAdapter.java +++ b/jme3-core/src/main/java/com/jme3/scene/SceneGraphVisitorAdapter.java @@ -51,9 +51,9 @@ public void visit(Geometry geom) {} /** * Called when a {@link Node} is visited. * - * @param geom The visited node + * @param node The visited node */ - public void visit(Node geom) {} + public void visit(Node node) {} @Override public final void visit(Spatial spatial) { diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Line.java b/jme3-core/src/main/java/com/jme3/scene/shape/Line.java index e2ac6d85c3..93e667d7c5 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Line.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Line.java @@ -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 @@ -51,8 +51,11 @@ public class Line extends Mesh { private Vector3f start; private Vector3f end; - - public Line() { + + /** + * No-argument constructor needed by SavableClassUtil. + */ + public Line() { // TODO protected } public Line(Vector3f start, Vector3f end) { @@ -79,9 +82,16 @@ protected void updateGeometry(Vector3f start, Vector3f end) { } /** - * Update the start and end points of the line. + * Alter the start and end. + * + * @param start the desired mesh location of the start (not null, + * unaffected) + * @param end the desired mesh location of the end (not null, unaffected) */ public void updatePoints(Vector3f start, Vector3f end) { + this.start.set(start); + this.end.set(end); + VertexBuffer posBuf = getBuffer(Type.Position); FloatBuffer fb = (FloatBuffer) posBuf.getData(); diff --git a/jme3-core/src/main/java/com/jme3/shader/Glsl300ShaderGenerator.java b/jme3-core/src/main/java/com/jme3/shader/Glsl300ShaderGenerator.java new file mode 100644 index 0000000000..450a21a5ff --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shader/Glsl300ShaderGenerator.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009-2012 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.shader; + +import com.jme3.asset.AssetManager; +import com.jme3.material.ShaderGenerationInfo; +import com.jme3.shader.Shader.ShaderType; + + +/** + * This shader Generator can generate Vertex and Fragment shaders from + * ShaderNodes for GLESSL 3.0 + * Nowdays it's just a subclass of Glsl150ShaderGenerator overriding the version + * string because GLSL 1.5 is mostly compatible with GLESSL 3.0 + * + * @author Nehon + * @author Joliver82 + */ +public class Glsl300ShaderGenerator extends Glsl150ShaderGenerator { + + /** + * Creates a Glsl300ShaderGenerator + * + * @param assetManager the assetmanager + */ + public Glsl300ShaderGenerator(AssetManager assetManager) { + super(assetManager); + } + + @Override + protected String getLanguageAndVersion(ShaderType type) { + return "GLSL300"; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java index 26e79aacc1..71c4176327 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java @@ -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 @@ -169,8 +169,13 @@ public Platform getPlatform() { boolean is64 = is64Bit(arch); if (os.contains("windows")) { return is64 ? Platform.Windows64 : Platform.Windows32; - } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) { - return is64 ? Platform.Linux64 : Platform.Linux32; + } else if (os.contains("linux") || os.contains("freebsd") + || os.contains("sunos") || os.contains("unix")) { + if (arch.startsWith("arm")) { + return is64 ? Platform.Linux_ARM64 : Platform.Linux_ARM32; + } else { + return is64 ? Platform.Linux64 : Platform.Linux32; + } } else if (os.contains("mac os x") || os.contains("darwin")) { if (arch.startsWith("ppc")) { return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32; diff --git a/jme3-core/src/main/java/com/jme3/system/Platform.java b/jme3-core/src/main/java/com/jme3/system/Platform.java index f9206d33f8..1b80ff2e25 100644 --- a/jme3-core/src/main/java/com/jme3/system/Platform.java +++ b/jme3-core/src/main/java/com/jme3/system/Platform.java @@ -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 @@ -44,22 +44,32 @@ public enum Platform { Windows64(true), /** - * Linux 32 bit + * Linux 32-bit Intel */ Linux32, /** - * Linux 64 bit + * Linux 64-bit Intel */ Linux64(true), /** - * Apple Mac OS X 32 bit + * Linux 32-bit ARM + */ + Linux_ARM32, + + /** + * Linux 64-bit ARM + */ + Linux_ARM64(true), + + /** + * Apple Mac OS X 32-bit Intel */ MacOSX32, /** - * Apple Mac OS X 64 bit + * Apple Mac OS X 64-bit Intel */ MacOSX64(true), diff --git a/jme3-core/src/main/java/com/jme3/texture/Image.java b/jme3-core/src/main/java/com/jme3/texture/Image.java index eaa3aae8a4..f7cee1b55e 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Image.java +++ b/jme3-core/src/main/java/com/jme3/texture/Image.java @@ -494,6 +494,11 @@ public enum Format { * Requires {@link Caps#FloatTexture}. */ RG32F(64,true), + + /** + * 10-bit red, green, and blue with 2-bit alpha. + */ + RGB10A2(32), ; private int bpp; diff --git a/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java b/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java index bbad96ace8..993ce508b4 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java @@ -130,6 +130,10 @@ public ImageCodec(int bpp, int flags, int maxAlpha, int maxRed, int maxGreen, in 0, 11, 6, 1)); ((BitMaskImageCodec)params.get(Format.RGB5A1)).be = true; + params.put(Format.RGB10A2, new BitMaskImageCodec(4, 0, + 2, 10, 10, 10, + 0, 22, 12, 2)); + // params.put(Format.RGBA8, new ByteAlignedImageCodec(4, 0, // 0, 1, 1, 1, // 0, 0, 1, 2)); diff --git a/jme3-core/src/main/resources/Common/MatDefs/Blur/RadialBlur.j3md b/jme3-core/src/main/resources/Common/MatDefs/Blur/RadialBlur.j3md index e409926963..977007f073 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Blur/RadialBlur.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Blur/RadialBlur.j3md @@ -10,8 +10,8 @@ MaterialDef Radial Blur { } Technique { - VertexShader GLSL120 GLSL150: Common/MatDefs/Post/Post.vert - FragmentShader GLSL120 GLSL150: Common/MatDefs/Blur/RadialBlur.frag + VertexShader GLSL300 GLSL120 GLSL150: Common/MatDefs/Post/Post.vert + FragmentShader GLSL300 GLSL120 GLSL150: Common/MatDefs/Blur/RadialBlur.frag WorldParameters { } @@ -20,4 +20,4 @@ MaterialDef Radial Blur { RESOLVE_MS : NumSamples } } -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md index 824a173696..4a5fa256ee 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md @@ -133,8 +133,8 @@ MaterialDef Phong Lighting { Technique { LightMode SinglePass - VertexShader GLSL100 GLSL150: Common/MatDefs/Light/SPLighting.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Light/SPLighting.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/SPLighting.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/SPLighting.frag WorldParameters { WorldViewProjectionMatrix @@ -180,8 +180,8 @@ MaterialDef Phong Lighting { LightMode MultiPass - VertexShader GLSL100 GLSL150: Common/MatDefs/Light/Lighting.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Light/Lighting.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Lighting.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Lighting.frag WorldParameters { WorldViewProjectionMatrix @@ -225,8 +225,8 @@ MaterialDef Phong Lighting { Technique PreShadow { - VertexShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert - FragmentShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag WorldParameters { WorldViewProjectionMatrix @@ -255,8 +255,8 @@ MaterialDef Phong Lighting { Technique PostShadow { - VertexShader GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag WorldParameters { WorldViewProjectionMatrix @@ -292,8 +292,8 @@ MaterialDef Phong Lighting { Technique PreNormalPass { - VertexShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.vert - FragmentShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag WorldParameters { WorldViewProjectionMatrix @@ -315,8 +315,8 @@ MaterialDef Phong Lighting { Technique Glow { - VertexShader GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag WorldParameters { WorldViewProjectionMatrix diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index 077b08579f..4eaa6a88f5 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -296,7 +296,7 @@ void main(){ weight3 /= weightSum; #endif - #if USE_AMBIENT_LIGHT + #ifdef USE_AMBIENT_LIGHT color1.rgb *= g_AmbientLightColor.rgb; color2.rgb *= g_AmbientLightColor.rgb; color3.rgb *= g_AmbientLightColor.rgb; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md index 8465d2f0b2..40b4f9a6c7 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md @@ -123,8 +123,8 @@ MaterialDef PBR Lighting { Technique { LightMode SinglePassAndImageBased - VertexShader GLSL110 GLSL150: Common/MatDefs/Light/PBRLighting.vert - FragmentShader GLSL110 GLSL150: Common/MatDefs/Light/PBRLighting.frag + VertexShader GLSL300 GLSL110 GLSL150: Common/MatDefs/Light/PBRLighting.vert + FragmentShader GLSL300 GLSL110 GLSL150: Common/MatDefs/Light/PBRLighting.frag WorldParameters { WorldViewProjectionMatrix @@ -167,8 +167,8 @@ MaterialDef PBR Lighting { Technique PreShadow { - VertexShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert - FragmentShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag + VertexShader GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert + FragmentShader GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag WorldParameters { WorldViewProjectionMatrix @@ -197,8 +197,8 @@ MaterialDef PBR Lighting { Technique PostShadow{ - VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert - FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag + VertexShader GLSL310 GLSL300 GLSL150: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL310 GLSL300 GLSL150: Common/MatDefs/Shadow/PostShadow.frag WorldParameters { WorldViewProjectionMatrix diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md index f765d20445..4c609dca8d 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md @@ -62,8 +62,8 @@ MaterialDef Unshaded { } Technique { - VertexShader GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.frag WorldParameters { WorldViewProjectionMatrix @@ -88,8 +88,8 @@ MaterialDef Unshaded { Technique PreNormalPass { - VertexShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.vert - FragmentShader GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag WorldParameters { WorldViewProjectionMatrix @@ -109,8 +109,8 @@ MaterialDef Unshaded { Technique PreShadow { - VertexShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert - FragmentShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag WorldParameters { WorldViewProjectionMatrix @@ -140,8 +140,8 @@ MaterialDef Unshaded { Technique PostShadow { - VertexShader GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag WorldParameters { WorldViewProjectionMatrix @@ -176,8 +176,8 @@ MaterialDef Unshaded { Technique Glow { - VertexShader GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag WorldParameters { WorldViewProjectionMatrix @@ -196,4 +196,4 @@ MaterialDef Unshaded { NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers } } -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping.j3sn b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping.j3sn index 148c88f1c5..529dea4bae 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping.j3sn +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping.j3sn @@ -2,6 +2,7 @@ ShaderNodeDefinitions{ ShaderNodeDefinition EnvMapping { Type: Fragment + Shader GLSL300: Common/MatDefs/ShaderNodes/Environment/envMapping130.frag Shader GLSL100: Common/MatDefs/ShaderNodes/Environment/envMapping100.frag Shader GLSL130: Common/MatDefs/ShaderNodes/Environment/envMapping130.frag @@ -19,4 +20,4 @@ ShaderNodeDefinitions{ vec4 color } } -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping100.frag b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping100.frag index 9d40ec523b..29932c7c5a 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping100.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Environment/envMapping100.frag @@ -1,8 +1,17 @@ #extension GL_ARB_shader_texture_lod : enable +#extension GL_EXT_shader_texture_lod : enable void main(){ //@input vec3 refVec the reflection vector //@input samplerCube cubeMap the cube map //@output vec4 color the output color - color = textureCubeLod(cubeMap, refVec, 0.0); -} \ No newline at end of file + #ifdef GL_ES + #ifdef GL_EXT_shader_texture_lod + color = textureCubeLodEXT(cubeMap, refVec, 0.0); + #else + color = textureCube(cubeMap, refVec); + #endif + #else + color = textureCubeLod(cubeMap, refVec, 0.0); + #endif +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md index fd8ea8a24d..ce588b60af 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md @@ -35,8 +35,8 @@ MaterialDef Post Shadow { } Technique { - VertexShader GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag + VertexShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag WorldParameters { WorldViewProjectionMatrix @@ -61,4 +61,4 @@ MaterialDef Post Shadow { } } -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md index 0023b0b813..05188796c6 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md @@ -42,8 +42,8 @@ MaterialDef Post Shadow { } Technique { - VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter.vert - FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag + VertexShader GLSL310 GLSL150: Common/MatDefs/Shadow/PostShadowFilter.vert + FragmentShader GLSL310 GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag WorldParameters { ResolutionInverse @@ -88,4 +88,4 @@ MaterialDef Post Shadow { -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib index c1ace91d12..697db195d5 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib @@ -19,13 +19,18 @@ #endif #if __VERSION__ >= 130 +# ifdef GL_ES +out highp vec4 outFragColor; +# else out vec4 outFragColor; +#endif # define texture1D texture # define texture2D texture # define texture3D texture # define textureCube texture # define texture2DLod textureLod # define textureCubeLod textureLod +# define texture2DArray texture # if defined VERTEX_SHADER # define varying out # define attribute in @@ -33,4 +38,8 @@ out vec4 outFragColor; # define varying in # define gl_FragColor outFragColor # endif -#endif \ No newline at end of file +#else +# define isnan(val) !(val<0.0||val>0.0||val==0.0) +#endif + + diff --git a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib index 4ecd331576..8dc1e2a728 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib @@ -16,7 +16,7 @@ uniform int m_NumSamplesDepth; #endif // NOTE: Only define multisample functions if multisample is available -#if defined(GL_ARB_texture_multisample) +#if defined(GL_ARB_texture_multisample) || (defined GL_ES && __VERSION__>=310) vec4 textureFetch(in sampler2DMS tex,in vec2 texC, in int numSamples){ ivec2 iTexC = ivec2(texC * vec2(textureSize(tex))); vec4 color = vec4(0.0); diff --git a/jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib index c7c6b88518..eb3380ce1f 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib @@ -1,16 +1,23 @@ #if __VERSION__ >= 130 // Because gpu_shader5 is actually where those // gather functions are declared to work on shadowmaps - #extension GL_ARB_gpu_shader5 : enable + // This "if" statement is useless as jme3 changes line ordering, so all extensions are tried to be loaded + #ifdef GL_ES + #extension GL_OES_gpu_shader5 : enable + #else + #extension GL_ARB_gpu_shader5 : enable + #endif + #define IVEC2 ivec2 - #if defined GL_ES - #define SHADOWMAP sampler2D - #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) - #elif defined HARDWARE_SHADOWS + #ifdef HARDWARE_SHADOWS #define SHADOWMAP sampler2DShadow #define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset) #define SHADOWCOMPARE(tex,coord) textureProj(tex, coord) - #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) + #if defined GL_ES && __VERSION__ <= 300 + #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy)) + #else + #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) + #endif #else #define SHADOWMAP sampler2D #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r) @@ -31,7 +38,10 @@ #endif #else #define IVEC2 vec2 - #ifdef HARDWARE_SHADOWS + #if defined GL_ES + #define SHADOWMAP sampler2D + #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) + #elif defined HARDWARE_SHADOWS #define SHADOWMAP sampler2DShadow #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r #else @@ -91,10 +101,14 @@ float Shadow_DoShadowCompare(in SHADOWMAP tex,in vec4 projCoord){ } float Shadow_BorderCheck(in vec2 coord){ - // Fastest, "hack" method (uses 4-5 instructions) - vec4 t = vec4(coord.xy, 0.0, 1.0); - t = step(t.wwxy, t.xyzz); - return dot(t,t); + #ifdef GL_ES + return 0.0; + #else + // Fastest, "hack" method (uses 4-5 instructions) + vec4 t = vec4(coord.xy, 0.0, 1.0); + t = step(t.wwxy, t.xyzz); + return dot(t,t); + #endif } float Shadow_Nearest(in SHADOWMAP tex,in vec4 projCoord){ @@ -117,7 +131,8 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ return 1.0; float shadow = 0.0; - IVEC2 o = IVEC2(mod(floor(gl_FragCoord.xy), 2.0)); + //IVEC2 o = IVEC2(mod(floor(gl_FragCoord.xy), 2.0)); + vec2 o = vec2(IVEC2(mod(floor(gl_FragCoord.xy), 2.0))); //Strict type checking in GLSL ES shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2(-1.5, 1.5)+o)); shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2( 0.5, 1.5)+o)); shadow += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2(-1.5, -0.5)+o)); @@ -134,7 +149,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ vec4 gather = vec4(0.0); #if __VERSION__ >= 130 - #ifdef GL_ARB_gpu_shader5 + #if defined GL_ARB_gpu_shader5 || defined GL_OES_gpu_shader5 vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0); gather = SHADOWGATHER(tex, coord); #else diff --git a/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java b/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java index d4c24054dc..93f082337b 100644 --- a/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java +++ b/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java @@ -345,16 +345,7 @@ public Savable readObject(int id) { return out; - } catch (IOException e) { - logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e); - return null; - } catch (ClassNotFoundException e) { - logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e); - return null; - } catch (InstantiationException e) { - logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e); - return null; - } catch (IllegalAccessException e) { + } catch (Exception e) { logger.logp(Level.SEVERE, this.getClass().toString(), "readObject(int id)", "Exception", e); return null; } diff --git a/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java b/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java index ce3264b2d5..3c4dd91300 100644 --- a/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java @@ -191,7 +191,7 @@ public Object load(AssetInfo info) throws IOException { if (info.getKey() instanceof ShaderAssetKey) { injectDependencies = ((ShaderAssetKey) info.getKey()).isInjectDependencies(); } - if (info.getKey().getExtension().equals("glsllib")) { + if (info.getKey().getExtension().equals("glsllib")||info.getKey().getExtension().equals("glsl")) { // NOTE: Loopback, GLSLLIB is loaded by this loader // and needs data as InputStream return reader; diff --git a/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy b/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy new file mode 100644 index 0000000000..45e39c1b79 --- /dev/null +++ b/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy @@ -0,0 +1,98 @@ +package com.jme3.app.state; + +import com.jme3.app.LegacyApplication; + +class AppStateManagerTest { + + static class AttachTest extends GroovyTestCase { + + void testDuplicateId() { + def state1 = new AbstractAppState("test1") {}; + def state2 = new AbstractAppState("test1") {}; + + def app = new LegacyApplication(); + + app.getStateManager().attach(state1); + + shouldFail(IllegalArgumentException) { + app.getStateManager().attach(state2); + } + } + + void testDuplicateNullId() { + // Make sure that two states without an ID can + // still be registered. + def state1 = new AbstractAppState() {}; + def state2 = new AbstractAppState() {}; + + def app = new LegacyApplication(); + + app.getStateManager().attach(state1); + app.getStateManager().attach(state2); + } + } + + static class GetStateWithIdTest extends GroovyTestCase { + void testIdHit() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertNotNull app.stateManager.getState("test1", AppState.class); + } + + void testIdMiss() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertNull app.stateManager.getState("test2", AppState.class); + } + + void testDetached() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + app.stateManager.detach(state); + + assertNull app.stateManager.getState("test2", AppState.class); + } + } + + static class StateForIdTest extends GroovyTestCase { + void testIdHit() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertNotNull app.stateManager.stateForId("test1", AppState.class); + } + + void testIdMiss() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + shouldFail(IllegalArgumentException) { + app.stateManager.stateForId("test2", AppState.class); + } + } + + void testDetached() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + app.stateManager.detach(state); + + shouldFail(IllegalArgumentException) { + app.stateManager.stateForId("test2", AppState.class); + } + } + } +} diff --git a/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java b/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java index 0007bca2c6..e4f3e57a8b 100644 --- a/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java +++ b/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java @@ -46,6 +46,7 @@ public void testLoadShaderSource() { assetManager.registerLocator(null, ClasspathLocator.class); assetManager.registerLoader(GLSLLoader.class, "frag"); assetManager.registerLoader(GLSLLoader.class, "glsllib"); + assetManager.registerLoader(GLSLLoader.class, "glsl"); String showNormals = (String) assetManager.loadAsset("Common/MatDefs/Misc/ShowNormals.frag"); System.out.println(showNormals); } diff --git a/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java b/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java index 98d379b38a..ad4bf1dd18 100644 --- a/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java +++ b/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java @@ -31,7 +31,7 @@ private static void initAssetManager(){ assetManager.registerLocator("/", ClasspathLocator.class); assetManager.registerLoader(J3MLoader.class, "j3m"); assetManager.registerLoader(J3MLoader.class, "j3md"); - assetManager.registerLoader(GLSLLoader.class, "vert", "frag","geom","tsctrl","tseval","glsllib"); + assetManager.registerLoader(GLSLLoader.class, "vert", "frag","geom","tsctrl","tseval","glsllib","glsl"); } private static void checkMatDef(String matdefName) { diff --git a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java index c5faa0a74e..1cba8bcf28 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java +++ b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java @@ -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 @@ -175,6 +175,8 @@ public static void registerNativeLibrary(String name, Platform platform, registerNativeLibrary("bulletjme", Platform.Windows64, "native/windows/x86_64/bulletjme.dll"); registerNativeLibrary("bulletjme", Platform.Linux32, "native/linux/x86/libbulletjme.so"); registerNativeLibrary("bulletjme", Platform.Linux64, "native/linux/x86_64/libbulletjme.so"); + registerNativeLibrary("bulletjme", Platform.Linux_ARM32, "native/linux/arm32/libbulletjme.so"); + registerNativeLibrary("bulletjme", Platform.Linux_ARM64, "native/linux/arm64/libbulletjme.so"); registerNativeLibrary("bulletjme", Platform.MacOSX32, "native/osx/x86/libbulletjme.dylib"); registerNativeLibrary("bulletjme", Platform.MacOSX64, "native/osx/x86_64/libbulletjme.dylib"); diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/FXAA.j3md b/jme3-effects/src/main/resources/Common/MatDefs/Post/FXAA.j3md index 68f329892b..2debc760c7 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/FXAA.j3md +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/FXAA.j3md @@ -8,10 +8,10 @@ MaterialDef FXAA { Float ReduceMul } Technique { - VertexShader GLSL100 GLSL150: Common/MatDefs/Post/FXAA.vert - FragmentShader GLSL100 GLSL150: Common/MatDefs/Post/FXAA.frag + VertexShader GLSL300 GLSL100 GLSL150: Common/MatDefs/Post/FXAA.vert + FragmentShader GLSL300 GLSL100 GLSL150: Common/MatDefs/Post/FXAA.frag WorldParameters { ResolutionInverse } } -} \ No newline at end of file +} diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Post/LightScattering.j3md b/jme3-effects/src/main/resources/Common/MatDefs/Post/LightScattering.j3md index 613955536b..590dbdc051 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Post/LightScattering.j3md +++ b/jme3-effects/src/main/resources/Common/MatDefs/Post/LightScattering.j3md @@ -14,8 +14,8 @@ MaterialDef Light Scattering { } Technique { - VertexShader GLSL150 GLSL120: Common/MatDefs/Post/Post.vert - FragmentShader GLSL150 GLSL120: Common/MatDefs/Post/LightScattering.frag + VertexShader GLSL300 GLSL150 GLSL120: Common/MatDefs/Post/Post.vert + FragmentShader GLSL300 GLSL150 GLSL120: Common/MatDefs/Post/LightScattering.frag WorldParameters { } @@ -26,4 +26,4 @@ MaterialDef Light Scattering { DISPLAY: Display } } -} \ No newline at end of file +} diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md index 6cf780e1f1..0f5b5433e7 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md @@ -18,8 +18,8 @@ MaterialDef SSAO { } Technique { - VertexShader GLSL150 GLSL120: Common/MatDefs/Post/Post.vert - FragmentShader GLSL150 GLSL120: Common/MatDefs/SSAO/ssao.frag + VertexShader GLSL300 GLSL150 GLSL120: Common/MatDefs/Post/Post.vert + FragmentShader GLSL300 GLSL150 GLSL120: Common/MatDefs/SSAO/ssao.frag WorldParameters { WorldViewMatrix @@ -34,4 +34,4 @@ MaterialDef SSAO { } } -} \ No newline at end of file +} diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.frag b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.frag index ef6b278ec5..aaa7bd8acb 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.frag @@ -32,77 +32,77 @@ vec4 convolutionFilter(){ float zsum = 1.0; float Zp =readDepth(texCoord); - vec2 sample = vec2(x - 2.0 * xScale, y - 2.0 * yScale); - float zTmp = readDepth(sample); + vec2 samplePos = vec2(x - 2.0 * xScale, y - 2.0 * yScale); + float zTmp = readDepth(samplePos); float coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x - 0.0 * xScale, y - 2.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x - 0.0 * xScale, y - 2.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x + 2.0 * xScale, y - 2.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x + 2.0 * xScale, y - 2.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x - 1.0 * xScale, y - 1.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x - 1.0 * xScale, y - 1.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x + 1.0 * xScale, y - 1.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x + 1.0 * xScale, y - 1.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x - 2.0 * xScale, y - 0.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x - 2.0 * xScale, y - 0.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x + 2.0 * xScale, y - 0.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x + 2.0 * xScale, y - 0.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x - 1.0 * xScale, y + 1.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x - 1.0 * xScale, y + 1.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x + 1.0 * xScale, y + 1.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x + 1.0 * xScale, y + 1.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x - 2.0 * xScale, y + 2.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x - 2.0 * xScale, y + 2.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x - 0.0 * xScale, y + 2.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x - 0.0 * xScale, y + 2.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); - sample = vec2(x + 2.0 * xScale, y + 2.0 * yScale); - zTmp =readDepth(sample); + samplePos = vec2(x + 2.0 * xScale, y + 2.0 * yScale); + zTmp =readDepth(samplePos); coefZ = 1.0 / (epsilon + abs(Zp - zTmp)); zsum += coefZ; - sum += coefZ* texture2D( m_SSAOMap, sample); + sum += coefZ* texture2D( m_SSAOMap, samplePos); return sum / zsum; @@ -124,4 +124,4 @@ vec4 getColor(vec4 color){ void main(){ gl_FragColor = getColor(convolutionFilter()); -} \ No newline at end of file +} diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.j3md b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.j3md index f5a1892cab..2314876d6d 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.j3md +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur.j3md @@ -14,8 +14,8 @@ MaterialDef SSAOBlur { } Technique { - VertexShader GLSL150 GLSL120: Common/MatDefs/Post/Post.vert - FragmentShader GLSL150 GLSL120: Common/MatDefs/SSAO/ssaoBlur.frag + VertexShader GLSL300 GLSL150 GLSL120: Common/MatDefs/Post/Post.vert + FragmentShader GLSL300 GLSL150 GLSL120: Common/MatDefs/SSAO/ssaoBlur.frag WorldParameters { WorldViewMatrix @@ -28,4 +28,4 @@ MaterialDef SSAOBlur { RESOLVE_DEPTH_MS : NumSamplesDepth } } -} \ No newline at end of file +} diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Water/Textures/water_normalmap.dds b/jme3-effects/src/main/resources/Common/MatDefs/Water/Textures/water_normalmap.dds index 13582e148c..529b8327b4 100644 Binary files a/jme3-effects/src/main/resources/Common/MatDefs/Water/Textures/water_normalmap.dds and b/jme3-effects/src/main/resources/Common/MatDefs/Water/Textures/water_normalmap.dds differ diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.frag b/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.frag index c52a8d6921..d97e23dcb0 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.frag @@ -48,9 +48,8 @@ uniform vec3 m_Center; uniform float m_Radius; #endif - -vec2 scale = vec2(m_WaveScale, m_WaveScale); -float refractionScale = m_WaveScale; +vec2 scale; // = vec2(m_WaveScale, m_WaveScale); +float refractionScale; // = m_WaveScale; // Modifies 4 sampled normals. Increase first values to have more // smaller "waves" or last to have more bigger "waves" @@ -62,8 +61,18 @@ const float visibility = 3.0; // foam intensity uniform float m_FoamIntensity ; +vec2 m_FrustumNearFar; //=vec2(1.0,m_UnderWaterFogDistance); +const float LOG2 = 1.442695; + + varying vec2 texCoord; +void setGlobals(){ +scale = vec2(m_WaveScale, m_WaveScale); +refractionScale = m_WaveScale; +m_FrustumNearFar=vec2(1.0,m_UnderWaterFogDistance); +} + mat3 MatrixInverse(in mat3 inMatrix){ float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]); mat3 T = transpose(inMatrix); @@ -119,9 +128,6 @@ float fresnelTerm(in vec3 normal,in vec3 eyeVec){ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength); } -vec2 m_FrustumNearFar=vec2(1.0,m_UnderWaterFogDistance); -const float LOG2 = 1.442695; - vec4 underWater(int sampleNum){ @@ -225,7 +231,7 @@ vec4 underWater(int sampleNum){ #endif float fogDepth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - sceneDepth* (m_FrustumNearFar.y-m_FrustumNearFar.x)); - float fogIntensity= 18 * m_WaterTransparency; + float fogIntensity= 18.0 * m_WaterTransparency; fogFactor = exp2( -fogIntensity * fogIntensity * fogDepth * fogDepth * LOG2 ); fogFactor = clamp(fogFactor, 0.0, 1.0); color =mix(m_DeepWaterColor.rgb,color,fogFactor); @@ -284,7 +290,7 @@ vec4 main_multiSample(int sampleNum){ samples = 10; #endif - float biasFactor = 1.0 / samples; + float biasFactor = 1.0 / float(samples); for (int i = 0; i < samples; i++){ texC = (surfacePoint.xz + eyeVecNorm.xz * biasFactor) * scale + m_Time * 0.03 * m_WindDirection; @@ -344,7 +350,7 @@ vec4 main_multiSample(int sampleNum){ normal = normalize(normal0a * normalModifier.x + normal1a * normalModifier.y +normal2a * normalModifier.z + normal3a * normalModifier.w); - #if __VERSION__ >= 130 + #if __VERSION__ >= 130 && !defined GL_ES // XXX: Here's another way to fix the terrain edge issue, // But it requires GLSL 1.3 and still looks kinda incorrect // around edges @@ -435,13 +441,14 @@ vec4 main_multiSample(int sampleNum){ } void main(){ + setGlobals(); #ifdef RESOLVE_MS vec4 color = vec4(0.0); for (int i = 0; i < m_NumSamples; i++){ color += main_multiSample(i); } - gl_FragColor = color / m_NumSamples; + gl_FragColor = color / float(m_NumSamples); #else gl_FragColor = main_multiSample(0); #endif -} \ No newline at end of file +} diff --git a/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.j3md b/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.j3md index b75bd2c45f..49c5a06e53 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.j3md +++ b/jme3-effects/src/main/resources/Common/MatDefs/Water/Water.j3md @@ -51,8 +51,8 @@ MaterialDef Advanced Water { } Technique { - VertexShader GLSL150 GLSL120 : Common/MatDefs/Post/Post.vert - FragmentShader GLSL150 GLSL120: Common/MatDefs/Water/Water.frag + VertexShader GLSL310 GLSL300 GLSL150 GLSL120 : Common/MatDefs/Post/Post.vert + FragmentShader GLSL310 GLSL300 GLSL150 GLSL120: Common/MatDefs/Water/Water.frag WorldParameters { ViewProjectionMatrixInverse @@ -72,4 +72,4 @@ MaterialDef Advanced Water { } } -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/java/jme3test/animation/TestIssue1138.java b/jme3-examples/src/main/java/jme3test/animation/TestIssue1138.java new file mode 100644 index 0000000000..ac390aaf51 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/animation/TestIssue1138.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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 jme3test.animation; + +import com.jme3.anim.AnimComposer; +import com.jme3.anim.Joint; +import com.jme3.anim.SkinningControl; +import com.jme3.app.SimpleApplication; +import com.jme3.light.AmbientLight; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; + +/** + * Test case for JME issue #1138: Elephant's legUp animation sets Joint + * translation to NaN. + *

+ * If successful, the animation cycle will complete without throwing an + * IllegalStateException. + * + * @author Stephen Gold + */ +public class TestIssue1138 extends SimpleApplication { + + SkinningControl sControl; + + public static void main(String... argv) { + new TestIssue1138().start(); + } + + @Override + public void simpleInitApp() { + Node cgModel = (Node) assetManager.loadModel( + "Models/Elephant/Elephant.mesh.xml"); + rootNode.attachChild(cgModel); + cgModel.rotate(0f, -1f, 0f); + cgModel.scale(0.04f); + + AnimComposer composer = cgModel.getControl(AnimComposer.class); + composer.setCurrentAction("legUp"); + sControl = cgModel.getControl(SkinningControl.class); + + AmbientLight light = new AmbientLight(); + rootNode.addLight(light); + } + + @Override + public void simpleUpdate(float tpf) { + for (Joint joint : sControl.getArmature().getJointList()) { + Vector3f translation = joint.getLocalTranslation(); + if (!Vector3f.isValidVector(translation)) { + String msg = "Invalid translation for joint " + joint.getName(); + throw new IllegalStateException(msg); + } + } + } +} diff --git a/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java b/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java index b6517c9d2b..3bbc316b4e 100644 --- a/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java +++ b/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java @@ -64,7 +64,10 @@ public void simpleInitApp() { rootNode.attachChild(geom); System.out.println("Attaching test state."); - stateManager.attach(new TestState()); + stateManager.attach(new TestState()); + + System.out.println("Attaching test state with an ID."); + stateManager.attach(new TestState("Test ID")); } @Override @@ -74,51 +77,64 @@ public void simpleUpdate(float tpf) { System.out.println("Detaching test state."); stateManager.detach(stateManager.getState(TestState.class)); System.out.println("Done"); - } + } + + if( stateManager.hasState("Test ID") ) { + System.out.println("Detaching test state with an ID."); + stateManager.detach(stateManager.getState("Test ID", TestState.class)); + System.out.println("Done"); + } } public class TestState extends AbstractAppState { + public TestState() { + } + + public TestState( String id ) { + super(id); + } + @Override public void initialize(AppStateManager stateManager, Application app) { super.initialize(stateManager, app); - System.out.println("Initialized"); + System.out.println("Initialized, id:" + getId()); } @Override public void stateAttached(AppStateManager stateManager) { super.stateAttached(stateManager); - System.out.println("Attached"); + System.out.println("Attached, id:" + getId()); } @Override public void update(float tpf) { super.update(tpf); - System.out.println("update"); + System.out.println("update, id:" + getId()); } @Override public void render(RenderManager rm) { super.render(rm); - System.out.println("render"); + System.out.println("render, id:" + getId()); } @Override public void postRender() { super.postRender(); - System.out.println("postRender"); + System.out.println("postRender, id:" + getId()); } @Override public void stateDetached(AppStateManager stateManager) { super.stateDetached(stateManager); - System.out.println("Detached"); + System.out.println("Detached, id:" + getId()); } @Override public void cleanup() { super.cleanup(); - System.out.println("Cleanup"); + System.out.println("Cleanup, id:" + getId()); } } diff --git a/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java b/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java index 3c919268a2..484f0f1774 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java +++ b/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java @@ -106,7 +106,7 @@ public class TestGimpactShape extends SimpleApplication { public static void main(String[] args) { test = new TestGimpactShape(); test.setSettings(new AppSettings(true)); - test.settings.setFrameRate(60); + test.settings.setVSync(true); if (SKIP_SETTINGS) { test.settings.setWidth(1920); test.settings.setHeight(1150); @@ -117,6 +117,7 @@ public static void main(String[] args) { @Override public void simpleInitApp() { + test = this; getCamera().setLocation(new Vector3f(40, 30, 160)); getCamera().lookAt(new Vector3f(40, -5, 0), Vector3f.UNIT_Y); getFlyByCamera().setMoveSpeed(25); diff --git a/jme3-examples/src/main/java/jme3test/gui/TestRtlBitmapText.java b/jme3-examples/src/main/java/jme3test/gui/TestRtlBitmapText.java new file mode 100644 index 0000000000..ed65c4a071 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/gui/TestRtlBitmapText.java @@ -0,0 +1,65 @@ +/* + * 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 jme3test.gui; + +import com.jme3.app.SimpleApplication; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.font.LineWrapMode; +import com.jme3.font.Rectangle; + +/** + * Test case for JME issue #1158: BitmapText right to left line wrapping not work + */ +public class TestRtlBitmapText extends SimpleApplication { + + // A right to left text. + private String text = ".text left to right test a is This"; + + public static void main(String[] args) { + TestRtlBitmapText app = new TestRtlBitmapText(); + app.start(); + } + + @Override + public void simpleInitApp() { + BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt"); + // A right to left BitmapText + BitmapText txt = new BitmapText(fnt, true); + txt.setBox(new Rectangle(0, 0, 150, 0)); + txt.setLineWrapMode(LineWrapMode.Word); + txt.setAlignment(BitmapFont.Align.Right); + txt.setText(text); + txt.setLocalTranslation(cam.getWidth() / 2, cam.getHeight() / 2, 0); + guiNode.attachChild(txt); + } +} diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java index 6822251ba7..faa67fd137 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java @@ -64,6 +64,9 @@ import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; +import java.util.ArrayList; +import java.util.List; + /** * Creates a terrain object and a collision node to go with it. Then * drops several balls from the sky that collide with the terrain @@ -83,7 +86,7 @@ public class TerrainTestCollision extends SimpleApplication { protected BitmapText hintText; PointLight pl; Geometry lightMdl; - Geometry collisionMarker; + List collisionMarkers; private BulletAppState bulletAppState; Geometry collisionSphere; Geometry collisionBox; @@ -103,6 +106,7 @@ public void initialize() { @Override public void simpleInitApp() { + collisionMarkers = new ArrayList<>(); bulletAppState = new BulletAppState(); bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); stateManager.attach(bulletAppState); @@ -142,6 +146,8 @@ public void simpleInitApp() { terrain.setLocked(false); // unlock it so we can edit the height rootNode.attachChild(terrain); + // if set to false, only the first collision is returned and collision is slightly faster. + terrain.setSupportMultipleCollisions(true); /** * Create PhysicsRigidBodyControl for collision @@ -227,15 +233,19 @@ public void update() { super.update(); } - private void createCollisionMarker() { - Sphere s = new Sphere(6, 6, 1); - collisionMarker = new Geometry("collisionMarker"); - collisionMarker.setMesh(s); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Orange); - collisionMarker.setMaterial(mat); - rootNode.attachChild(collisionMarker); + private void createCollisionMarkers(int num) { + for (int i = 0; i < num; i++) { + Sphere s = new Sphere(6, 6, 1); + Geometry collisionMarker = new Geometry("collisionMarker"); + collisionMarker.setMesh(s); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", i == 0 ? ColorRGBA.Orange : ColorRGBA.Blue); + collisionMarker.setMaterial(mat); + rootNode.attachChild(collisionMarker); + collisionMarkers.add(collisionMarker); + } } + private ActionListener actionListener = new ActionListener() { public void onAction(String binding, boolean keyPressed, float tpf) { @@ -247,24 +257,35 @@ public void onAction(String binding, boolean keyPressed, float tpf) { terrain.setMaterial(matRock); } } else if (binding.equals("shoot") && !keyPressed) { - Vector3f origin = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.0f); Vector3f direction = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.3f); direction.subtractLocal(origin).normalizeLocal(); - Ray ray = new Ray(origin, direction); CollisionResults results = new CollisionResults(); - int numCollisions = terrain.collideWith(ray, results); - if (numCollisions > 0) { - CollisionResult hit = results.getClosestCollision(); - if (collisionMarker == null) { - createCollisionMarker(); + + if (terrain.collideWith(ray, results) > 0) { + CollisionResult hit = results.getClosestCollision(); // sorts the collection before printing + printCollisions(results); + + // Remove old markers. + for (Geometry g: collisionMarkers) { + g.removeFromParent(); } + collisionMarkers.clear(); + + createCollisionMarkers(results.size()); + + // Position Closest Collision Vector2f loc = new Vector2f(hit.getContactPoint().x, hit.getContactPoint().z); float height = terrain.getHeight(loc); - System.out.println("collide " + hit.getContactPoint() + ", height: " + height + ", distance: " + hit.getDistance()); - collisionMarker.setLocalTranslation(new Vector3f(hit.getContactPoint().x, height, hit.getContactPoint().z)); + System.out.println("Closest Collision: " + hit.getContactPoint() + ", height: " + height + ", distance: " + hit.getDistance()); + collisionMarkers.get(0).setLocalTranslation(new Vector3f(hit.getContactPoint().x, height, hit.getContactPoint().z)); + + // Position Rest: When getClosestCollision has been called, the results are sorted, and thus 0 is closest. + for (int i = 1; i < results.size(); i++) { + collisionMarkers.get(i).setLocalTranslation(results.getCollision(i).getContactPoint()); + } } } else if (binding.equals("cameraDown") && !keyPressed) { getCamera().lookAtDirection(new Vector3f(0, -1, 0), Vector3f.UNIT_Y); @@ -302,4 +323,14 @@ private void testCollision(Vector3f oldLoc) { selectedCollisionObject.setLocalTranslation(oldLoc); } } + + private void printCollisions(CollisionResults cr) { + System.out.println("================ Collision Results ================"); + for (int i = 0; i < cr.size(); i++) { + CollisionResult res = cr.getCollision(i); + System.out.println("Result " + i); + System.out.println("\t\t" + res.toString()); + } + System.out.println("================ END Collision Results ================"); + } } diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java b/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java index a7e553a9a1..1e151bf741 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java @@ -128,4 +128,4 @@ private Texture getTexture() throws IOException { data.add(bb); return new Texture3D(new Image(Format.RGB8, 10, 10, 10, data, null, ColorSpace.Linear)); } -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java b/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java index 391e4efcc6..1757c021bd 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java @@ -84,4 +84,4 @@ public static void main(String[] args) app.start(); } -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java b/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java index 2187c58d87..738f4b94c2 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java @@ -31,8 +31,8 @@ public void simpleInitApp() } - Texture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond.dds"); - Texture tex2 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.dds"); + Texture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond_dxt5.dds"); + Texture tex2 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_dxt5.dds"); List images = new ArrayList(); images.add(tex1.getImage()); images.add(tex2.getImage()); @@ -84,4 +84,4 @@ public static void main(String[] args) app.start(); } -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.frag b/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.frag index 355cf60928..4b888e81d8 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.frag +++ b/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.frag @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + #extension GL_EXT_texture_array : enable // #extension GL_EXT_gpu_shader4 : enable @@ -8,7 +10,7 @@ uniform vec4 m_Color; #endif #ifdef HAS_COLORMAP - #if !defined(GL_EXT_texture_array) + #if !defined(GL_EXT_texture_array) && __VERSION__ < 130 #error Texture arrays are not supported, but required for this shader. #endif @@ -54,4 +56,4 @@ void main(){ #endif gl_FragColor = color; -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.j3md b/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.j3md index 6a3219e602..bcbee23d89 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.j3md +++ b/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.j3md @@ -14,8 +14,8 @@ MaterialDef Unshaded { } Technique { - VertexShader GLSL100: jme3test/texture/UnshadedArray.vert - FragmentShader GLSL100: jme3test/texture/UnshadedArray.frag + VertexShader GLSL300 GLSL100: jme3test/texture/UnshadedArray.vert + FragmentShader GLSL300 GLSL100: jme3test/texture/UnshadedArray.frag WorldParameters { WorldViewProjectionMatrix @@ -32,8 +32,8 @@ MaterialDef Unshaded { Technique PreNormalPass { - VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert - FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag + VertexShader GLSL300 GLSL100 : Common/MatDefs/SSAO/normal.vert + FragmentShader GLSL300 GLSL100 : Common/MatDefs/SSAO/normal.frag WorldParameters { WorldViewProjectionMatrix @@ -50,8 +50,8 @@ MaterialDef Unshaded { Technique Glow { - VertexShader GLSL100: Cjme3test/texture/UnshadedArray.vert - FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag + VertexShader GLSL300 GLSL100: jme3test/texture/UnshadedArray.vert + FragmentShader GLSL300 GLSL100: Common/MatDefs/Light/Glow.frag WorldParameters { WorldViewProjectionMatrix @@ -63,4 +63,4 @@ MaterialDef Unshaded { HAS_COLORMAP // Must be passed so that Unshaded.vert exports texCoord. } } -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.vert b/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.vert index 9d1153c3d4..3f85e3063b 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.vert +++ b/jme3-examples/src/main/resources/jme3test/texture/UnshadedArray.vert @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + uniform mat4 g_WorldViewProjectionMatrix; attribute vec3 inPosition; diff --git a/jme3-examples/src/main/resources/jme3test/texture/tex3D.frag b/jme3-examples/src/main/resources/jme3test/texture/tex3D.frag index 55862acf93..2e811ad03b 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/tex3D.frag +++ b/jme3-examples/src/main/resources/jme3test/texture/tex3D.frag @@ -1,7 +1,9 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + uniform sampler3D m_Texture; varying vec3 texCoord; void main(){ gl_FragColor= texture3D(m_Texture,texCoord); -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/resources/jme3test/texture/tex3D.j3md b/jme3-examples/src/main/resources/jme3test/texture/tex3D.j3md index 1ba2605948..61b35c99aa 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/tex3D.j3md +++ b/jme3-examples/src/main/resources/jme3test/texture/tex3D.j3md @@ -5,8 +5,8 @@ MaterialDef My MaterialDef { } Technique { - VertexShader GLSL100: jme3test/texture/tex3D.vert - FragmentShader GLSL100: jme3test/texture/tex3D.frag + VertexShader GLSL300 GLSL100: jme3test/texture/tex3D.vert + FragmentShader GLSL300 GLSL100: jme3test/texture/tex3D.frag WorldParameters { WorldViewProjectionMatrix diff --git a/jme3-examples/src/main/resources/jme3test/texture/tex3D.vert b/jme3-examples/src/main/resources/jme3test/texture/tex3D.vert index f91b7b3097..4681a36250 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/tex3D.vert +++ b/jme3-examples/src/main/resources/jme3test/texture/tex3D.vert @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + uniform mat4 g_WorldViewProjectionMatrix; attribute vec3 inTexCoord; @@ -8,4 +10,4 @@ varying vec3 texCoord; void main(){ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0); texCoord=inTexCoord; -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.frag b/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.frag index e70bc5b25a..9664f996a4 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.frag +++ b/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.frag @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + uniform sampler3D m_Texture; uniform int m_Rows; uniform float m_InvDepth; @@ -12,4 +14,4 @@ void main(){ vec3 texC = vec3(fract(texCoord.x),fract(texCoord.y),(depthy * rows + depthx) * m_InvDepth); gl_FragColor = texture3D(m_Texture, texC); -} \ No newline at end of file +} diff --git a/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.j3md b/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.j3md index a42bd381ed..ee00681d34 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.j3md +++ b/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.j3md @@ -7,8 +7,8 @@ MaterialDef Tex3DThumb { } Technique { - VertexShader GLSL100: jme3test/texture/tex3DThumb.vert - FragmentShader GLSL100: jme3test/texture/tex3DThumb.frag + VertexShader GLSL300 GLSL100: jme3test/texture/tex3DThumb.vert + FragmentShader GLSL300 GLSL100: jme3test/texture/tex3DThumb.frag WorldParameters { WorldViewProjectionMatrix diff --git a/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.vert b/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.vert index 6d27bc030c..2514cb532c 100644 --- a/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.vert +++ b/jme3-examples/src/main/resources/jme3test/texture/tex3DThumb.vert @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + uniform mat4 g_WorldViewProjectionMatrix; attribute vec2 inTexCoord; @@ -8,4 +10,4 @@ varying vec2 texCoord; void main(){ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0); texCoord=inTexCoord; -} \ No newline at end of file +} diff --git a/jme3-lwjgl3/build.gradle b/jme3-lwjgl3/build.gradle index 442aa091e8..324c027b4b 100644 --- a/jme3-lwjgl3/build.gradle +++ b/jme3-lwjgl3/build.gradle @@ -2,7 +2,7 @@ if (!hasProperty('mainClass')) { ext.mainClass = '' } -def lwjglVersion = '3.2.1' +def lwjglVersion = '3.2.3' sourceCompatibility = '1.8' diff --git a/jme3-networking/src/main/java/com/jme3/network/base/ConnectorAdapter.java b/jme3-networking/src/main/java/com/jme3/network/base/ConnectorAdapter.java index 6f4935f8fb..100747ffd7 100644 --- a/jme3-networking/src/main/java/com/jme3/network/base/ConnectorAdapter.java +++ b/jme3-networking/src/main/java/com/jme3/network/base/ConnectorAdapter.java @@ -65,6 +65,7 @@ public class ConnectorAdapter extends Thread private MessageListener dispatcher; private ErrorListener errorHandler; private AtomicBoolean go = new AtomicBoolean(true); + private MessageProtocol protocol; private BlockingQueue outbound; @@ -75,11 +76,13 @@ public class ConnectorAdapter extends Thread // through this connector. private boolean reliable; - public ConnectorAdapter( Connector connector, MessageListener dispatcher, + public ConnectorAdapter( Connector connector, MessageProtocol protocol, + MessageListener dispatcher, ErrorListener errorHandler, boolean reliable ) { super( String.valueOf(connector) ); - this.connector = connector; + this.connector = connector; + this.protocol = protocol; this.dispatcher = dispatcher; this.errorHandler = errorHandler; this.reliable = reliable; @@ -151,7 +154,7 @@ protected void handleError( Exception e ) public void run() { - MessageProtocol protocol = new MessageProtocol(); + MessageBuffer messageBuffer = protocol.createBuffer(); try { while( go.get() ) { @@ -166,10 +169,10 @@ public void run() } } - protocol.addBuffer( buffer ); + messageBuffer.addBytes(buffer); Message m = null; - while( (m = protocol.getMessage()) != null ) { + while( (m = messageBuffer.pollMessage()) != null ) { m.setReliable( reliable ); dispatch( m ); } diff --git a/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java b/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java index 94a951e520..eec00323fd 100644 --- a/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java +++ b/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java @@ -33,6 +33,7 @@ import com.jme3.network.*; import com.jme3.network.ClientStateListener.DisconnectInfo; +import com.jme3.network.base.protocol.SerializerMessageProtocol; import com.jme3.network.kernel.Connector; import com.jme3.network.message.ChannelInfoMessage; import com.jme3.network.message.ClientRegistrationMessage; @@ -83,6 +84,7 @@ public class DefaultClient implements Client private ConnectorFactory connectorFactory; private ClientServiceManager services; + private MessageProtocol protocol = new SerializerMessageProtocol(); public DefaultClient( String gameName, int version ) { @@ -114,9 +116,9 @@ protected void setPrimaryConnectors( Connector reliable, Connector fast, Connect throw new IllegalStateException( "Channels already exist." ); this.connectorFactory = connectorFactory; - channels.add(new ConnectorAdapter(reliable, dispatcher, dispatcher, true)); + channels.add(new ConnectorAdapter(reliable, protocol, dispatcher, dispatcher, true)); if( fast != null ) { - channels.add(new ConnectorAdapter(fast, dispatcher, dispatcher, false)); + channels.add(new ConnectorAdapter(fast, protocol, dispatcher, dispatcher, false)); } else { // Add the null adapter to keep the indexes right channels.add(null); @@ -279,7 +281,7 @@ protected void send( int channel, Message message, boolean waitForConnected ) buffer.clear(); // Convert the message to bytes - buffer = MessageProtocol.messageToBuffer(message, buffer); + buffer = protocol.toByteBuffer(message, buffer); // Since we share the buffer between invocations, we will need to // copy this message's part out of it. This is because we actually @@ -431,7 +433,7 @@ protected void configureChannels( long tempId, int[] ports ) { try { for( int i = 0; i < ports.length; i++ ) { Connector c = connectorFactory.createConnector( i, ports[i] ); - ConnectorAdapter ca = new ConnectorAdapter(c, dispatcher, dispatcher, true); + ConnectorAdapter ca = new ConnectorAdapter(c, protocol, dispatcher, dispatcher, true); int ch = channels.size(); channels.add( ca ); diff --git a/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java b/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java index eceaf6d730..3040fdcdae 100644 --- a/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java +++ b/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java @@ -32,6 +32,7 @@ package com.jme3.network.base; import com.jme3.network.*; +import com.jme3.network.base.protocol.SerializerMessageProtocol; import com.jme3.network.kernel.Endpoint; import com.jme3.network.kernel.Kernel; import com.jme3.network.message.ChannelInfoMessage; @@ -88,6 +89,7 @@ public class DefaultServer implements Server private final List connectionListeners = new CopyOnWriteArrayList(); private HostedServiceManager services; + private MessageProtocol protocol = new SerializerMessageProtocol(); public DefaultServer( String gameName, int version, Kernel reliable, Kernel fast ) { @@ -99,10 +101,10 @@ public DefaultServer( String gameName, int version, Kernel reliable, Kernel fast this.services = new HostedServiceManager(this); addStandardServices(); - reliableAdapter = new KernelAdapter( this, reliable, dispatcher, true ); + reliableAdapter = new KernelAdapter(this, reliable, protocol, dispatcher, true); channels.add( reliableAdapter ); if( fast != null ) { - fastAdapter = new KernelAdapter( this, fast, dispatcher, false ); + fastAdapter = new KernelAdapter(this, fast, protocol, dispatcher, false); channels.add( fastAdapter ); } } @@ -153,7 +155,7 @@ public int addChannel( int port ) alternatePorts.add(port); Kernel kernel = kernelFactory.createKernel(result, port); - channels.add( new KernelAdapter(this, kernel, dispatcher, true) ); + channels.add( new KernelAdapter(this, kernel, protocol, dispatcher, true) ); return result; } catch( IOException e ) { @@ -238,7 +240,7 @@ public void broadcast( Filter filter, Message message if( connections.isEmpty() ) return; - ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); + ByteBuffer buffer = protocol.toByteBuffer(message, null); FilterAdapter adapter = filter == null ? null : new FilterAdapter(filter); @@ -263,7 +265,7 @@ public void broadcast( int channel, Filter filter, Mes checkChannel(channel); - ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); + ByteBuffer buffer = protocol.toByteBuffer(message, null); FilterAdapter adapter = filter == null ? null : new FilterAdapter(filter); @@ -579,7 +581,7 @@ public void send( Message message ) if( log.isLoggable(Level.FINER) ) { log.log(Level.FINER, "send({0})", message); } - ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); + ByteBuffer buffer = protocol.toByteBuffer(message, null); if( message.isReliable() || channels[CH_UNRELIABLE] == null ) { channels[CH_RELIABLE].send( buffer ); } else { @@ -594,7 +596,7 @@ public void send( int channel, Message message ) log.log(Level.FINER, "send({0}, {1})", new Object[]{channel, message}); } checkChannel(channel); - ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); + ByteBuffer buffer = protocol.toByteBuffer(message, null); channels[channel+CH_FIRST].send(buffer); } diff --git a/jme3-networking/src/main/java/com/jme3/network/base/KernelAdapter.java b/jme3-networking/src/main/java/com/jme3/network/base/KernelAdapter.java index f3a9965e2a..cb2dd21929 100644 --- a/jme3-networking/src/main/java/com/jme3/network/base/KernelAdapter.java +++ b/jme3-networking/src/main/java/com/jme3/network/base/KernelAdapter.java @@ -70,21 +70,24 @@ public class KernelAdapter extends Thread private Kernel kernel; private MessageListener messageDispatcher; private AtomicBoolean go = new AtomicBoolean(true); - + + private MessageProtocol protocol; + // Keeps track of the in-progress messages that are received // on reliable connections - private Map messageBuffers = new ConcurrentHashMap(); + private Map messageBuffers = new ConcurrentHashMap<>(); // Marks the messages as reliable or not if they came // through this connector. private boolean reliable; - public KernelAdapter( DefaultServer server, Kernel kernel, MessageListener messageDispatcher, + public KernelAdapter( DefaultServer server, Kernel kernel, MessageProtocol protocol, MessageListener messageDispatcher, boolean reliable ) { super( String.valueOf(kernel) ); this.server = server; this.kernel = kernel; + this.protocol = protocol; this.messageDispatcher = messageDispatcher; this.reliable = reliable; setDaemon(true); @@ -190,7 +193,7 @@ protected void dispatch( Endpoint p, Message m ) } } - protected MessageProtocol getMessageBuffer( Endpoint p ) + protected MessageBuffer getMessageBuffer( Endpoint p ) { if( !reliable ) { // Since UDP comes in packets and they aren't split @@ -198,12 +201,12 @@ protected MessageProtocol getMessageBuffer( Endpoint p ) // be a down side because there is no way for us to reliably // clean these up later since we'd create another one for // any random UDP packet that comes to the port. - return new MessageProtocol(); + return protocol.createBuffer(); } else { // See if we already have one - MessageProtocol result = messageBuffers.get(p); + MessageBuffer result = messageBuffers.get(p); if( result == null ) { - result = new MessageProtocol(); + result = protocol.createBuffer(); messageBuffers.put(p, result); } return result; @@ -212,13 +215,12 @@ protected MessageProtocol getMessageBuffer( Endpoint p ) protected void createAndDispatch( Envelope env ) { - MessageProtocol protocol = getMessageBuffer(env.getSource()); + MessageBuffer protocol = getMessageBuffer(env.getSource()); byte[] data = env.getData(); ByteBuffer buffer = ByteBuffer.wrap(data); - int count = protocol.addBuffer( buffer ); - if( count == 0 ) { + if( !protocol.addBytes(buffer) ) { // This can happen if there was only a partial message // received. However, this should never happen for unreliable // connections. @@ -236,9 +238,9 @@ protected void createAndDispatch( Envelope env ) // Should be complete... and maybe we should check but we don't Message m = null; - while( (m = protocol.getMessage()) != null ) { + while( (m = protocol.pollMessage()) != null ) { m.setReliable(reliable); - dispatch( env.getSource(), m ); + dispatch(env.getSource(), m); } } diff --git a/jme3-networking/src/main/java/com/jme3/network/base/MessageBuffer.java b/jme3-networking/src/main/java/com/jme3/network/base/MessageBuffer.java new file mode 100644 index 0000000000..2e4ca4e380 --- /dev/null +++ b/jme3-networking/src/main/java/com/jme3/network/base/MessageBuffer.java @@ -0,0 +1,65 @@ +/* + * 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.network.base; + +import java.nio.ByteBuffer; + +import com.jme3.network.Message; + +/** + * Accumulates network data into Message objects. This allows + * random chunks of bytes to be assembled into messages even if + * the buffer boundaries don't line up. + * + * @author Paul Speed + */ +public interface MessageBuffer { + + /** + * Returns the next message in the buffer or null if there are no more + * messages in the buffer. + */ + public Message pollMessage(); + + /** + * Returns true if there is a message waiting in the buffer. + */ + public boolean hasMessages(); + + /** + * Adds byte data to the message buffer. Returns true if there is + * a message waiting after this call. + */ + public boolean addBytes( ByteBuffer buffer ); +} + diff --git a/jme3-networking/src/main/java/com/jme3/network/base/MessageProtocol.java b/jme3-networking/src/main/java/com/jme3/network/base/MessageProtocol.java index cebb842951..74aad907cb 100644 --- a/jme3-networking/src/main/java/com/jme3/network/base/MessageProtocol.java +++ b/jme3-networking/src/main/java/com/jme3/network/base/MessageProtocol.java @@ -31,159 +31,26 @@ */ package com.jme3.network.base; -import com.jme3.network.Message; -import com.jme3.network.serializing.Serializer; -import java.io.IOException; import java.nio.ByteBuffer; -import java.util.LinkedList; +import com.jme3.network.Message; /** * Consolidates the conversion of messages to/from byte buffers * and provides a rolling message buffer. ByteBuffers can be * pushed in and messages will be extracted, accumulated, and - * available for retrieval. This is not thread safe and is meant - * to be used within a single message processing thread. + * available for retrieval. The MessageBuffers returned are generally + * not thread safe and are meant to be used within a single message + * processing thread. MessageProtocol implementations themselves should + * be thread safe. * - *

The protocol is based on a simple length + data format - * where two bytes represent the (short) length of the data - * and the rest is the raw data for the Serializers class.

+ *

The specific serialization protocol used is up to the implementation.

* - * @version $Revision$ * @author Paul Speed - */ -public class MessageProtocol -{ - private final LinkedList messages = new LinkedList(); - private ByteBuffer current; - private int size; - private Byte carry; - - /** - * Converts a message to a ByteBuffer using the Serializer - * and the (short length) + data protocol. If target is null - * then a 32k byte buffer will be created and filled. - */ - public static ByteBuffer messageToBuffer( Message message, ByteBuffer target ) - { - // Could let the caller pass their own in - ByteBuffer buffer = target == null ? ByteBuffer.allocate( 32767 + 2 ) : target; - - try { - buffer.position( 2 ); - Serializer.writeClassAndObject( buffer, message ); - buffer.flip(); - short dataLength = (short)(buffer.remaining() - 2); - buffer.putShort( dataLength ); - buffer.position( 0 ); - - return buffer; - } catch( IOException e ) { - throw new RuntimeException( "Error serializing message", e ); - } - } - - /** - * Retrieves and removes an extracted message from the accumulated buffer - * or returns null if there are no more messages. - */ - public Message getMessage() - { - if( messages.isEmpty() ) { - return null; - } - - return messages.removeFirst(); - } - - /** - * Adds the specified buffer, extracting the contained messages - * and making them available to getMessage(). The left over - * data is buffered to be combined with future data. - * - * @return The total number of queued messages after this call. - */ - public int addBuffer( ByteBuffer buffer ) - { - // push the data from the buffer into as - // many messages as we can - while( buffer.remaining() > 0 ) { - - if( current == null ) { - - // If we have a left over carry then we need to - // do manual processing to get the short value - if( carry != null ) { - byte high = carry; - byte low = buffer.get(); - - size = (high & 0xff) << 8 | (low & 0xff); - carry = null; - } - else if( buffer.remaining() < 2 ) { - // It's possible that the supplied buffer only has one - // byte in it... and in that case we will get an underflow - // when attempting to read the short below. - - // It has to be 1 or we'd never get here... but one - // isn't enough so we stash it away. - carry = buffer.get(); - break; - } else { - // We are not currently reading an object so - // grab the size. - // Note: this is somewhat limiting... int would - // be better. - size = buffer.getShort(); - } - - // Allocate the buffer into which we'll feed the - // data as we get it - current = ByteBuffer.allocate(size); - } - - if( current.remaining() <= buffer.remaining() ) { - // We have at least one complete object so - // copy what we can into current, create a message, - // and then continue pulling from buffer. - - // Artificially set the limit so we don't overflow - int extra = buffer.remaining() - current.remaining(); - buffer.limit( buffer.position() + current.remaining() ); - - // Now copy the data - current.put( buffer ); - current.flip(); - - // Now set the limit back to a good value - buffer.limit( buffer.position() + extra ); - - createMessage( current ); - - current = null; - } else { - - // Not yet a complete object so just copy what we have - current.put( buffer ); - } - } - - return messages.size(); - } - - /** - * Creates a message from the properly sized byte buffer - * and adds it to the messages queue. - */ - protected void createMessage( ByteBuffer buffer ) - { - try { - Object obj = Serializer.readClassAndObject( buffer ); - Message m = (Message)obj; - messages.add(m); - } catch( IOException e ) { - throw new RuntimeException( "Error deserializing object, class ID:" + buffer.getShort(0), e ); - } - } + */ +public interface MessageProtocol { + public ByteBuffer toByteBuffer( Message message, ByteBuffer target ); + public Message toMessage( ByteBuffer bytes ); + public MessageBuffer createBuffer(); } diff --git a/jme3-networking/src/main/java/com/jme3/network/base/protocol/GreedyMessageBuffer.java b/jme3-networking/src/main/java/com/jme3/network/base/protocol/GreedyMessageBuffer.java new file mode 100644 index 0000000000..14831dfda9 --- /dev/null +++ b/jme3-networking/src/main/java/com/jme3/network/base/protocol/GreedyMessageBuffer.java @@ -0,0 +1,157 @@ +/* + * 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.network.base.protocol; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedList; + +import com.jme3.network.Message; +import com.jme3.network.base.MessageBuffer; +import com.jme3.network.base.MessageProtocol; + + +/** + * A MessageBuffer implementation that will aggressively create + * all messages as byte data comes in. In other words, if there + * are four messages in the ByteBuffer passed to addBuffer() then + * all of the messages will be deserialized during that call and + * queued up for later return. The down side is that if any of + * those messages was going to alter the MessageProtocol serialization + * behavior in a way that affects later messages then problems occur + * when those messages are all in one block. + * + * @author Paul Speed + */ +public class GreedyMessageBuffer implements MessageBuffer { + + private MessageProtocol protocol; + private final LinkedList messages = new LinkedList(); + private ByteBuffer current; + private int size; + private Byte carry; + + public GreedyMessageBuffer( MessageProtocol protocol ) { + this.protocol = protocol; + } + + /** + * Returns the next message in the buffer or null if there are no more + * messages in the buffer. + */ + public Message pollMessage() { + if( messages.isEmpty() ) { + return null; + } + return messages.removeFirst(); + } + + /** + * Returns true if there is a message waiting in the buffer. + */ + public boolean hasMessages() { + return !messages.isEmpty(); + } + + /** + * Adds byte data to the message buffer. Returns true if there is + * a message waiting after this call. + */ + public boolean addBytes( ByteBuffer buffer ) { + // push the data from the buffer into as + // many messages as we can + while( buffer.remaining() > 0 ) { + + if( current == null ) { + + // If we have a left over carry then we need to + // do manual processing to get the short value + if( carry != null ) { + byte high = carry; + byte low = buffer.get(); + + size = (high & 0xff) << 8 | (low & 0xff); + carry = null; + } + else if( buffer.remaining() < 2 ) { + // It's possible that the supplied buffer only has one + // byte in it... and in that case we will get an underflow + // when attempting to read the short below. + + // It has to be 1 or we'd never get here... but one + // isn't enough so we stash it away. + carry = buffer.get(); + break; + } else { + // We are not currently reading an object so + // grab the size. + // Note: this is somewhat limiting... int would + // be better. + size = buffer.getShort(); + } + + // Allocate the buffer into which we'll feed the + // data as we get it + current = ByteBuffer.allocate(size); + } + + if( current.remaining() <= buffer.remaining() ) { + // We have at least one complete object so + // copy what we can into current, create a message, + // and then continue pulling from buffer. + + // Artificially set the limit so we don't overflow + int extra = buffer.remaining() - current.remaining(); + buffer.limit(buffer.position() + current.remaining()); + + // Now copy the data + current.put(buffer); + current.flip(); + + // Now set the limit back to a good value + buffer.limit(buffer.position() + extra); + + messages.add(protocol.toMessage(current)); + + current = null; + } else { + // Not yet a complete object so just copy what we have + current.put(buffer); + } + } + + return hasMessages(); + } +} + + diff --git a/jme3-networking/src/main/java/com/jme3/network/base/protocol/LazyMessageBuffer.java b/jme3-networking/src/main/java/com/jme3/network/base/protocol/LazyMessageBuffer.java new file mode 100644 index 0000000000..fc66cb148f --- /dev/null +++ b/jme3-networking/src/main/java/com/jme3/network/base/protocol/LazyMessageBuffer.java @@ -0,0 +1,175 @@ +/* + * 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.network.base.protocol; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedList; + +import com.jme3.network.Message; +import com.jme3.network.base.MessageBuffer; +import com.jme3.network.base.MessageProtocol; + + +/** + * A MessageBuffer implementation that will deserialize messages as they + * are returned instead of deserializing them as the data comes in. This + * allows the individual messages to be processed before later messages + * are deserialized, thus allowing the serialization process itself to be + * altered mid-stream. + * + * @author Paul Speed + */ +public class LazyMessageBuffer implements MessageBuffer { + + private MessageProtocol protocol; + private final LinkedList messages = new LinkedList(); + private ByteBuffer current; + private int size; + private Byte carry; + + public LazyMessageBuffer( MessageProtocol protocol ) { + this.protocol = protocol; + } + + /** + * Returns the next message in the buffer or null if there are no more + * messages in the buffer. + */ + public Message pollMessage() { + if( messages.isEmpty() ) { + return null; + } + ByteBuffer bytes = messages.removeFirst(); + return protocol.toMessage(bytes); + } + + /** + * Returns true if there is a message waiting in the buffer. + */ + public boolean hasMessages() { + return !messages.isEmpty(); + } + + /** + * Adds byte data to the message buffer. Returns true if there is + * a message waiting after this call. + */ + public boolean addBytes( ByteBuffer buffer ) { + // push the data from the buffer into as + // many messages as we can + while( buffer.remaining() > 0 ) { + + if( current == null ) { + + // If we have a left over carry then we need to + // do manual processing to get the short value + if( carry != null ) { + byte high = carry; + byte low = buffer.get(); + + size = (high & 0xff) << 8 | (low & 0xff); + carry = null; + } + else if( buffer.remaining() < 2 ) { + // It's possible that the supplied buffer only has one + // byte in it... and in that case we will get an underflow + // when attempting to read the short below. + + // It has to be 1 or we'd never get here... but one + // isn't enough so we stash it away. + carry = buffer.get(); + break; + } else { + // We are not currently reading an object so + // grab the size. + // Note: this is somewhat limiting... int would + // be better. + size = buffer.getShort(); + } + + // Allocate the buffer into which we'll feed the + // data as we get it + current = ByteBuffer.allocate(size); + } + + if( current.remaining() <= buffer.remaining() ) { + // We have at least one complete object so + // copy what we can into current, create a message, + // and then continue pulling from buffer. + + // Artificially set the limit so we don't overflow + int extra = buffer.remaining() - current.remaining(); + buffer.limit(buffer.position() + current.remaining()); + + // Now copy the data + current.put(buffer); + current.flip(); + + // Now set the limit back to a good value + buffer.limit(buffer.position() + extra); + + // Just push the bytes and let the serialization happen later. + messages.add(current); + + current = null; + + // Note: I originally thought that lazy deserialization was + // going to be tricky/fancy because I'd imagined having to + // collect partial buffers, leave data in working buffers, etc.. + // However, the buffer we are passed is reused by the caller + // (it's part of the API contract) and so we MUST copy the + // data into "something" before returning. We already know + // what size buffer the message is going to need. That can't + // change. We are already creating per-message byte buffers. + // ...so we might as well just buffer this in our queue instead. + // The alternative is to somehow have an open-ended working buffer + // that expands/shrinks as needed to accomodate the 'unknown' number + // of messages that must be buffered before the caller asks for + // one. Obviously, that's way more wasteful than just keeping + // per-message byte buffers around. We already had them anyway. + // So in the end, I probably could have just altered the original + // buffering code and called it a day... but I had to do the refactoring + // before I figured that out and now we have the ability to more easily + // swap out protocol implementations. -pspeed:2019-09-08 + } else { + // Not yet a complete object so just copy what we have + current.put(buffer); + } + } + + return hasMessages(); + } +} + + diff --git a/jme3-networking/src/main/java/com/jme3/network/base/protocol/SerializerMessageProtocol.java b/jme3-networking/src/main/java/com/jme3/network/base/protocol/SerializerMessageProtocol.java new file mode 100644 index 0000000000..80493660ee --- /dev/null +++ b/jme3-networking/src/main/java/com/jme3/network/base/protocol/SerializerMessageProtocol.java @@ -0,0 +1,103 @@ +/* + * 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.network.base.protocol; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedList; + +import com.jme3.network.Message; +import com.jme3.network.base.MessageBuffer; +import com.jme3.network.base.MessageProtocol; +import com.jme3.network.serializing.Serializer; + +/** + * Implements a MessageProtocol providing message serializer/deserialization + * based on the built-in Serializer code. + * + *

The protocol is based on a simple length + data format + * where two bytes represent the (short) length of the data + * and the rest is the raw data for the Serializers class.

+ * + * @version $Revision$ + * @author Paul Speed + */ +public class SerializerMessageProtocol implements MessageProtocol { + + public SerializerMessageProtocol() { + } + + /** + * Converts a message to a ByteBuffer using the com.jme3.network.serializing.Serializer + * and the (short length) + data protocol. If target is null + * then a 32k byte buffer will be created and filled. + */ + public ByteBuffer toByteBuffer( Message message, ByteBuffer target ) { + + // Could let the caller pass their own in + ByteBuffer buffer = target == null ? ByteBuffer.allocate(32767 + 2) : target; + + try { + buffer.position(2); + Serializer.writeClassAndObject(buffer, message); + buffer.flip(); + short dataLength = (short)(buffer.remaining() - 2); + buffer.putShort(dataLength); + buffer.position(0); + + return buffer; + } catch( IOException e ) { + throw new RuntimeException("Error serializing message", e); + } + } + + /** + * Creates and returns a message from the properly sized byte buffer + * using com.jme3.network.serializing.Serializer. + */ + public Message toMessage( ByteBuffer bytes ) { + try { + return (Message)Serializer.readClassAndObject(bytes); + } catch( IOException e ) { + throw new RuntimeException("Error deserializing object, class ID:" + bytes.getShort(0), e); + } + } + + public MessageBuffer createBuffer() { + // Defaulting to LazyMessageBuffer + return new LazyMessageBuffer(this); + } + +} + + + diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java index 8fa23a538a..d6443cfcbd 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java @@ -194,7 +194,7 @@ public T getLocalObject( Class type ) { public T getLocalObject( String name, Class type ) { local.lock.readLock().lock(); try { - return type.cast(local.byName.get(name)); + return type.cast(local.byName.get(name).object); } finally { local.lock.readLock().unlock(); } diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java index a8947fdea6..d84c41b96b 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.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 @@ -541,7 +541,7 @@ private RenderState.BlendMode convertBlend(final BlendMode blendMode) { } else if (blendMode == BlendMode.BLEND) { return RenderState.BlendMode.Alpha; } else if (blendMode == BlendMode.MULIPLY) { - return RenderState.BlendMode.Modulate; + return RenderState.BlendMode.Alpha; } else { throw new UnsupportedOperationException(); } diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java index ab0fc6659e..7390a32654 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java @@ -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 @@ -209,7 +209,7 @@ private RenderState.BlendMode convertBlend(BlendMode blendMode) { } else if (blendMode == BlendMode.BLEND) { return RenderState.BlendMode.Alpha; } else if (blendMode == BlendMode.MULIPLY) { - return RenderState.BlendMode.Modulate; + return RenderState.BlendMode.Alpha; } else { throw new UnsupportedOperationException(); } diff --git a/jme3-plugins/src/xml/java/com/jme3/export/xml/DOMInputCapsule.java b/jme3-plugins/src/xml/java/com/jme3/export/xml/DOMInputCapsule.java index 6b0eb39b75..22779cf254 100644 --- a/jme3-plugins/src/xml/java/com/jme3/export/xml/DOMInputCapsule.java +++ b/jme3-plugins/src/xml/java/com/jme3/export/xml/DOMInputCapsule.java @@ -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 @@ -38,6 +38,7 @@ import com.jme3.util.BufferUtils; import com.jme3.util.IntMap; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; @@ -962,6 +963,7 @@ public Savable readSavable(String name, Savable defVal) throws IOException { private Savable readSavableFromCurrentElem(Savable defVal) throws InstantiationException, ClassNotFoundException, + NoSuchMethodException, InvocationTargetException, IOException, IllegalAccessException { Savable ret = defVal; Savable tmp = null; diff --git a/jme3-terrain/build.gradle b/jme3-terrain/build.gradle index 0a77d7d720..a782045c6a 100644 --- a/jme3-terrain/build.gradle +++ b/jme3-terrain/build.gradle @@ -4,4 +4,8 @@ if (!hasProperty('mainClass')) { dependencies { compile project(':jme3-core') + testCompile project(':jme3-core') + testCompile project(':jme3-desktop') + testCompile project(':jme3-core').sourceSets.test.output + testCompile project(':jme3-testdata') } diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java index 4b10e5c59b..0f18af88ef 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java @@ -45,8 +45,8 @@ import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; import java.io.IOException; -import java.nio.Buffer; import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; @@ -81,12 +81,7 @@ public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, floa FloatBuffer pb = writeVertexArray(null, scale, center); FloatBuffer texb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize); FloatBuffer nb = writeNormalArray(null, scale); - Buffer ib; - IndexBuffer idxB = writeIndexArrayLodDiff(lod, rightLod, topLod, leftLod, bottomLod, totalSize); - if (idxB.getBuffer() instanceof IntBuffer) - ib = (IntBuffer)idxB.getBuffer(); - else - ib = (ShortBuffer)idxB.getBuffer(); + IndexBuffer ib = writeIndexArrayLodDiff(lod, rightLod, topLod, leftLod, bottomLod, totalSize); FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3); FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3); writeTangentArray(nb, tanb, bb, texb, scale); @@ -97,10 +92,17 @@ public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, floa m.setBuffer(Type.Tangent, 3, tanb); m.setBuffer(Type.Binormal, 3, bb); m.setBuffer(Type.TexCoord, 2, texb); - if (ib instanceof IntBuffer) - m.setBuffer(Type.Index, 3, (IntBuffer)ib); - else if (ib instanceof ShortBuffer) - m.setBuffer(Type.Index, 3, (ShortBuffer)ib); + switch (ib.getFormat()) { + case UnsignedInt: + m.setBuffer(Type.Index, 3, (IntBuffer) ib.getBuffer()); + break; + case UnsignedShort: + m.setBuffer(Type.Index, 3, (ShortBuffer) ib.getBuffer()); + break; + case UnsignedByte: + m.setBuffer(Type.Index, 3, (ByteBuffer) ib.getBuffer()); + break; + } m.setStatic(); m.updateBound(); return m; diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java index 7f41bd3727..f89198f251 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java @@ -123,7 +123,7 @@ public class TerrainQuad extends Node implements Terrain { private int maxLod = -1; private BoundingBox affectedAreaBBox; // only set in the root quad - private TerrainPicker picker; + private TerrainPicker picker = new BresenhamTerrainPicker(this); private Vector3f lastScale = Vector3f.UNIT_XYZ; protected NeighbourFinder neighbourFinder; @@ -274,20 +274,7 @@ protected void cacheTerrainTransforms() { } private int collideWithRay(Ray ray, CollisionResults results) { - if (picker == null) - picker = new BresenhamTerrainPicker(this); - - Vector3f intersection = picker.getTerrainIntersection(ray, results); - if (intersection != null) { - if (ray.getLimit() < Float.POSITIVE_INFINITY) { - if (results.getClosestCollision().getDistance() <= ray.getLimit()) - return 1; // in range - else - return 0; // out of range - } else - return 1; - } else - return 0; + return picker.getTerrainIntersection(ray, results); } /** @@ -1915,5 +1902,23 @@ else if(((TerrainPatch) s).getQuadrant() == 4) return hm; } + + /** + * When colliding with this terrain, is a report of all collisions wanted or only the closest collision?
+ * If only the closest collision is required, the collision calculation will be faster.
+ * Note: If no collision happens, it takes as long as a collision with multipleCollisions on would take. + * + * @param set Whether to support multiple collisions or not + */ + public void setSupportMultipleCollisions(boolean set) { + if (picker == null) { + // This is so that it doesn't fail at the IllegalStateException because null !instanceof Anything + throw new NullPointerException("TerrainPicker is null"); + } else if (picker instanceof BresenhamTerrainPicker) { + ((BresenhamTerrainPicker)picker).setSupportMultipleCollisions(set); + } else { + throw new IllegalStateException("The underlying picking implementation does not support multiple collisions"); + } + } } diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java index cf7efc085e..5adf4f2bd6 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java @@ -67,31 +67,41 @@ public class BresenhamTerrainPicker implements TerrainPicker { private final TerrainQuad root; private final BresenhamYUpGridTracer tracer = new BresenhamYUpGridTracer(); + private boolean multipleCollisions = true; + public BresenhamTerrainPicker(TerrainQuad root) { this.root = root; } - public Vector3f getTerrainIntersection(Ray worldPick, CollisionResults results) { + public void setSupportMultipleCollisions(boolean multipleCollisions) { + this.multipleCollisions = multipleCollisions; + } + public boolean isSupportingMultipleCollisions() { + return multipleCollisions; + } + + public int getTerrainIntersection(Ray worldPick, CollisionResults results) { + int numCollisions = 0; worldPickRay.set(worldPick); - List pickData = new ArrayList(); + List pickData = new ArrayList<>(); root.findPick(worldPick.clone(), pickData); Collections.sort(pickData); - if (pickData.isEmpty()) - return null; + if (pickData.isEmpty()) { + return 0; + } workRay.set(worldPick); for (TerrainPickData pd : pickData) { TerrainPatch patch = pd.targetPatch; - tracer.getGridSpacing().set(patch.getWorldScale()); tracer.setGridOrigin(patch.getWorldTranslation()); - workRay.getOrigin().set(worldPick.getDirection()).multLocal(pd.cr.getDistance()-.1f).addLocal(worldPick.getOrigin()); + workRay.getOrigin().set(worldPick.getDirection()).multLocal(pd.cr.getDistance() - .1f).addLocal(worldPick.getOrigin()); tracer.startWalk(workRay); @@ -99,67 +109,141 @@ public Vector3f getTerrainIntersection(Ray worldPick, CollisionResults results) final Vector2f loc = tracer.getGridLocation(); if (tracer.isRayPerpendicularToGrid()) { - Triangle hit = new Triangle(); - checkTriangles(loc.x, loc.y, workRay, intersection, patch, hit); - float distance = worldPickRay.origin.distance(intersection); - CollisionResult cr = new CollisionResult(intersection, distance); - cr.setGeometry(patch); - cr.setContactNormal(hit.getNormal()); - results.addCollision(cr); - return intersection; - } - - - - while (loc.x >= -1 && loc.x <= patch.getSize() && - loc.y >= -1 && loc.y <= patch.getSize()) { - - //System.out.print(loc.x+","+loc.y+" : "); - // check the triangles of main square for intersection. Triangle hit = new Triangle(); if (checkTriangles(loc.x, loc.y, workRay, intersection, patch, hit)) { - // we found an intersection, so return that! float distance = worldPickRay.origin.distance(intersection); - CollisionResult cr = new CollisionResult(intersection, distance); - cr.setGeometry(patch); - results.addCollision(cr); - cr.setContactNormal(hit.getNormal()); - return intersection; - } - // because of how we get our height coords, we will - // sometimes be off by a grid spot, so we check the next - // grid space up. - int dx = 0, dz = 0; - Direction d = tracer.getLastStepDirection(); - switch (d) { - case PositiveX: - case NegativeX: - dx = 0; - dz = 1; - break; - case PositiveZ: - case NegativeZ: - dx = 1; - dz = 0; - break; + //@TODO: Verify if it's even possible to have a ray hit multiple "PickData"s when being perpendicular at all. + // because otherwise, we could always return 1 here. + if (worldPick.getLimit() < Float.POSITIVE_INFINITY) { + if (distance <= worldPick.getLimit()) { + if (addCollision(results, patch, intersection, hit, distance)) { + if (!multipleCollisions) { + return 1; + } else { + numCollisions++; + } + } + } // else return 0; // < this is the old behavior, since the code checked for the range afterwards. + } else { // unlimited range + if (addCollision(results, patch, intersection, hit, distance)) { + if (!multipleCollisions) { + return 1; + } else { + numCollisions++; + } + } + } + } // else no collision + } else { // If the ray is perpendicular, tracer.next() would never advance loc, leading to an infinite loop. + while (loc.x >= -1 && loc.x <= patch.getSize() && + loc.y >= -1 && loc.y <= patch.getSize()) { + + //System.out.print(loc.x + "," + loc.y + " : "); + // check the triangles of main square for intersection. + Triangle hit = new Triangle(); + if (checkTriangles(loc.x, loc.y, workRay, intersection, patch, hit)) { + // we found an intersection, so return that! + float distance = worldPickRay.origin.distance(intersection); + + if (worldPick.getLimit() < Float.POSITIVE_INFINITY) { + if (distance <= worldPick.getLimit()) { + if (addCollision(results, patch, intersection, hit, distance)) { + if (!multipleCollisions) { + return 1; + } else { + numCollisions++; + } + } + }// else return 0; // < this is the old behavior, since the code checked for the range afterwards. + } else { // unlimited range + if (addCollision(results, patch, intersection, hit, distance)) { + if (!multipleCollisions) { + return 1; + } else { + numCollisions++; + } + } + } + } + + // because of how we get our height coords, we will + // sometimes be off by a grid spot, so we check the next + // grid space up. + int dx = 0, dz = 0; + Direction d = tracer.getLastStepDirection(); + switch (d) { + case PositiveX: + case NegativeX: + dx = 0; + dz = 1; + break; + case PositiveZ: + case NegativeZ: + dx = 1; + dz = 0; + break; + } + + if (checkTriangles(loc.x + dx, loc.y + dz, workRay, intersection, patch, hit)) { + // we found an intersection, so return that! + float distance = worldPickRay.origin.distance(intersection); + + if (worldPick.getLimit() < Float.POSITIVE_INFINITY) { + if (distance <= worldPick.getLimit()) { + if (addCollision(results, patch, intersection, hit, distance)) { + if (!multipleCollisions) { + return 1; + } else { + numCollisions++; + } + } + } // else return null; // < this is the old behavior, since the code checked for the range afterwards. + } else { // unlimited range + if (addCollision(results, patch, intersection, hit, distance)) { + if (!multipleCollisions) { + return 1; + } else { + numCollisions++; + } + } + } + } + + tracer.next(); } + } + } - if (checkTriangles(loc.x + dx, loc.y + dz, workRay, intersection, patch, hit)) { - // we found an intersection, so return that! - float distance = worldPickRay.origin.distance(intersection); - CollisionResult cr = new CollisionResult(intersection, distance); - results.addCollision(cr); - cr.setGeometry(patch); - cr.setContactNormal(hit.getNormal()); - return intersection; - } + return numCollisions; + } - tracer.next(); + /** + * This method adds the found Collision to an existing collisionResult. + * @param results The results to add this collision to + * @param patch The TerrainPatch which collided + * @param intersection The actual intersection position + * @param hit The hit triangle + * @param distance The distance at which the hit occurred + * @return Whether the collision was accepted to the list or whether it has been deduplicated + */ + private boolean addCollision(CollisionResults results, TerrainPatch patch, Vector3f intersection, Triangle hit, float distance) { + CollisionResult cr = new CollisionResult(intersection.clone(), distance); + cr.setGeometry(patch); + cr.setContactNormal(hit.getNormal()); + cr.setTriangleIndex(hit.getIndex()); // this will probably always be 0 + + for (int i = 0; i < results.size(); i++) { + CollisionResult compare = results.getCollision(i); + if (compare.getDistance() == cr.getDistance() && compare.getGeometry() == cr.getGeometry() && + compare.getContactPoint().equals(cr.getContactPoint()) && + compare.getContactNormal().equals(cr.getContactNormal())) { + return false; // Collision already available, deduplicate. } } - return null; + results.addCollision(cr); + return true; } protected boolean checkTriangles(float gridX, float gridY, Ray pick, Vector3f intersection, TerrainPatch patch, Triangle store) { diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/TerrainPicker.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/TerrainPicker.java index 08308849f5..bd45f3a084 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/TerrainPicker.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/picking/TerrainPicker.java @@ -47,9 +47,8 @@ public interface TerrainPicker { * * @param worldPick * our pick ray, in world space. - * @return null if no pick is found. Otherwise it returns a Vector3f populated with the pick - * coordinates. + * @return The number of collisions found */ - public Vector3f getTerrainIntersection(final Ray worldPick, CollisionResults results); + public int getTerrainIntersection(final Ray worldPick, CollisionResults results); } diff --git a/jme3-terrain/src/test/java/com/jme3/terrain/collision/BaseAWTTest.java b/jme3-terrain/src/test/java/com/jme3/terrain/collision/BaseAWTTest.java new file mode 100644 index 0000000000..4ff66b4bff --- /dev/null +++ b/jme3-terrain/src/test/java/com/jme3/terrain/collision/BaseAWTTest.java @@ -0,0 +1,38 @@ +package com.jme3.terrain.collision; + +import com.jme3.asset.AssetManager; +import com.jme3.system.JmeDesktopSystem; +import com.jme3.system.JmeSystem; + +/** + * This class provides some utility functions to properly test the jMonkeyEngine.
+ * Thus it contains simple methods to get and create a headless assetManager amongst other things.
+ * In comparison to {@link BaseTest} it provides a DesktopAssetManager capable of loading image formats using AWT, which + * however makes those tests unsuitable for headless ci testing. This requires jme3-desktop to be a testRuntime dependency. + * + * @author MeFisto94 + */ +public abstract class BaseAWTTest { + private AssetManager assetManager; + + static { + //JmeSystem.setSystemDelegate(new JmeDesktopSystem()); + } + + public AssetManager getAssetManager() { + if (assetManager == null) { + assetManager = createAssetManager(); + } + + return assetManager; + } + + private AssetManager createAssetManager() { + /* Desktop.cfg supports the following additional file formats at the time of writing: + LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg + LOADER com.jme3.audio.plugins.OGGLoader : ogg + */ + return JmeSystem.newAssetManager(BaseTest.class.getResource("/com/jme3/asset/Desktop.cfg")); + } + +} diff --git a/jme3-terrain/src/test/java/com/jme3/terrain/collision/BaseTest.java b/jme3-terrain/src/test/java/com/jme3/terrain/collision/BaseTest.java new file mode 100644 index 0000000000..15f7518558 --- /dev/null +++ b/jme3-terrain/src/test/java/com/jme3/terrain/collision/BaseTest.java @@ -0,0 +1,28 @@ +package com.jme3.terrain.collision; + +import com.jme3.asset.AssetManager; +import com.jme3.system.TestUtil; + +/** + * This class provides some utility functions to properly test the jMonkeyEngine.
+ * Thus it contains simple methods to get and create a headless assetManager amongst other things.
+ * If you need support for image/texture formats (png, tga, jpg, ...) see {@link BaseAWTTest} + * + * @author MeFisto94 + */ +public abstract class BaseTest { + private AssetManager assetManager; + + public AssetManager getAssetManager() { + if (assetManager == null) { + assetManager = createAssetManager(); + } + + return assetManager; + } + + private AssetManager createAssetManager() { + return TestUtil.createAssetManager(); + } + +} diff --git a/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java b/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java new file mode 100644 index 0000000000..32768f4060 --- /dev/null +++ b/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java @@ -0,0 +1,87 @@ +package com.jme3.terrain.collision; + +import com.jme3.collision.CollisionResults; +import com.jme3.math.Ray; +import com.jme3.math.Vector3f; +import com.jme3.terrain.geomipmap.TerrainQuad; +import com.jme3.terrain.heightmap.AbstractHeightMap; +import com.jme3.terrain.heightmap.ImageBasedHeightMap; +import com.jme3.texture.Texture; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class TerrainCollisionTest extends BaseAWTTest { + TerrainQuad quad; + + @Before + public void initQuad() { + Texture heightMapImage = getAssetManager().loadTexture("Textures/Terrain/splat/mountains512.png"); + AbstractHeightMap map = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); + map.load(); + quad = new TerrainQuad("terrain", 65, 513, map.getHeightMap()); + } + + /** + * Due to a previous bug, when no collision should happen, the CollisionResults struct was still populated, leading + * to an incoherency of data and ghost collisions when passing a non-empty CR. + */ + @Test + public void testNoCollision() { + Ray r = new Ray(new Vector3f(0f, 40f, 0f), Vector3f.UNIT_Y.negate()); + r.setLimit(0.1f); + CollisionResults cr = new CollisionResults(); + long l = System.nanoTime(); + int cw = quad.collideWith(r, cr); + System.out.println((System.nanoTime() - l) + " ns"); + + Assert.assertEquals(0, cw); + Assert.assertEquals(0, cr.size()); + Assert.assertEquals(null, cr.getClosestCollision()); + Assert.assertEquals(null, cr.getFarthestCollision()); + } + + @Test + public void testPerpendicularCollision() { + Ray r = new Ray(new Vector3f(0f, 40f, 0f), Vector3f.UNIT_Y.negate()); + CollisionResults cr = new CollisionResults(); + int cw = quad.collideWith(r, cr); + + Assert.assertEquals(1, cw); + Assert.assertEquals(1, cr.size()); + Assert.assertEquals(new Vector3f(0f, 28f, 0f), cr.getClosestCollision().getContactPoint()); + Assert.assertEquals(new Vector3f(-0.5144958f, 0.6859944f, 0.5144958f), cr.getClosestCollision().getContactNormal()); + Assert.assertEquals(12, cr.getClosestCollision().getDistance(), 0.01d); + Assert.assertEquals(0, cr.getClosestCollision().getTriangleIndex()); + } + + @Test + public void testMultiCollision() { + // Ray parameters obtained by using TerrainTestCollision (manual inspection of a feasible ray and commenting out setLocalScale(2) + Ray r = new Ray(new Vector3f(-38.689114f, 35.622643f, -40.222355f), new Vector3f(0.68958646f, 0.0980845f, 0.7175304f)); + + CollisionResults cr = new CollisionResults(); + long l = System.nanoTime(); + int cw = quad.collideWith(r, cr); + System.out.println((System.nanoTime() - l) + " ns"); + Assert.assertEquals(6, cw); + Assert.assertEquals(6, cr.size()); + + } + + @Test + public void testPreventRegression() { + // This test is as the multi collision changes lead to a regression where sometimes a collision was ignored + // Ray parameters obtained by using TerrainTestCollision (manual inspection of a feasible ray and commenting out setLocalScale(2)) + Ray r = new Ray(new Vector3f(101.61858f, 78.35965f, 17.645157f), new Vector3f(-0.4188528f, -0.56462675f, 0.71116734f)); + + CollisionResults cr = new CollisionResults(); + quad.collideWith(r, cr); + + Assert.assertEquals(3, cr.size()); + Assert.assertEquals(68.1499f, cr.getClosestCollision().getDistance(), 0.01f); + Assert.assertEquals(new Vector3f(73.07381f, 39.88039f, 66.11114f), cr.getClosestCollision().getContactPoint()); + Assert.assertEquals(new Vector3f(0.9103665f, 0.33104235f, -0.24828176f), cr.getClosestCollision().getContactNormal()); + } + +} diff --git a/jme3-testdata/build.gradle b/jme3-testdata/build.gradle index f0f070fbcb..d025b5775d 100644 --- a/jme3-testdata/build.gradle +++ b/jme3-testdata/build.gradle @@ -2,13 +2,9 @@ if (!hasProperty('mainClass')) { ext.mainClass = '' } -repositories { - maven { - url 'http://nifty-gui.sourceforge.net/nifty-maven-repo' - } -} +def niftyVersion = '1.4.3' dependencies { - compile 'lessvoid:nifty-examples:1.4.1' - runtime 'com.github.nifty-gui:nifty-style-black:1.4.3' + runtime "com.github.nifty-gui:nifty-examples:$niftyVersion" + runtime "com.github.nifty-gui:nifty-style-black:$niftyVersion" } diff --git a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.frag b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.frag index 636af0c7ec..ac75aaf04f 100644 --- a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.frag +++ b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.frag @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + void main(){ gl_FragColor=vec4(1.0,0.0,1.0,0.5); -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.geom b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.geom index e0c5d7b9ea..7acd056e8f 100644 --- a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.geom +++ b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.geom @@ -7,8 +7,8 @@ const float PI = 3.1415926; void main(){ for (int i = 0; i <= 10; i++) { - float ang = PI * 2.0 / 10.0 * i; - vec4 offset = vec4(cos(ang) * 5, -sin(ang) * 5, 0.0, 0.0); + float ang = PI * 2.0 / 10.0 * float(i); + vec4 offset = vec4(cos(ang) * 5.0, -sin(ang) * 5.0, 0.0, 0.0); gl_Position = g_WorldViewProjectionMatrix*vec4(gl_in[0].gl_Position.xyz + offset.xyz,1.0); EmitVertex(); diff --git a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.j3md b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.j3md index 4ae79e3ed0..b2289b30d4 100644 --- a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.j3md +++ b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.j3md @@ -5,13 +5,13 @@ MaterialDef SimpleGeom { } Technique { - VertexShader GLSL330: Materials/Geom/SimpleGeom.vert - GeometryShader GLSL330: Materials/Geom/SimpleGeom.geom - FragmentShader GLSL330: Materials/Geom/SimpleGeom.frag + VertexShader GLSL330 GLSL320: Materials/Geom/SimpleGeom.vert + GeometryShader GLSL330 GLSL320: Materials/Geom/SimpleGeom.geom + FragmentShader GLSL330 GLSL320: Materials/Geom/SimpleGeom.frag WorldParameters { WorldViewProjectionMatrix } } -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.vert b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.vert index f82ebb79b7..1653607cd5 100644 --- a/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.vert +++ b/jme3-testdata/src/main/resources/Materials/Geom/SimpleGeom.vert @@ -1,5 +1,7 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + attribute vec3 inPosition; void main(){ gl_Position=vec4(inPosition,1); -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.frag b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.frag index 636af0c7ec..ac75aaf04f 100644 --- a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.frag +++ b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.frag @@ -1,3 +1,5 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + void main(){ gl_FragColor=vec4(1.0,0.0,1.0,0.5); -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.j3md b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.j3md index 81813493da..5797bea53e 100644 --- a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.j3md +++ b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.j3md @@ -6,14 +6,14 @@ MaterialDef SimpleGeom { } Technique { - VertexShader GLSL400: Materials/Tess/SimpleTess.vert - TessellationEvaluationShader GLSL400: Materials/Tess/SimpleTess.tseval - TessellationControlShader GLSL400: Materials/Tess/SimpleTess.tsctrl - FragmentShader GLSL400: Materials/Tess/SimpleTess.frag + VertexShader GLSL400 GLSL320: Materials/Tess/SimpleTess.vert + TessellationEvaluationShader GLSL400 GLSL320: Materials/Tess/SimpleTess.tseval + TessellationControlShader GLSL400 GLSL320: Materials/Tess/SimpleTess.tsctrl + FragmentShader GLSL400 GLSL320: Materials/Tess/SimpleTess.frag WorldParameters { WorldViewProjectionMatrix } } -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tsctrl b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tsctrl index 3f70746420..81b4e291a5 100644 --- a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tsctrl +++ b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tsctrl @@ -5,13 +5,15 @@ out gl_PerVertex{ uniform int m_TessellationFactor; void main(){ if (gl_InvocationID == 0){ - gl_TessLevelOuter[0]=m_TessellationFactor; - gl_TessLevelOuter[1]=m_TessellationFactor; - gl_TessLevelOuter[2]=m_TessellationFactor; - gl_TessLevelOuter[3]=m_TessellationFactor; + float f_TessellationFactor=float(m_TessellationFactor); - gl_TessLevelInner[0]=m_TessellationFactor; - gl_TessLevelInner[1]=m_TessellationFactor; + gl_TessLevelOuter[0]=f_TessellationFactor; + gl_TessLevelOuter[1]=f_TessellationFactor; + gl_TessLevelOuter[2]=f_TessellationFactor; + gl_TessLevelOuter[3]=f_TessellationFactor; + + gl_TessLevelInner[0]=f_TessellationFactor; + gl_TessLevelInner[1]=f_TessellationFactor; } gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tseval b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tseval index 445276e427..4dbf976f31 100644 --- a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tseval +++ b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.tseval @@ -8,6 +8,6 @@ void main(){ vec3 p1 = mix(gl_in[1].gl_Position.xyz, gl_in[2].gl_Position.xyz, gl_TessCoord.x); // interpolate in vert direction vec3 tePosition = mix(p0, p1, gl_TessCoord.y); - gl_Position = g_WorldViewProjectionMatrix * vec4(tePosition, 1); + gl_Position = g_WorldViewProjectionMatrix * vec4(tePosition, 1.0); } diff --git a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.vert b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.vert index f82ebb79b7..1653607cd5 100644 --- a/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.vert +++ b/jme3-testdata/src/main/resources/Materials/Tess/SimpleTess.vert @@ -1,5 +1,7 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" + attribute vec3 inPosition; void main(){ gl_Position=vec4(inPosition,1); -} \ No newline at end of file +} diff --git a/jme3-testdata/src/main/resources/Scenes/Beach/FullskiesSunset0068.dds b/jme3-testdata/src/main/resources/Scenes/Beach/FullskiesSunset0068.dds index 6d5e0d8e8f..44b0b41ba3 100644 Binary files a/jme3-testdata/src/main/resources/Scenes/Beach/FullskiesSunset0068.dds and b/jme3-testdata/src/main/resources/Scenes/Beach/FullskiesSunset0068.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/3D/flame.dds b/jme3-testdata/src/main/resources/Textures/3D/flame.dds index 2eb00f7462..0a19ac0e81 100644 Binary files a/jme3-testdata/src/main/resources/Textures/3D/flame.dds and b/jme3-testdata/src/main/resources/Textures/3D/flame.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Sky/Bright/BrightSky.dds b/jme3-testdata/src/main/resources/Textures/Sky/Bright/BrightSky.dds index 3379320ab1..1da88cf5f2 100644 Binary files a/jme3-testdata/src/main/resources/Textures/Sky/Bright/BrightSky.dds and b/jme3-testdata/src/main/resources/Textures/Sky/Bright/BrightSky.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Sky/Bright/FullskiesBlueClear03.dds b/jme3-testdata/src/main/resources/Textures/Sky/Bright/FullskiesBlueClear03.dds index 1a86a1fda3..a79cbe75c7 100644 Binary files a/jme3-testdata/src/main/resources/Textures/Sky/Bright/FullskiesBlueClear03.dds and b/jme3-testdata/src/main/resources/Textures/Sky/Bright/FullskiesBlueClear03.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.dds b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.dds index 7efe7899c9..2ac3b8b863 100644 Binary files a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.dds and b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_dxt5.dds b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_dxt5.dds new file mode 100644 index 0000000000..7efe7899c9 Binary files /dev/null and b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_dxt5.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds index fd93003dda..df93b19cca 100644 Binary files a/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds and b/jme3-testdata/src/main/resources/Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond.dds b/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond.dds index 309f2c7d41..7470ead5d2 100644 Binary files a/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond.dds and b/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond.dds differ diff --git a/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond_dxt5.dds b/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond_dxt5.dds new file mode 100644 index 0000000000..309f2c7d41 Binary files /dev/null and b/jme3-testdata/src/main/resources/Textures/Terrain/Pond/Pond_dxt5.dds differ diff --git a/jme3-vr/build.gradle b/jme3-vr/build.gradle index 7a9ea7d34f..e3db2d6ead 100644 --- a/jme3-vr/build.gradle +++ b/jme3-vr/build.gradle @@ -2,7 +2,7 @@ if (!hasProperty('mainClass')) { ext.mainClass = '' } -def lwjglVersion = '3.2.0' +def lwjglVersion = '3.2.3' sourceCompatibility = '1.8' @@ -26,4 +26,4 @@ dependencies { runtime "org.lwjgl:lwjgl-openvr:${lwjglVersion}:natives-windows" runtime "org.lwjgl:lwjgl-openvr:${lwjglVersion}:natives-linux" runtime "org.lwjgl:lwjgl-openvr:${lwjglVersion}:natives-macos" -} \ No newline at end of file +} diff --git a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java index 485a563cec..3ed87b6960 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java +++ b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java @@ -1,550 +1,555 @@ -package com.jme3.app; - -import java.util.Locale; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.app.state.AppState; -import com.jme3.input.vr.VRAPI; -import com.jme3.input.vr.VRBounds; -import com.jme3.input.vr.VRInputAPI; -import com.jme3.input.vr.VRMouseManager; -import com.jme3.input.vr.VRViewManager; -import com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVR; -import com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVRMouseManager; -import com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVRViewManager; -import com.jme3.input.vr.oculus.OculusMouseManager; -import com.jme3.input.vr.oculus.OculusVR; -import com.jme3.input.vr.oculus.OculusViewManager; -import com.jme3.input.vr.openvr.OpenVR; -import com.jme3.input.vr.openvr.OpenVRMouseManager; -import com.jme3.input.vr.openvr.OpenVRViewManager; -import com.jme3.input.vr.osvr.OSVR; -import com.jme3.input.vr.osvr.OSVRViewManager; -import com.jme3.renderer.Camera; -import com.jme3.scene.Spatial; -import com.jme3.system.AppSettings; -import com.jme3.system.jopenvr.JOpenVRLibrary; -import com.jme3.util.VRGuiManager; - -/** - * - * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr - * - */ -public class VREnvironment { - - private static final Logger logger = Logger.getLogger(VREnvironment.class.getName()); - - private VRAPI hardware = null; - private VRGuiManager guiManager = null; - private VRMouseManager mouseManager = null; - private VRViewManager viewmanager = null; - - private VRBounds bounds = null; - - /** - * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}. - */ - public int vrBinding = VRConstants.SETTING_VRAPI_OPENVR_VALUE; - - private boolean seated = false; - - private Spatial observer = null; - - private boolean forceVR = false; - - private boolean vrSupportedOS = false; - - private boolean nogui = false; - - private boolean compositorOS; - - private boolean useCompositor = true; - - private boolean instanceRendering = false; - - private boolean disableSwapBuffers = true; - - private float defaultFOV = 108f; - - private float defaultAspect = 1f; - - private AppSettings settings = null; - - private Application application = null; - - private Camera dummyCam = null; - - private AppState app = null; - - private boolean initialized = false; - - - public VREnvironment(AppSettings settings){ - - this.settings = settings; - - bounds = null; - - processSettings(); - } - - /** - * Get the VR underlying hardware. - * @return the VR underlying hardware. - */ - public VRAPI getVRHardware() { - return hardware; - } - - /** - * Set the VR bounds. - * @see #getVRBounds() - */ - public void setVRBounds(VRBounds bounds){ - this.bounds = bounds; - } - - /** - * Get the VR bounds. - * @return the VR bounds. - * @see #setVRBounds(VRBounds) - */ - public VRBounds getVRBounds(){ - return bounds; - } - - /** - * Get the VR dedicated input. - * @return the VR dedicated input. - */ - public VRInputAPI getVRinput() { - if( hardware == null ){ - return null; - } - - return hardware.getVRinput(); - } - - /** - * Get the VR view manager. - * @return the VR view manager. - */ - public VRViewManager getVRViewManager() { - return viewmanager; - } - - /** - * Get the GUI manager attached to this environment. - * @return the GUI manager attached to this environment. - */ - public VRGuiManager getVRGUIManager(){ - return guiManager; - } - - /** - * Get the VR mouse manager attached to this environment. - * @return the VR mouse manager attached to this environment. - */ - public VRMouseManager getVRMouseManager(){ - return mouseManager; - } - - /** - * Can be used to change seated experience during runtime. - * @param isSeated true if designed for sitting, false for standing/roomscale - * @see #isSeatedExperience() - */ - public void setSeatedExperience(boolean isSeated) { - seated = isSeated; - if( hardware instanceof OpenVR ) { - if( hardware.getCompositor() == null ) { - return; - } - - if( seated ) { - ((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated); - } else { - ((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding); - } - } else if (hardware instanceof LWJGLOpenVR) { - if( ((LWJGLOpenVR)hardware).isInitialized() ) { - ((LWJGLOpenVR)hardware).setTrackingSpace(seated); - } - } - } - - /** - * Check if the application is configured as a seated experience. - * @return true if the application is configured as a seated experience and false otherwise. - * @see #setSeatedExperience(boolean) - */ - public boolean isSeatedExperience() { - return seated; - } - - /** - * Set the VR headset height from the ground. - * @param amount the VR headset height from the ground. - * @see #getVRHeightAdjustment() - */ - public void setVRHeightAdjustment(float amount) { - if( viewmanager != null ){ - viewmanager.setHeightAdjustment(amount); - } - } - - /** - * Get the VR headset height from the ground. - * @return the VR headset height from the ground. - * @see #setVRHeightAdjustment(float) - */ - public float getVRHeightAdjustment() { - if( viewmanager != null ){ - return viewmanager.getHeightAdjustment(); - } - return 0f; - } - - /** - * Get the scene observer. If no observer has been set, this method return the application {@link #getCamera() camera}. - * @return the scene observer. - * @see #setObserver(Spatial) - */ - public Object getObserver() { - if( observer == null ) { - - if (application != null){ - return application.getCamera(); - } else { - throw new IllegalStateException("VR environment is not attached to any application."); - } - } - return observer; - } - - /** - * Set the scene observer. The VR headset will be linked to it. If no observer is set, the VR headset is linked to the application {@link #getCamera() camera}. - * @param observer the scene observer. - */ - public void setObserver(Spatial observer) { - this.observer = observer; - } - - /** - * Get the default Field Of View (FOV) value. - * @return the default Field Of View (FOV) value. - * @see #setDefaultFOV(float) - */ - public float getDefaultFOV() { - return defaultFOV; - } - - /** - * Set the default Field Of View (FOV) value. - * @param defaultFOV the default Field Of View (FOV) value. - * @see #getDefaultFOV() - */ - public void setDefaultFOV(float defaultFOV) { - this.defaultFOV = defaultFOV; - } - - /** - * Get the default aspect ratio. - * @return the default aspect ratio. - * @see #setDefaultAspect(float) - */ - public float getDefaultAspect() { - return defaultAspect; - } - - /** - * Set the default aspect ratio. - * @param defaultAspect the default aspect ratio. - * @see #getDefaultAspect() - */ - public void setDefaultAspect(float defaultAspect) { - this.defaultAspect = defaultAspect; - } - - /** - * Get the {@link AppSettings settings} attached to this environment. - * @return the {@link AppSettings settings} attached to this environment. - * @see #setSettings(AppSettings) - */ - public AppSettings getSettings(){ - return settings; - } - - /** - * Set the {@link AppSettings settings} attached to this environment. - * @param settings the {@link AppSettings settings} attached to this environment. - * @see #getSettings() - */ - public void setSettings(AppSettings settings){ - this.settings = settings; - processSettings(); - } - - /** - * Get if the system currently support VR. - * @return true if the system currently support VR and false otherwise. - */ - public boolean isVRSupported() { - return vrSupportedOS; - } - - /** - * Check if the VR mode is enabled. - * @return true if the VR mode is enabled and false otherwise. - */ - public boolean isInVR() { - return (forceVR || vrSupportedOS && hardware != null && hardware.isInitialized() && isInitialized()); - } - - /** - * Check if the rendering is instanced (see Geometry instancing). - * @return true if the rendering is instanced and false otherwise. - */ - public boolean isInstanceRendering() { - return instanceRendering; - } - - public boolean isSwapBuffers(){ - return disableSwapBuffers; - } - - /** - * Check if the application has a GUI overlay attached. - * @return true if the application has a GUI overlay attached and false otherwise. - */ - public boolean hasTraditionalGUIOverlay() { - return !nogui; - } - - /** - * Check if the VR environment is initialized. A call to the {@link #initialize() initialize()} method should set this value to true - * @return true if the VR environment is initialized and false otherwise. - */ - public boolean isInitialized(){ - return initialized; - } - - /** - * Is the VR compositor is active. - * @return true if the VR compositor is active and false otherwise. - */ - public boolean compositorAllowed() { - return useCompositor && compositorOS; - } - - /** - * Reset headset pose if seating experience. - */ - public void resetSeatedPose(){ - if( vrSupportedOS == false || isSeatedExperience() == false ){ - return; - } - getVRHardware().reset(); - } - - public AppState getAppState(){ - return app; - } - - public Application getApplication(){ - return application; - } - - /** - * Get the {@link Camera camera} used for rendering. - * If the VR mode is {@link #isInVR() active}, this method return a dummy camera, otherwise, - * this method return the camera of the attached application. - * @return the camera attached used for rendering. - */ - public Camera getCamera() { - if( isInVR() && getVRViewManager() != null && getVRViewManager().getLeftCamera() != null ) { - return getDummyCamera(); - } - - return application.getCamera(); - } - - public Camera getDummyCamera(){ - - if (dummyCam == null){ - - if (application != null){ - - if (application.getCamera() != null){ - dummyCam = application.getCamera().clone(); - } else { - - if ((settings != null) && (settings.getWidth() != 0) && (settings.getHeight() != 0)){ - dummyCam = new Camera(settings.getWidth(), settings.getHeight()); - } else { - dummyCam = new Camera(); - } - } - } else { - throw new IllegalStateException("VR environment is not attached to any application."); - } - } - return dummyCam; - } - - /** - * Attach the VR environment to the given app state and application. - * This method should be called within the {@link AppState#stateAttached(com.jme3.app.state.AppStateManager) stateAttached(com.jme3.app.state.AppStateManager)} method - * from the app state. - * @param appState the app state to attach. - * @param application the application to attach. - */ - public void atttach(AppState appState, Application application){ - this.application = application; - this.app = appState; - - // Instanciate view manager - if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE){ - viewmanager = new OpenVRViewManager(this); - } else if (vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE){ - viewmanager = new OSVRViewManager(this); - } else if (vrBinding == VRConstants.SETTING_VRAPI_OCULUSVR_VALUE) { - viewmanager = new OculusViewManager(this); - } else if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE) { - viewmanager = new LWJGLOpenVRViewManager(this); - } else { - logger.severe("Cannot instanciate view manager, unknown VRAPI type: "+vrBinding); - } - } - - /** - * Initialize this VR environment. This method enable the system bindings and configure all the VR system modules. - * A call to this method has to be made before any use of VR capabilities. - * @return true if the VR environment is successfully initialized and false otherwise. - */ - public boolean initialize(){ - - logger.config("Initializing VR environment."); - - initialized = false; - - // we are going to use OpenVR now, not the Oculus Rift - // OpenVR does support the Rift - String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); - vrSupportedOS = !OS.contains("nux") && System.getProperty("sun.arch.data.model").equalsIgnoreCase("64"); //for the moment, linux/unix causes crashes, 64-bit only - compositorOS = OS.contains("indows"); - - if( vrSupportedOS) { - if( vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE ) { - - guiManager = new VRGuiManager(this); - mouseManager = new OpenVRMouseManager(this); - - hardware = new OSVR(this); - initialized = true; - logger.config("Creating OSVR wrapper [SUCCESS]"); - } else if( vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE ) { - - guiManager = new VRGuiManager(this); - mouseManager = new OpenVRMouseManager(this); - - hardware = new OpenVR(this); - initialized = true; - logger.config("Creating OpenVR wrapper [SUCCESS]"); - } else if (vrBinding == VRConstants.SETTING_VRAPI_OCULUSVR_VALUE) { - - guiManager = new VRGuiManager(this); - mouseManager = new OculusMouseManager(this); - - hardware = new OculusVR(this); - initialized = true; - logger.config("Creating Occulus Rift wrapper [SUCCESS]"); - } else if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE) { - - guiManager = new VRGuiManager(this); - mouseManager = new LWJGLOpenVRMouseManager(this); - - hardware = new LWJGLOpenVR(this); - initialized = true; - logger.config("Creating OpenVR/LWJGL wrapper [SUCCESS]"); - } else { - logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]"); - logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); - } - - if( hardware.initialize() ) { - initialized &= true; - logger.config("VR native wrapper initialized [SUCCESS]"); - } else { - initialized &= false; - logger.warning("VR native wrapper initialized [FAILED]"); - logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); - } - } else { - logger.log(Level.SEVERE, "System does not support VR capabilities."); - logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); - } - - return initialized; - } - - private void processSettings(){ - if (settings != null){ - - if (settings.get(VRConstants.SETTING_USE_COMPOSITOR) != null){ - useCompositor = settings.getBoolean(VRConstants.SETTING_USE_COMPOSITOR); - if( useCompositor == false ){ - disableSwapBuffers = false; - } - } - - if (settings.get(VRConstants.SETTING_ENABLE_MIRROR_WINDOW) != null){ - if( useCompositor == false ) { - disableSwapBuffers = false; - } else { - disableSwapBuffers = !settings.getBoolean(VRConstants.SETTING_ENABLE_MIRROR_WINDOW); - } - } - - if (settings.get(VRConstants.SETTING_GUI_OVERDRAW) != null){ - getVRGUIManager().setGuiOverdraw(settings.getBoolean(VRConstants.SETTING_GUI_OVERDRAW)); - } - - if (settings.get(VRConstants.SETTING_GUI_CURVED_SURFACE) != null){ - getVRGUIManager().setCurvedSurface(settings.getBoolean(VRConstants.SETTING_GUI_CURVED_SURFACE)); - } - - if (settings.get(VRConstants.SETTING_NO_GUI) != null){ - nogui = settings.getBoolean(VRConstants.SETTING_NO_GUI); - } - - if (settings.get(VRConstants.SETTING_VRAPI) != null){ - vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI); - } - - if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){ - seated = settings.getBoolean(VRConstants.SETTING_SEATED_EXPERIENCE); - } - - if (settings.get(VRConstants.SETTING_INSTANCE_RENDERING) != null){ - instanceRendering = settings.getBoolean(VRConstants.SETTING_INSTANCE_RENDERING); - } - - if (settings.get(VRConstants.SETTING_DEFAULT_FOV) != null){ - defaultFOV = settings.getFloat(VRConstants.SETTING_DEFAULT_FOV); - } - - if (settings.get(VRConstants.SETTING_DEFAULT_ASPECT_RATIO) != null){ - defaultAspect = settings.getFloat(VRConstants.SETTING_DEFAULT_ASPECT_RATIO); - } - - if (settings.get(VRConstants.SETTING_FLIP_EYES) != null){ - if( getVRHardware() != null ){ - getVRHardware().setFlipEyes(settings.getBoolean(VRConstants.SETTING_FLIP_EYES)); - } - } - } - } -} +package com.jme3.app; + +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.jme3.app.state.AppState; +import com.jme3.input.vr.VRAPI; +import com.jme3.input.vr.VRBounds; +import com.jme3.input.vr.VRInputAPI; +import com.jme3.input.vr.VRMouseManager; +import com.jme3.input.vr.VRViewManager; +import com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVR; +import com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVRMouseManager; +import com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVRViewManager; +import com.jme3.input.vr.oculus.OculusMouseManager; +import com.jme3.input.vr.oculus.OculusVR; +import com.jme3.input.vr.oculus.OculusViewManager; +import com.jme3.input.vr.openvr.OpenVR; +import com.jme3.input.vr.openvr.OpenVRMouseManager; +import com.jme3.input.vr.openvr.OpenVRViewManager; +import com.jme3.input.vr.osvr.OSVR; +import com.jme3.input.vr.osvr.OSVRViewManager; +import com.jme3.renderer.Camera; +import com.jme3.scene.Spatial; +import com.jme3.system.AppSettings; +import com.jme3.system.jopenvr.JOpenVRLibrary; +import com.jme3.util.VRGuiManager; + +/** + * + * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr + * + */ +public class VREnvironment { + + private static final Logger logger = Logger.getLogger(VREnvironment.class.getName()); + + private VRAPI hardware = null; + private VRGuiManager guiManager = null; + private VRMouseManager mouseManager = null; + private VRViewManager viewmanager = null; + + private VRBounds bounds = null; + + /** + * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}. + */ + public int vrBinding = VRConstants.SETTING_VRAPI_OPENVR_VALUE; + + private boolean seated = false; + + private Spatial observer = null; + + private boolean forceVR = false; + + private boolean vrSupportedOS = false; + + private boolean nogui = false; + + private boolean compositorOS; + + private boolean useCompositor = true; + + private boolean instanceRendering = false; + + private boolean disableSwapBuffers = true; + + private float defaultFOV = 108f; + + private float defaultAspect = 1f; + + private AppSettings settings = null; + + private Application application = null; + + private Camera dummyCam = null; + + private AppState app = null; + + private boolean initialized = false; + + + public VREnvironment(AppSettings settings){ + + this.settings = settings; + + bounds = null; + + processSettings(); + } + + /** + * Get the VR underlying hardware. + * @return the VR underlying hardware. + */ + public VRAPI getVRHardware() { + return hardware; + } + + /** + * Set the VR bounds. + * @see #getVRBounds() + */ + public void setVRBounds(VRBounds bounds){ + this.bounds = bounds; + } + + /** + * Get the VR bounds. + * @return the VR bounds. + * @see #setVRBounds(VRBounds) + */ + public VRBounds getVRBounds(){ + return bounds; + } + + /** + * Get the VR dedicated input. + * @return the VR dedicated input. + */ + public VRInputAPI getVRinput() { + if( hardware == null ){ + return null; + } + + return hardware.getVRinput(); + } + + /** + * Get the VR view manager. + * @return the VR view manager. + */ + public VRViewManager getVRViewManager() { + return viewmanager; + } + + /** + * Get the GUI manager attached to this environment. + * @return the GUI manager attached to this environment. + */ + public VRGuiManager getVRGUIManager(){ + return guiManager; + } + + /** + * Get the VR mouse manager attached to this environment. + * @return the VR mouse manager attached to this environment. + */ + public VRMouseManager getVRMouseManager(){ + return mouseManager; + } + + /** + * Can be used to change seated experience during runtime. + * @param isSeated true if designed for sitting, false for standing/roomscale + * @see #isSeatedExperience() + */ + public void setSeatedExperience(boolean isSeated) { + seated = isSeated; + if( hardware instanceof OpenVR ) { + if( hardware.getCompositor() == null ) { + return; + } + + if( seated ) { + ((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated); + } else { + ((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding); + } + } else if (hardware instanceof LWJGLOpenVR) { + if( ((LWJGLOpenVR)hardware).isInitialized() ) { + ((LWJGLOpenVR)hardware).setTrackingSpace(seated); + } + } + } + + /** + * Check if the application is configured as a seated experience. + * @return true if the application is configured as a seated experience and false otherwise. + * @see #setSeatedExperience(boolean) + */ + public boolean isSeatedExperience() { + return seated; + } + + /** + * Set the VR headset height from the ground. + * @param amount the VR headset height from the ground. + * @see #getVRHeightAdjustment() + */ + public void setVRHeightAdjustment(float amount) { + if( viewmanager != null ){ + viewmanager.setHeightAdjustment(amount); + } + } + + /** + * Get the VR headset height from the ground. + * @return the VR headset height from the ground. + * @see #setVRHeightAdjustment(float) + */ + public float getVRHeightAdjustment() { + if( viewmanager != null ){ + return viewmanager.getHeightAdjustment(); + } + return 0f; + } + + /** + * Get the scene observer. If no observer has been set, this method return the application {@link #getCamera() camera}. + * @return the scene observer. + * @see #setObserver(Spatial) + */ + public Object getObserver() { + if( observer == null ) { + + if (application != null){ + return application.getCamera(); + } else { + throw new IllegalStateException("VR environment is not attached to any application."); + } + } + return observer; + } + + /** + * Set the scene observer. The VR headset will be linked to it. If no observer is set, the VR headset is linked to the application {@link #getCamera() camera}. + * @param observer the scene observer. + */ + public void setObserver(Spatial observer) { + this.observer = observer; + } + + /** + * Get the default Field Of View (FOV) value. + * @return the default Field Of View (FOV) value. + * @see #setDefaultFOV(float) + */ + public float getDefaultFOV() { + return defaultFOV; + } + + /** + * Set the default Field Of View (FOV) value. + * @param defaultFOV the default Field Of View (FOV) value. + * @see #getDefaultFOV() + */ + public void setDefaultFOV(float defaultFOV) { + this.defaultFOV = defaultFOV; + } + + /** + * Get the default aspect ratio. + * @return the default aspect ratio. + * @see #setDefaultAspect(float) + */ + public float getDefaultAspect() { + return defaultAspect; + } + + /** + * Set the default aspect ratio. + * @param defaultAspect the default aspect ratio. + * @see #getDefaultAspect() + */ + public void setDefaultAspect(float defaultAspect) { + this.defaultAspect = defaultAspect; + } + + /** + * Get the {@link AppSettings settings} attached to this environment. + * @return the {@link AppSettings settings} attached to this environment. + * @see #setSettings(AppSettings) + */ + public AppSettings getSettings(){ + return settings; + } + + /** + * Set the {@link AppSettings settings} attached to this environment. + * @param settings the {@link AppSettings settings} attached to this environment. + * @see #getSettings() + */ + public void setSettings(AppSettings settings){ + this.settings = settings; + processSettings(); + } + + /** + * Get if the system currently support VR. + * @return true if the system currently support VR and false otherwise. + */ + public boolean isVRSupported() { + return vrSupportedOS; + } + + /** + * Check if the VR mode is enabled. + * @return true if the VR mode is enabled and false otherwise. + */ + public boolean isInVR() { + return (forceVR || vrSupportedOS && hardware != null && hardware.isInitialized() && isInitialized()); + } + + /** + * Check if the rendering is instanced (see Geometry instancing). + * @return true if the rendering is instanced and false otherwise. + */ + public boolean isInstanceRendering() { + return instanceRendering; + } + + public boolean isSwapBuffers(){ + return disableSwapBuffers; + } + + /** + * Check if the application has a GUI overlay attached. + * @return true if the application has a GUI overlay attached and false otherwise. + */ + public boolean hasTraditionalGUIOverlay() { + return !nogui; + } + + /** + * Check if the VR environment is initialized. A call to the {@link #initialize() initialize()} method should set this value to true + * @return true if the VR environment is initialized and false otherwise. + */ + public boolean isInitialized(){ + return initialized; + } + + /** + * Is the VR compositor is active. + * @return true if the VR compositor is active and false otherwise. + */ + public boolean compositorAllowed() { + return useCompositor && compositorOS; + } + + /** + * Reset headset pose if seating experience. + */ + public void resetSeatedPose(){ + if( vrSupportedOS == false || isSeatedExperience() == false ){ + return; + } + getVRHardware().reset(); + } + + public AppState getAppState(){ + return app; + } + + public Application getApplication(){ + return application; + } + + /** + * Get the {@link Camera camera} used for rendering. + * If the VR mode is {@link #isInVR() active}, this method return a dummy camera, otherwise, + * this method return the camera of the attached application. + * @return the camera attached used for rendering. + */ + public Camera getCamera() { + if( isInVR() && getVRViewManager() != null && getVRViewManager().getLeftCamera() != null ) { + return getDummyCamera(); + } + + return application.getCamera(); + } + + public Camera getDummyCamera(){ + + if (dummyCam == null){ + + if (application != null){ + + if (application.getCamera() != null){ + dummyCam = application.getCamera().clone(); + } else { + + if ((settings != null) && (settings.getWidth() != 0) && (settings.getHeight() != 0)){ + dummyCam = new Camera(settings.getWidth(), settings.getHeight()); + } else { + dummyCam = new Camera(); + } + } + } else { + throw new IllegalStateException("VR environment is not attached to any application."); + } + } + return dummyCam; + } + + /** + * Attach the VR environment to the given app state and application. + * This method should be called within the {@link AppState#stateAttached(com.jme3.app.state.AppStateManager) stateAttached(com.jme3.app.state.AppStateManager)} method + * from the app state. + * @param appState the app state to attach. + * @param application the application to attach. + */ + public void atttach(AppState appState, Application application){ + this.application = application; + this.app = appState; + + // Instanciate view manager + if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE){ + viewmanager = new OpenVRViewManager(this); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE){ + viewmanager = new OSVRViewManager(this); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OCULUSVR_VALUE) { + viewmanager = new OculusViewManager(this); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE) { + viewmanager = new LWJGLOpenVRViewManager(this); + } else { + logger.severe("Cannot instanciate view manager, unknown VRAPI type: "+vrBinding); + } + } + + /** + * Initialize this VR environment. This method enable the system bindings and configure all the VR system modules. + * A call to this method has to be made before any use of VR capabilities. + * @return true if the VR environment is successfully initialized and false otherwise. + */ + public boolean initialize(){ + + logger.config("Initializing VR environment."); + + initialized = false; + + // we are going to use OpenVR now, not the Oculus Rift + // OpenVR does support the Rift + String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); + vrSupportedOS = System.getProperty("sun.arch.data.model").equalsIgnoreCase("64"); //64-bit only + compositorOS = OS.contains("indows") || OS.contains("nux"); + + if (OS.contains("nux") && vrBinding != VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE){ + logger.severe("Only LWJGL VR backend is currently (partially) supported on Linux."); + vrSupportedOS = false; + } + + if( vrSupportedOS) { + if( vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE ) { + + guiManager = new VRGuiManager(this); + mouseManager = new OpenVRMouseManager(this); + + hardware = new OSVR(this); + initialized = true; + logger.config("Creating OSVR wrapper [SUCCESS]"); + } else if( vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE ) { + + guiManager = new VRGuiManager(this); + mouseManager = new OpenVRMouseManager(this); + + hardware = new OpenVR(this); + initialized = true; + logger.config("Creating OpenVR wrapper [SUCCESS]"); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OCULUSVR_VALUE) { + + guiManager = new VRGuiManager(this); + mouseManager = new OculusMouseManager(this); + + hardware = new OculusVR(this); + initialized = true; + logger.config("Creating Occulus Rift wrapper [SUCCESS]"); + } else if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE) { + + guiManager = new VRGuiManager(this); + mouseManager = new LWJGLOpenVRMouseManager(this); + + hardware = new LWJGLOpenVR(this); + initialized = true; + logger.config("Creating OpenVR/LWJGL wrapper [SUCCESS]"); + } else { + logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]"); + logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); + } + + if( hardware.initialize() ) { + initialized &= true; + logger.config("VR native wrapper initialized [SUCCESS]"); + } else { + initialized &= false; + logger.warning("VR native wrapper initialized [FAILED]"); + logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); + } + } else { + logger.log(Level.SEVERE, "System does not support VR capabilities."); + logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); + } + + return initialized; + } + + private void processSettings(){ + if (settings != null){ + + if (settings.get(VRConstants.SETTING_USE_COMPOSITOR) != null){ + useCompositor = settings.getBoolean(VRConstants.SETTING_USE_COMPOSITOR); + if( useCompositor == false ){ + disableSwapBuffers = false; + } + } + + if (settings.get(VRConstants.SETTING_ENABLE_MIRROR_WINDOW) != null){ + if( useCompositor == false ) { + disableSwapBuffers = false; + } else { + disableSwapBuffers = !settings.getBoolean(VRConstants.SETTING_ENABLE_MIRROR_WINDOW); + } + } + + if (settings.get(VRConstants.SETTING_GUI_OVERDRAW) != null){ + getVRGUIManager().setGuiOverdraw(settings.getBoolean(VRConstants.SETTING_GUI_OVERDRAW)); + } + + if (settings.get(VRConstants.SETTING_GUI_CURVED_SURFACE) != null){ + getVRGUIManager().setCurvedSurface(settings.getBoolean(VRConstants.SETTING_GUI_CURVED_SURFACE)); + } + + if (settings.get(VRConstants.SETTING_NO_GUI) != null){ + nogui = settings.getBoolean(VRConstants.SETTING_NO_GUI); + } + + if (settings.get(VRConstants.SETTING_VRAPI) != null){ + vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI); + } + + if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){ + seated = settings.getBoolean(VRConstants.SETTING_SEATED_EXPERIENCE); + } + + if (settings.get(VRConstants.SETTING_INSTANCE_RENDERING) != null){ + instanceRendering = settings.getBoolean(VRConstants.SETTING_INSTANCE_RENDERING); + } + + if (settings.get(VRConstants.SETTING_DEFAULT_FOV) != null){ + defaultFOV = settings.getFloat(VRConstants.SETTING_DEFAULT_FOV); + } + + if (settings.get(VRConstants.SETTING_DEFAULT_ASPECT_RATIO) != null){ + defaultAspect = settings.getFloat(VRConstants.SETTING_DEFAULT_ASPECT_RATIO); + } + + if (settings.get(VRConstants.SETTING_FLIP_EYES) != null){ + if( getVRHardware() != null ){ + getVRHardware().setFlipEyes(settings.getBoolean(VRConstants.SETTING_FLIP_EYES)); + } + } + } + } +} diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/HmdType.java b/jme3-vr/src/main/java/com/jme3/input/vr/HmdType.java index 5ad535d729..cffc1b35d4 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/HmdType.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/HmdType.java @@ -1,64 +1,69 @@ -package com.jme3.input.vr; - -/** - * The type of VR Head Mounted Device (HMD) - * @author reden - phr00t - https://github.com/phr00t - * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr - */ -public enum HmdType { - - /** - * HTC vive Head Mounted Device (HMD). - */ - HTC_VIVE, - - /** - * Occulus Rift Head Mounted Device (HMD). - */ - OCULUS_RIFT, - - /** - * OSVR generic Head Mounted Device (HMD). - */ - OSVR, - - /** - * FOVE Head Mounted Device (HMD). - */ - FOVE, - - /** - * STARVR Head Mounted Device (HMD). - */ - STARVR, - - /** - * GameFace Head Mounted Device (HMD). - */ - GAMEFACE, - - /** - * PlayStation VR (formely Morpheus) Head Mounted Device (HMD). - */ - MORPHEUS, - - /** - * Samsung GearVR Head Mounted Device (HMD). - */ - GEARVR, - - /** - * a null Head Mounted Device (HMD). - */ - NULL, - - /** - * a none Head Mounted Device (HMD). - */ - NONE, - - /** - * a not referenced Head Mounted Device (HMD). - */ - OTHER +package com.jme3.input.vr; + +/** + * The type of VR Head Mounted Device (HMD) + * @author reden - phr00t - https://github.com/phr00t + * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr + */ +public enum HmdType { + + /** + * HTC vive Head Mounted Device (HMD). + */ + HTC_VIVE, + + /** + * Valve Index Head Mounted Device (HMD). + */ + VALVE_INDEX, + + /** + * Occulus Rift Head Mounted Device (HMD). + */ + OCULUS_RIFT, + + /** + * OSVR generic Head Mounted Device (HMD). + */ + OSVR, + + /** + * FOVE Head Mounted Device (HMD). + */ + FOVE, + + /** + * STARVR Head Mounted Device (HMD). + */ + STARVR, + + /** + * GameFace Head Mounted Device (HMD). + */ + GAMEFACE, + + /** + * PlayStation VR (formely Morpheus) Head Mounted Device (HMD). + */ + MORPHEUS, + + /** + * Samsung GearVR Head Mounted Device (HMD). + */ + GEARVR, + + /** + * a null Head Mounted Device (HMD). + */ + NULL, + + /** + * a none Head Mounted Device (HMD). + */ + NONE, + + /** + * a not referenced Head Mounted Device (HMD). + */ + OTHER } \ No newline at end of file diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVR.java index 3e7c22cd34..7615ae8449 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVR.java @@ -421,6 +421,8 @@ public HmdType getType() { completeName = completeName.toLowerCase(Locale.ENGLISH).trim(); if( completeName.contains("htc") || completeName.contains("vive") ) { return HmdType.HTC_VIVE; + } else if ( completeName.contains("index") ) { + return HmdType.VALVE_INDEX; } else if( completeName.contains("osvr") ) { return HmdType.OSVR; } else if( completeName.contains("oculus") || completeName.contains("rift") || diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVR.java index ef70bccbc1..a4d76018be 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVR.java @@ -1,595 +1,597 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3.input.vr.openvr; - -import com.jme3.app.VREnvironment; -import com.jme3.input.vr.HmdType; -import com.jme3.input.vr.VRAPI; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.renderer.Camera; -import com.jme3.system.jopenvr.HmdMatrix34_t; -import com.jme3.system.jopenvr.HmdMatrix44_t; -import com.jme3.system.jopenvr.JOpenVRLibrary; -import com.jme3.system.jopenvr.OpenVRUtil; -import com.jme3.system.jopenvr.TrackedDevicePose_t; -import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable; -import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; -import com.jme3.system.jopenvr.VR_IVRTrackedCamera_FnTable; -import com.jme3.util.VRUtil; -import com.sun.jna.Memory; -import com.sun.jna.Pointer; -import com.sun.jna.ptr.FloatByReference; -import com.sun.jna.ptr.IntByReference; -import com.sun.jna.ptr.LongByReference; - -import java.nio.IntBuffer; -import java.util.Locale; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A class that wraps an OpenVR system. - * @author reden - phr00t - https://github.com/phr00t - * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr - */ -public class OpenVR implements VRAPI { - - private static final Logger logger = Logger.getLogger(OpenVR.class.getName()); - - private static VR_IVRCompositor_FnTable compositorFunctions; - private static VR_IVRSystem_FnTable vrsystemFunctions; - private static VR_IVRTrackedCamera_FnTable cameraFunctions; - - private static boolean initSuccess = false; - private static boolean flipEyes = false; - - private IntBuffer hmdDisplayFrequency; - private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; - protected TrackedDevicePose_t[] hmdTrackedDevicePoses; - - protected IntByReference hmdErrorStore; - - private final Quaternion rotStore = new Quaternion(); - private final Vector3f posStore = new Vector3f(); - - private static FloatByReference tlastVsync; - - /** - * The actual frame count. - */ - public static LongByReference _tframeCount; - - // for debugging latency - private int frames = 0; - - protected Matrix4f[] poseMatrices; - - private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); - private Matrix4f hmdProjectionLeftEye; - private Matrix4f hmdProjectionRightEye; - private Matrix4f hmdPoseLeftEye; - private Matrix4f hmdPoseRightEye; - - private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; - - private float vsyncToPhotons; - private double timePerFrame, frameCountRun; - private long frameCount; - private OpenVRInput VRinput; - - - private VREnvironment environment = null; - - /** - * Convert specific OpenVR {@link com.jme3.system.jopenvr.HmdMatrix34_t HmdMatrix34_t} into JME {@link Matrix4f Matrix4f} - * @param hmdMatrix the input matrix - * @param mat the converted matrix - * @return the converted matrix - */ - public static Matrix4f convertSteamVRMatrix3ToMatrix4f(com.jme3.system.jopenvr.HmdMatrix34_t hmdMatrix, Matrix4f mat){ - mat.set(hmdMatrix.m[0], hmdMatrix.m[1], hmdMatrix.m[2], hmdMatrix.m[3], - hmdMatrix.m[4], hmdMatrix.m[5], hmdMatrix.m[6], hmdMatrix.m[7], - hmdMatrix.m[8], hmdMatrix.m[9], hmdMatrix.m[10], hmdMatrix.m[11], - 0f, 0f, 0f, 1f); - return mat; - } - - /** - * Convert specific OpenVR {@link com.jme3.system.jopenvr.HmdMatrix44_t HmdMatrix34_t} into JME {@link Matrix4f Matrix4f} - * @param hmdMatrix the input matrix - * @param mat the converted matrix - * @return the converted matrix - */ - public static Matrix4f convertSteamVRMatrix4ToMatrix4f(com.jme3.system.jopenvr.HmdMatrix44_t hmdMatrix, Matrix4f mat){ - mat.set(hmdMatrix.m[0], hmdMatrix.m[1], hmdMatrix.m[2], hmdMatrix.m[3], - hmdMatrix.m[4], hmdMatrix.m[5], hmdMatrix.m[6], hmdMatrix.m[7], - hmdMatrix.m[8], hmdMatrix.m[9], hmdMatrix.m[10], hmdMatrix.m[11], - hmdMatrix.m[12], hmdMatrix.m[13], hmdMatrix.m[14], hmdMatrix.m[15]); - return mat; - } - - - /** - * Create a new OpenVR system - * attached to the given {@link VREnvironment VR environment}. - * @param environment the VR environment to which this API is attached. - */ - public OpenVR(VREnvironment environment){ - this.environment = environment; - } - - @Override - public OpenVRInput getVRinput() { - return VRinput; - } - - @Override - public VR_IVRSystem_FnTable getVRSystem() { - return vrsystemFunctions; - } - - @Override - public VR_IVRCompositor_FnTable getCompositor() { - return compositorFunctions; - } - - public VR_IVRTrackedCamera_FnTable getTrackedCamera(){ - return cameraFunctions; - } - - @Override - public String getName() { - return "OpenVR"; - } - - private static long latencyWaitTime = 0; - - @Override - public void setFlipEyes(boolean set) { - flipEyes = set; - } - - private boolean enableDebugLatency = false; - - @Override - public void printLatencyInfoToConsole(boolean set) { - enableDebugLatency = set; - } - - @Override - public int getDisplayFrequency() { - if( hmdDisplayFrequency == null ) return 0; - return hmdDisplayFrequency.get(0); - } - - @Override - public boolean initialize() { - - logger.config("Initializing OpenVR system..."); - - hmdErrorStore = new IntByReference(); - vrsystemFunctions = null; - - // Init the native linking to the OpenVR library. - try{ - JOpenVRLibrary.init(); - } catch(Throwable t){ - logger.log(Level.SEVERE, "Cannot link to OpenVR system library: "+t.getMessage(), t); - return false; - } - - JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene); - - if( hmdErrorStore.getValue() == 0 ) { - vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer()); - } - - if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) { - logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0)); - logger.severe("Initializing OpenVR system [FAILED]"); - return false; - } else { - logger.config("OpenVR initialized & VR connected."); - - vrsystemFunctions.setAutoSynch(false); - vrsystemFunctions.read(); - - - tlastVsync = new FloatByReference(); - _tframeCount = new LongByReference(); - - hmdDisplayFrequency = IntBuffer.allocate(1); - hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float); - hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference(); - hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount); - poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - for(int i=0;itrue is the use of the headset camera is allowed and false otherwise. - */ - public void initCamera(boolean allowed) { - hmdErrorStore.setValue(0); // clear the error store - - if( allowed && vrsystemFunctions != null ) { - IntByReference intptr = JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRTrackedCamera_Version, hmdErrorStore); - if (intptr != null){ - cameraFunctions = new VR_IVRTrackedCamera_FnTable(intptr.getPointer()); - if(cameraFunctions != null && hmdErrorStore.getValue() == 0 ){ - cameraFunctions.setAutoSynch(false); - cameraFunctions.read(); - logger.config("OpenVR Camera initialized"); - } - } - } - } - - @Override - public void destroy() { - JOpenVRLibrary.VR_ShutdownInternal(); - } - - @Override - public boolean isInitialized() { - return initSuccess; - } - - @Override - public void reset() { - if( vrsystemFunctions == null ) return; - vrsystemFunctions.ResetSeatedZeroPose.apply(); - hmdSeatToStand = null; - } - - @Override - public void getRenderSize(Vector2f store) { - if( vrsystemFunctions == null ) { - // 1344x1512 - store.x = 1344f; - store.y = 1512f; - } else { - IntByReference x = new IntByReference(); - IntByReference y = new IntByReference(); - vrsystemFunctions.GetRecommendedRenderTargetSize.apply(x, y); - store.x = x.getValue(); - store.y = y.getValue(); - } - } - /* - @Override - public float getFOV(int dir) { - float val = 0f; - if( vrsystemFunctions != null ) { - val = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, dir, hmdErrorStore); - } - // verification of number - if( val == 0f ) { - return 55f; - } else if( val <= 10f ) { - // most likely a radian number - return val * 57.2957795f; - } - return val; - } - */ - - @Override - public float getInterpupillaryDistance() { - if( vrsystemFunctions == null ) return 0.065f; - return vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_UserIpdMeters_Float, hmdErrorStore); - } - - @Override - public Quaternion getOrientation() { - VRUtil.convertMatrix4toQuat(hmdPose, rotStore); - return rotStore; - } - - @Override - public Vector3f getPosition() { - // the hmdPose comes in rotated funny, fix that here - hmdPose.toTranslationVector(posStore); - posStore.x = -posStore.x; - posStore.z = -posStore.z; - return posStore; - } - - @Override - public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) { - hmdPose.toTranslationVector(storePos); - storePos.x = -storePos.x; - storePos.z = -storePos.z; - storeRot.set(getOrientation()); - } - - @Override - public void updatePose(){ - if(vrsystemFunctions == null) return; - if(compositorFunctions != null) { - compositorFunctions.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0); - } else { - // wait - if( latencyWaitTime > 0 ) VRUtil.sleepNanos(latencyWaitTime); - - vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount); - float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons; - - if( enableDebugLatency ) { - if( frames == 10 ) { - System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime)); - System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons)); - } - frames = (frames + 1) % 60; - } - - // handle skipping frame stuff - long nowCount = _tframeCount.getValue(); - if( nowCount - frameCount > 1 ) { - // skipped a frame! - if( enableDebugLatency ) System.out.println("Frame skipped!"); - frameCountRun = 0; - if( latencyWaitTime > 0 ) { - latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1); - if( latencyWaitTime < 0 ) latencyWaitTime = 0; - } - } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) { - // didn't skip a frame, lets try waiting longer to improve latency - frameCountRun++; - latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0)); - } - - frameCount = nowCount; - - vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply( - environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated: - JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding, - fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount); - } - - // deal with controllers being plugged in and out - // causing an invalid memory crash... skipping for now - /*boolean hasEvent = false; - while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) { - // wait until the events are clear.. - hasEvent = true; - } - if( hasEvent ) { - // an event probably changed controller state - VRInput._updateConnectedControllers(); - }*/ - //update controllers pose information - environment.getVRinput().updateControllerStates(); - - // read pose data from native - for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){ - hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid"); - if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){ - hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking"); - convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]); - } - } - - if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){ - hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]); - } else { - hmdPose.set(Matrix4f.IDENTITY); - } - } - - @Override - public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){ - if( hmdProjectionLeftEye != null ) { - return hmdProjectionLeftEye; - } else if(vrsystemFunctions == null){ - return cam.getProjectionMatrix(); - } else { - HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar()); - hmdProjectionLeftEye = new Matrix4f(); - convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye); - return hmdProjectionLeftEye; - } - } - - @Override - public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){ - if( hmdProjectionRightEye != null ) { - return hmdProjectionRightEye; - } else if(vrsystemFunctions == null){ - return cam.getProjectionMatrix(); - } else { - HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar()); - hmdProjectionRightEye = new Matrix4f(); - convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye); - return hmdProjectionRightEye; - } - } - - @Override - public Vector3f getHMDVectorPoseLeftEye() { - if( hmdPoseLeftEyeVec == null ) { - hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector(); - // set default IPD if none or broken - if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) { - hmdPoseLeftEyeVec.x = 0.065f * -0.5f; - } - if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping - } - return hmdPoseLeftEyeVec; - } - - @Override - public Vector3f getHMDVectorPoseRightEye() { - if( hmdPoseRightEyeVec == null ) { - hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector(); - // set default IPD if none or broken - if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) { - hmdPoseRightEyeVec.x = 0.065f * 0.5f; - } - if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping - } - return hmdPoseRightEyeVec; - } - - @Override - public Vector3f getSeatedToAbsolutePosition() { - if( environment.isSeatedExperience() == false ) return Vector3f.ZERO; - if( hmdSeatToStand == null ) { - hmdSeatToStand = new Vector3f(); - HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply(); - Matrix4f tempmat = new Matrix4f(); - convertSteamVRMatrix3ToMatrix4f(mat, tempmat); - tempmat.toTranslationVector(hmdSeatToStand); - } - return hmdSeatToStand; - } - - @Override - public Matrix4f getHMDMatrixPoseLeftEye(){ - if( hmdPoseLeftEye != null ) { - return hmdPoseLeftEye; - } else if(vrsystemFunctions == null) { - return Matrix4f.IDENTITY; - } else { - HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left); - hmdPoseLeftEye = new Matrix4f(); - return convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye); - } - } - - @Override - public HmdType getType() { - if( vrsystemFunctions != null ) { - Pointer str1 = new Memory(128); - Pointer str2 = new Memory(128); - String completeName = ""; - vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, - JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String, - str1, 128, hmdErrorStore); - if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0); - vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, - JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String, - str2, 128, hmdErrorStore); - if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0); - if( completeName.length() > 0 ) { - completeName = completeName.toLowerCase(Locale.ENGLISH).trim(); - if( completeName.contains("htc") || completeName.contains("vive") ) { - return HmdType.HTC_VIVE; - } else if( completeName.contains("osvr") ) { - return HmdType.OSVR; - } else if( completeName.contains("oculus") || completeName.contains("rift") || - completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) { - return HmdType.OCULUS_RIFT; - } else if( completeName.contains("fove") ) { - return HmdType.FOVE; - } else if( completeName.contains("game") && completeName.contains("face") ) { - return HmdType.GAMEFACE; - } else if( completeName.contains("morpheus") ) { - return HmdType.MORPHEUS; - } else if( completeName.contains("gear") ) { - return HmdType.GEARVR; - } else if( completeName.contains("star") ) { - return HmdType.STARVR; - } else if( completeName.contains("null") ) { - return HmdType.NULL; - } - } - } else return HmdType.NONE; - return HmdType.OTHER; - } - - @Override - public Matrix4f getHMDMatrixPoseRightEye(){ - if( hmdPoseRightEye != null ) { - return hmdPoseRightEye; - } else if(vrsystemFunctions == null) { - return Matrix4f.IDENTITY; - } else { - HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right); - hmdPoseRightEye = new Matrix4f(); - return convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.input.vr.openvr; + +import com.jme3.app.VREnvironment; +import com.jme3.input.vr.HmdType; +import com.jme3.input.vr.VRAPI; +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.system.jopenvr.HmdMatrix34_t; +import com.jme3.system.jopenvr.HmdMatrix44_t; +import com.jme3.system.jopenvr.JOpenVRLibrary; +import com.jme3.system.jopenvr.OpenVRUtil; +import com.jme3.system.jopenvr.TrackedDevicePose_t; +import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable; +import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; +import com.jme3.system.jopenvr.VR_IVRTrackedCamera_FnTable; +import com.jme3.util.VRUtil; +import com.sun.jna.Memory; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.FloatByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +import java.nio.IntBuffer; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A class that wraps an OpenVR system. + * @author reden - phr00t - https://github.com/phr00t + * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr + */ +public class OpenVR implements VRAPI { + + private static final Logger logger = Logger.getLogger(OpenVR.class.getName()); + + private static VR_IVRCompositor_FnTable compositorFunctions; + private static VR_IVRSystem_FnTable vrsystemFunctions; + private static VR_IVRTrackedCamera_FnTable cameraFunctions; + + private static boolean initSuccess = false; + private static boolean flipEyes = false; + + private IntBuffer hmdDisplayFrequency; + private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; + protected TrackedDevicePose_t[] hmdTrackedDevicePoses; + + protected IntByReference hmdErrorStore; + + private final Quaternion rotStore = new Quaternion(); + private final Vector3f posStore = new Vector3f(); + + private static FloatByReference tlastVsync; + + /** + * The actual frame count. + */ + public static LongByReference _tframeCount; + + // for debugging latency + private int frames = 0; + + protected Matrix4f[] poseMatrices; + + private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); + private Matrix4f hmdProjectionLeftEye; + private Matrix4f hmdProjectionRightEye; + private Matrix4f hmdPoseLeftEye; + private Matrix4f hmdPoseRightEye; + + private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; + + private float vsyncToPhotons; + private double timePerFrame, frameCountRun; + private long frameCount; + private OpenVRInput VRinput; + + + private VREnvironment environment = null; + + /** + * Convert specific OpenVR {@link com.jme3.system.jopenvr.HmdMatrix34_t HmdMatrix34_t} into JME {@link Matrix4f Matrix4f} + * @param hmdMatrix the input matrix + * @param mat the converted matrix + * @return the converted matrix + */ + public static Matrix4f convertSteamVRMatrix3ToMatrix4f(com.jme3.system.jopenvr.HmdMatrix34_t hmdMatrix, Matrix4f mat){ + mat.set(hmdMatrix.m[0], hmdMatrix.m[1], hmdMatrix.m[2], hmdMatrix.m[3], + hmdMatrix.m[4], hmdMatrix.m[5], hmdMatrix.m[6], hmdMatrix.m[7], + hmdMatrix.m[8], hmdMatrix.m[9], hmdMatrix.m[10], hmdMatrix.m[11], + 0f, 0f, 0f, 1f); + return mat; + } + + /** + * Convert specific OpenVR {@link com.jme3.system.jopenvr.HmdMatrix44_t HmdMatrix34_t} into JME {@link Matrix4f Matrix4f} + * @param hmdMatrix the input matrix + * @param mat the converted matrix + * @return the converted matrix + */ + public static Matrix4f convertSteamVRMatrix4ToMatrix4f(com.jme3.system.jopenvr.HmdMatrix44_t hmdMatrix, Matrix4f mat){ + mat.set(hmdMatrix.m[0], hmdMatrix.m[1], hmdMatrix.m[2], hmdMatrix.m[3], + hmdMatrix.m[4], hmdMatrix.m[5], hmdMatrix.m[6], hmdMatrix.m[7], + hmdMatrix.m[8], hmdMatrix.m[9], hmdMatrix.m[10], hmdMatrix.m[11], + hmdMatrix.m[12], hmdMatrix.m[13], hmdMatrix.m[14], hmdMatrix.m[15]); + return mat; + } + + + /** + * Create a new OpenVR system + * attached to the given {@link VREnvironment VR environment}. + * @param environment the VR environment to which this API is attached. + */ + public OpenVR(VREnvironment environment){ + this.environment = environment; + } + + @Override + public OpenVRInput getVRinput() { + return VRinput; + } + + @Override + public VR_IVRSystem_FnTable getVRSystem() { + return vrsystemFunctions; + } + + @Override + public VR_IVRCompositor_FnTable getCompositor() { + return compositorFunctions; + } + + public VR_IVRTrackedCamera_FnTable getTrackedCamera(){ + return cameraFunctions; + } + + @Override + public String getName() { + return "OpenVR"; + } + + private static long latencyWaitTime = 0; + + @Override + public void setFlipEyes(boolean set) { + flipEyes = set; + } + + private boolean enableDebugLatency = false; + + @Override + public void printLatencyInfoToConsole(boolean set) { + enableDebugLatency = set; + } + + @Override + public int getDisplayFrequency() { + if( hmdDisplayFrequency == null ) return 0; + return hmdDisplayFrequency.get(0); + } + + @Override + public boolean initialize() { + + logger.config("Initializing OpenVR system..."); + + hmdErrorStore = new IntByReference(); + vrsystemFunctions = null; + + // Init the native linking to the OpenVR library. + try{ + JOpenVRLibrary.init(); + } catch(Throwable t){ + logger.log(Level.SEVERE, "Cannot link to OpenVR system library: "+t.getMessage(), t); + return false; + } + + JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene); + + if( hmdErrorStore.getValue() == 0 ) { + vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer()); + } + + if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) { + logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0)); + logger.severe("Initializing OpenVR system [FAILED]"); + return false; + } else { + logger.config("OpenVR initialized & VR connected."); + + vrsystemFunctions.setAutoSynch(false); + vrsystemFunctions.read(); + + + tlastVsync = new FloatByReference(); + _tframeCount = new LongByReference(); + + hmdDisplayFrequency = IntBuffer.allocate(1); + hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float); + hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference(); + hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount); + poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + for(int i=0;itrue is the use of the headset camera is allowed and false otherwise. + */ + public void initCamera(boolean allowed) { + hmdErrorStore.setValue(0); // clear the error store + + if( allowed && vrsystemFunctions != null ) { + IntByReference intptr = JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRTrackedCamera_Version, hmdErrorStore); + if (intptr != null){ + cameraFunctions = new VR_IVRTrackedCamera_FnTable(intptr.getPointer()); + if(cameraFunctions != null && hmdErrorStore.getValue() == 0 ){ + cameraFunctions.setAutoSynch(false); + cameraFunctions.read(); + logger.config("OpenVR Camera initialized"); + } + } + } + } + + @Override + public void destroy() { + JOpenVRLibrary.VR_ShutdownInternal(); + } + + @Override + public boolean isInitialized() { + return initSuccess; + } + + @Override + public void reset() { + if( vrsystemFunctions == null ) return; + vrsystemFunctions.ResetSeatedZeroPose.apply(); + hmdSeatToStand = null; + } + + @Override + public void getRenderSize(Vector2f store) { + if( vrsystemFunctions == null ) { + // 1344x1512 + store.x = 1344f; + store.y = 1512f; + } else { + IntByReference x = new IntByReference(); + IntByReference y = new IntByReference(); + vrsystemFunctions.GetRecommendedRenderTargetSize.apply(x, y); + store.x = x.getValue(); + store.y = y.getValue(); + } + } + /* + @Override + public float getFOV(int dir) { + float val = 0f; + if( vrsystemFunctions != null ) { + val = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, dir, hmdErrorStore); + } + // verification of number + if( val == 0f ) { + return 55f; + } else if( val <= 10f ) { + // most likely a radian number + return val * 57.2957795f; + } + return val; + } + */ + + @Override + public float getInterpupillaryDistance() { + if( vrsystemFunctions == null ) return 0.065f; + return vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_UserIpdMeters_Float, hmdErrorStore); + } + + @Override + public Quaternion getOrientation() { + VRUtil.convertMatrix4toQuat(hmdPose, rotStore); + return rotStore; + } + + @Override + public Vector3f getPosition() { + // the hmdPose comes in rotated funny, fix that here + hmdPose.toTranslationVector(posStore); + posStore.x = -posStore.x; + posStore.z = -posStore.z; + return posStore; + } + + @Override + public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) { + hmdPose.toTranslationVector(storePos); + storePos.x = -storePos.x; + storePos.z = -storePos.z; + storeRot.set(getOrientation()); + } + + @Override + public void updatePose(){ + if(vrsystemFunctions == null) return; + if(compositorFunctions != null) { + compositorFunctions.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0); + } else { + // wait + if( latencyWaitTime > 0 ) VRUtil.sleepNanos(latencyWaitTime); + + vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount); + float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons; + + if( enableDebugLatency ) { + if( frames == 10 ) { + System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime)); + System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons)); + } + frames = (frames + 1) % 60; + } + + // handle skipping frame stuff + long nowCount = _tframeCount.getValue(); + if( nowCount - frameCount > 1 ) { + // skipped a frame! + if( enableDebugLatency ) System.out.println("Frame skipped!"); + frameCountRun = 0; + if( latencyWaitTime > 0 ) { + latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1); + if( latencyWaitTime < 0 ) latencyWaitTime = 0; + } + } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) { + // didn't skip a frame, lets try waiting longer to improve latency + frameCountRun++; + latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0)); + } + + frameCount = nowCount; + + vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply( + environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated: + JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding, + fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount); + } + + // deal with controllers being plugged in and out + // causing an invalid memory crash... skipping for now + /*boolean hasEvent = false; + while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) { + // wait until the events are clear.. + hasEvent = true; + } + if( hasEvent ) { + // an event probably changed controller state + VRInput._updateConnectedControllers(); + }*/ + //update controllers pose information + environment.getVRinput().updateControllerStates(); + + // read pose data from native + for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){ + hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid"); + if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){ + hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking"); + convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]); + } + } + + if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){ + hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]); + } else { + hmdPose.set(Matrix4f.IDENTITY); + } + } + + @Override + public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){ + if( hmdProjectionLeftEye != null ) { + return hmdProjectionLeftEye; + } else if(vrsystemFunctions == null){ + return cam.getProjectionMatrix(); + } else { + HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar()); + hmdProjectionLeftEye = new Matrix4f(); + convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye); + return hmdProjectionLeftEye; + } + } + + @Override + public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){ + if( hmdProjectionRightEye != null ) { + return hmdProjectionRightEye; + } else if(vrsystemFunctions == null){ + return cam.getProjectionMatrix(); + } else { + HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar()); + hmdProjectionRightEye = new Matrix4f(); + convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye); + return hmdProjectionRightEye; + } + } + + @Override + public Vector3f getHMDVectorPoseLeftEye() { + if( hmdPoseLeftEyeVec == null ) { + hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector(); + // set default IPD if none or broken + if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) { + hmdPoseLeftEyeVec.x = 0.065f * -0.5f; + } + if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping + } + return hmdPoseLeftEyeVec; + } + + @Override + public Vector3f getHMDVectorPoseRightEye() { + if( hmdPoseRightEyeVec == null ) { + hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector(); + // set default IPD if none or broken + if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) { + hmdPoseRightEyeVec.x = 0.065f * 0.5f; + } + if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping + } + return hmdPoseRightEyeVec; + } + + @Override + public Vector3f getSeatedToAbsolutePosition() { + if( environment.isSeatedExperience() == false ) return Vector3f.ZERO; + if( hmdSeatToStand == null ) { + hmdSeatToStand = new Vector3f(); + HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply(); + Matrix4f tempmat = new Matrix4f(); + convertSteamVRMatrix3ToMatrix4f(mat, tempmat); + tempmat.toTranslationVector(hmdSeatToStand); + } + return hmdSeatToStand; + } + + @Override + public Matrix4f getHMDMatrixPoseLeftEye(){ + if( hmdPoseLeftEye != null ) { + return hmdPoseLeftEye; + } else if(vrsystemFunctions == null) { + return Matrix4f.IDENTITY; + } else { + HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left); + hmdPoseLeftEye = new Matrix4f(); + return convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye); + } + } + + @Override + public HmdType getType() { + if( vrsystemFunctions != null ) { + Pointer str1 = new Memory(128); + Pointer str2 = new Memory(128); + String completeName = ""; + vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, + JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String, + str1, 128, hmdErrorStore); + if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0); + vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, + JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String, + str2, 128, hmdErrorStore); + if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0); + if( completeName.length() > 0 ) { + completeName = completeName.toLowerCase(Locale.ENGLISH).trim(); + if( completeName.contains("htc") || completeName.contains("vive") ) { + return HmdType.HTC_VIVE; + } else if ( completeName.contains("index") ) { + return HmdType.VALVE_INDEX; + } else if( completeName.contains("osvr") ) { + return HmdType.OSVR; + } else if( completeName.contains("oculus") || completeName.contains("rift") || + completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) { + return HmdType.OCULUS_RIFT; + } else if( completeName.contains("fove") ) { + return HmdType.FOVE; + } else if( completeName.contains("game") && completeName.contains("face") ) { + return HmdType.GAMEFACE; + } else if( completeName.contains("morpheus") ) { + return HmdType.MORPHEUS; + } else if( completeName.contains("gear") ) { + return HmdType.GEARVR; + } else if( completeName.contains("star") ) { + return HmdType.STARVR; + } else if( completeName.contains("null") ) { + return HmdType.NULL; + } + } + } else return HmdType.NONE; + return HmdType.OTHER; + } + + @Override + public Matrix4f getHMDMatrixPoseRightEye(){ + if( hmdPoseRightEye != null ) { + return hmdPoseRightEye; + } else if(vrsystemFunctions == null) { + return Matrix4f.IDENTITY; + } else { + HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right); + hmdPoseRightEye = new Matrix4f(); + return convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye); + } + } + +} diff --git a/lib/android.jar b/lib/android.jar index b94c815185..66e18e0772 100644 Binary files a/lib/android.jar and b/lib/android.jar differ diff --git a/natives-snapshot.properties b/natives-snapshot.properties new file mode 100644 index 0000000000..50b135bd14 --- /dev/null +++ b/natives-snapshot.properties @@ -0,0 +1 @@ +natives.snapshot=128e079a22413f63b430ffe4ad2ec7ebe7f50f3c diff --git a/private/key.enc b/private/key.enc deleted file mode 100644 index ecabf16784..0000000000 --- a/private/key.enc +++ /dev/null @@ -1,4 +0,0 @@ -&"+q.g2K:ƇT >18]v^LЫ/R"o2#5;Q`;@񻦱&{yJGaG;lTdltnHxp~WrJ5争 Ch_# -۞){2xXdފu1޳ -" -P DC'e1Dk]ix&"n935d6==Xb8JWB/wmJ&Y죊u=* \ No newline at end of file diff --git a/private/upload_native.sh b/private/upload_native.sh deleted file mode 100755 index 94d5afdc60..0000000000 --- a/private/upload_native.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -euo pipefail -echo " - Checking if natives changed in commit $TRAVIS_COMMIT.." -NATIVE_CHANGES="$(git diff-tree --name-only "$TRAVIS_COMMIT" -- jme3-bullet-native/)" -if [ "$NATIVE_CHANGES" != "" ]; then - echo " - Configuring GIT user" - git config --global user.email "travis-ci" - git config --global user.name "travis-ci" - echo " - Decrypting private key" - openssl aes-256-cbc -K $encrypted_f0a0b284e2e8_key -iv $encrypted_f0a0b284e2e8_iv -in private/key.enc -out "$HOME/.ssh/id_rsa" -d - chmod 600 "$HOME/.ssh/id_rsa" - # ls jme3-bullet-native/build/libs/bulletjme/shared/ - # md5 -r jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib jme3-bullet-native/build/libs/bulletjme/shared/mac32/libbulletjme.dylib - echo " - Pushing natives onto branch $TRAVIS_BRANCH" - git checkout "$TRAVIS_BRANCH" - git add -v -- jme3-bullet-native/libs/native/ - git commit -v -m "[ci skip] bullet: update $TRAVIS_OS_NAME natives" - git pull -q --rebase - git push git@github.com:jMonkeyEngine/jmonkeyengine.git -else - echo No native changes. -fi diff --git a/version.gradle b/version.gradle index 22a34c38bc..80972003af 100644 --- a/version.gradle +++ b/version.gradle @@ -1,35 +1,3 @@ -/* - Version Info Examples - ===================== - - Nightly Build Snapshot - * git tag: - * Full Version: 3.1-5124 - * POM Version: 3.1.0-SNAPSHOT - * NBM Revision: 5124 - * NBM UC Suffix: nightly/3.1/plugins - - Nightly Build Snapshot (PBRIsComing branch) - * git tag: - * Full Version: 3.1-PBRIsComing-5124 - * POM Version: 3.1.0-PBRIsComing-SNAPSHOT - * NBM Revision: 5124 - * NBM UC Suffix: PBRIsComing-nightly/3.1/plugins - - Alpha1 Release - * git tag: v3.1.0-alpha1 - * Full Version: 3.1-alpha1 - * POM Version: 3.1.0-alpha1 - * NBM Revision: 0 - * NBM UC Suffix: stable/3.1/plugins - - Final Release - * git tag: v3.1.0 - * Full Version: 3.1 - * POM Version: 3.1.0 - * NBM Revision: 0 - * NBM UC Suffix: stable/3.1/plugins - */ import java.text.SimpleDateFormat import org.ajoberstar.grgit.* @@ -50,65 +18,8 @@ ext { jmeShortGitHash = "" jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) jmeBranchName = "unknown" - jmeFullVersion = "${jmeVersion}-UNKNOWN" - jmePomVersion = "unknown" -} - -def getReleaseInfo(String tag) { - if (tag == null) { - // not a tagged commit - return null; - } - if (!tag.startsWith("v")) { - // syntax error - return null; - } - tag = tag.substring(1) - - String[] parts = tag.split("-", 2); - String mainVersion; - boolean prerelease; - String releaseName = null; - - if (parts.length == 2) { - // prerelease - prerelease = true; - mainVersion = parts[0]; - releaseName = parts[1]; - if (releaseName.size() == 0) { - // syntax error - return null; - } - } else if (parts.length == 1) { - // final release - prerelease = false; - mainVersion = parts[0]; - } else { - // error - return null; - } - - if (mainVersion.size() == 0) { - // syntax error - return null; - } - - parts = mainVersion.split("\\."); - if (parts.size() != 3) { - // syntax error - return null; - } - - String baseVersion = parts[0] + "." + parts[1]; - - return [ - "tag" : tag, - "baseVersion" : baseVersion, - "mainVersion" : mainVersion, - "prerelease" : prerelease, - "releaseName" : releaseName, - "releaseSuffix": (prerelease ? "-${releaseName}": "") - ] + jmeFullVersion = "${jmeVersion}-SNAPSHOT" + jmeVersionTag="SNAPSHOT" } task configureVersionInfo { @@ -121,45 +32,38 @@ task configureVersionInfo { jmeBranchName = grgit.branch.current.name jmeGitTag = grgit.tag.list().find { it.commit == head } - if (jmeGitTag != null) { - jmeGitTag = jmeGitTag.name - } else { - if (System.env.APPVEYOR_REPO_TAG == "true") { - jmeGitTag = System.env.APPVEYOR_REPO_TAG_NAME + if(jmeVersionName==""){ + if (jmeGitTag != null) { + jmeGitTag = jmeGitTag.name + jmeFullVersion = jmeGitTag + jmeVersionTag = "" } else { - jmeGitTag = System.env.TRAVIS_TAG + if(useCommitHashAsVersionName=="true"&&jmeGitHash!=null&&!jmeGitHash.equals("")){ + jmeFullVersion = jmeGitHash + jmeVersionTag = "" + }else{ + jmeFullVersion="${jmeVersion}-"; + if(jmeBranchName!="master")jmeFullVersion+="${jmeBranchName}-"; + jmeFullVersion+="SNAPSHOT" + jmeVersionTag="SNAPSHOT" + } } + }else{ + jmeVersionTag="" + jmeFullVersion=jmeVersionName } - def releaseInfo = getReleaseInfo(jmeGitTag) - if (releaseInfo != null) { - jmeFullVersion = "${releaseInfo.baseVersion}${releaseInfo.releaseSuffix}" - jmePomVersion = "${releaseInfo.mainVersion}${releaseInfo.releaseSuffix}" - } else { - // SNAPSHOT - jmeFullVersion = jmeMainVersion - jmePomVersion = jmeVersion - if (System.env.TRAVIS_BRANCH != null) { - jmeBranchName = System.env.TRAVIS_BRANCH - } else if (System.env.APPVEYOR_REPO_BRANCH != null) { - jmeBranchName = System.env.APPVEYOR_REPO_BRANCH - } - if (System.env.TRAVIS_PULL_REQUEST != null && - System.env.TRAVIS_PULL_REQUEST != "false") { - jmeBranchName += "-pr-" + System.env.TRAVIS_PULL_REQUEST - } else if (System.env.APPVEYOR_PULL_REQUEST_NUMBER != null) { - jmeBranchName += "-pr-" + System.env.APPVEYOR_PULL_REQUEST_NUMBER - } - if (jmeBranchName != "master") { - jmeFullVersion += "-${jmeBranchName}" - jmePomVersion += "-${jmeBranchName}" - } - jmeFullVersion += "-${jmeRevision}" - jmePomVersion += "-SNAPSHOT" - } + + println("Revision: ${jmeRevision}") + println("Hash: ${jmeGitHash}") + println("Short Hash: ${jmeShortGitHash}") + println("Tag: ${jmeGitTag}") + println("Build Date: ${jmeBuildDate}") + println("Build Branch: ${jmeBranchName}") + println("Use commit hash as version ${useCommitHashAsVersionName}") + println("Build Tag: ${jmeVersionTag}") + println("Build Version: ${jmeFullVersion}") - logger.warn("Full Version: ${jmeFullVersion}") - logger.warn("POM Version: ${jmePomVersion}") } catch (ex) { // Failed to get repo info logger.warn("Failed to get repository info: " + ex.message + ". " + \