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 7234481dd8..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. @@ -22,6 +24,15 @@ p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/ Developers in the Contributors team can push directly to Main instead of submitting pull requests, however for new features it is often a good idea to do a pull request as a means to get a last code review. +## Customs around integration, branching, tagging, and releases + +- Most pull requests are integrated directly into the master branch of the repository. +- Integrators should note, unless the history of the pull request is important, it should be integrated to a single commit using “squash and merge”. If the history is important, favor “rebase and merge”. Don’t create a merge commit unless GitHub cannot rebase the PR. +- For each major release (such as v3.0 or v3.3), an appropriately named release branch is created in the repository. +- For each minor (or “dot-dot”) release (such as v3.2.3), an appropriately named tag is created in the repository. +- In general, library changes that plausibly might break existing apps appear only in major releases, not minor ones. + + ## Building the engine 1. Install [Gradle](http://www.gradle.org/) diff --git a/README.md b/README.md index d6f6517589..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 3D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.2 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives. +jMonkeyEngine is a 3-D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.4 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives. The engine is used by several commercial game studios and computer-science courses. Here's a taste: @@ -11,17 +11,15 @@ The engine is used by several commercial game studios and computer-science cours - [jME powered games on IndieDB](http://www.indiedb.com/engines/jmonkeyengine/games) - [Maker's Tale](http://steamcommunity.com/sharedfiles/filedetails/?id=93461954t) - - [Boardtastic 2](https://play.google.com/store/apps/details?id=com.boardtastic.skateboarding) - - [Copod](http://herebeben.com/copod) - - [Attack of the Gelatinous Blob](http://attackofthegelatinousblob.com/) - - [Chaos](http://4realms.net/) + - [Boardtastic 2](https://boardtastic-2.fileplanet.com/apk) + - [Attack of the Gelatinous Blob](https://attack-gelatinous-blob.softwareandgames.com/) - [Mythruna](http://mythruna.com/) - - [PirateHell](http://www.desura.com/games/piratehell) - - [3089 (on steam)](http://store.steampowered.com/app/263360/) - - [3079 (on steam)](http://store.steampowered.com/app/259620/) - - [Lightspeed Frontier](http://www.lightspeedfrontier.com/) + - [PirateHell (on Steam)](https://store.steampowered.com/app/321080/Pirate_Hell/) + - [3089 (on Steam)](http://store.steampowered.com/app/263360/) + - [3079 (on Steam)](http://store.steampowered.com/app/259620/) + - [Lightspeed Frontier (on Steam)](https://store.steampowered.com/app/548650/Lightspeed_Frontier/) - [Skullstone](http://www.skullstonegame.com/) - - [Spoxel](https://store.steampowered.com/app/746880/Spoxel/) + - [Spoxel (on Steam)](https://store.steampowered.com/app/746880/Spoxel/) ## Getting started 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/app/AndroidHarness.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java index 5ca059e9a7..b6d0913d16 100644 --- a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java +++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java @@ -88,7 +88,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt protected int eglStencilBits = 0; /** - * Set the desired frame rate. If frameRate > 0, the application + * Set the desired frame rate. If frameRate higher than 0, the application * will be capped at the desired frame rate. * (default = -1, no frame rate cap) */ diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java index 7b2bf180f2..ed8976955f 100644 --- a/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java +++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java @@ -116,7 +116,7 @@ public class AndroidHarnessFragment extends Fragment implements protected int eglStencilBits = 0; /** - * Set the desired frame rate. If frameRate > 0, the application + * Set the desired frame rate. If frameRate higher than 0, the application * will be capped at the desired frame rate. * (default = -1, no frame rate cap) */ @@ -129,7 +129,7 @@ public class AndroidHarnessFragment extends Fragment implements * will have the resolution set to a maximum of maxResolutionDimension. * The other direction will be set to a value that maintains the aspect * ratio of the surfaceview.
- * Any value < 0 (default = -1) will result in the surfaceview having the + * Any value less than 0 (default = -1) will result in the surfaceview having the * same resolution as the view layout (ie. no max resolution). */ protected int maxResolutionDimension = -1; diff --git a/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java b/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java index 1308b08ddc..4faa8aa898 100644 --- a/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java +++ b/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java @@ -63,7 +63,7 @@ * *

This profiler uses the Android Trace class which is only supported * on Android SDK rev 18 and higher (ver 4.3 and higher). If the - * device is running a version < rev 18, the logging will + * device is running a version less than rev 18, the logging will * be skipped.

* *

In the MainActivity class, add the following:

diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java index 1e610d24e5..6eb2a949f9 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java @@ -71,10 +71,11 @@ * This is done to allow for battery conservation when sensor data or gamepads * are not required by the application. * + * {@code * To use the joystick rumble feature, the following line needs to be * added to the Android Manifest File * - * + * } * @author iwgeric */ public class AndroidJoyInput implements JoyInput { 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/constraints/definitions/ConstraintDefinition.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java index a1f3a73165..166b28c614 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java @@ -151,7 +151,7 @@ public Set getAlteredOmas() { * @param targetTransform * the target transform used by some of the constraints * @param influence - * the influence of the constraint (from range <0; 1>) + * the influence of the constraint from range [0; 1] */ public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java index cd146b109a..5b7ccfc9bc 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java @@ -313,7 +313,7 @@ public AffineTransform createAffineTransform(Vector2f[] source, Vector2f[] dest, * This method returns the proper pixel position on the image. * * @param pos - * the relative position (value of range <0, 1> (both inclusive)) + * the relative position (value of range [0, 1] (both inclusive)) * @param size * the size of the line the pixel lies on (width, height or * depth) diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java index 365b34f5e9..def3462ed8 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TexturePixel.java @@ -118,7 +118,7 @@ public void fromIntensity(short intensity) { /** * This method sets the alpha value (converts it to float number from range - * <0, 1>). + * [0, 1]). * * @param alpha * the alpha value @@ -129,7 +129,7 @@ public void setAlpha(byte alpha) { /** * This method sets the alpha value (converts it to float number from range - * <0, 1>). + * [0, 1]). * * @param alpha * the alpha value 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 1c6f2549d6..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,15 @@ public Image blend(Image image, Image baseImage, BlenderContext blenderContext) } dataArray.add(newData); } - - Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, ColorSpace.Linear) : new Image(Format.RGBA8, width, height, dataArray.get(0), ColorSpace.Linear); + + ColorSpace colorSpace; + if (baseImage != null) { + colorSpace = baseImage.getColorSpace() != null ? baseImage.getColorSpace() : ColorSpace.Linear; + } else { + colorSpace = image.getColorSpace(); + } + + Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, colorSpace) : new Image(Format.RGBA8, width, height, dataArray.get(0), colorSpace); if (image.getMipMapSizes() != null) { result.setMipMapSizes(image.getMipMapSizes().clone()); } 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 558940173f..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 "-Ofast" - cppCompiler.args "-U_FORTIFY_SOURCE" - } else if (os == "linux") { - cppCompiler.args "-fvisibility=hidden" - cppCompiler.args '-I', "$javaHome/include/linux" - cppCompiler.args "-fPIC" - cppCompiler.args "-Ofast" - cppCompiler.args "-U_FORTIFY_SOURCE" - cppCompiler.args "-fpermissive" - linker.args "-fvisibility=hidden" - } else if (os == "windows") { - if (toolChain in Gcc) { - if (toolChain.name.startsWith('mingw')) { - cppCompiler.args '-I', "$projectDir/src/native/cpp/fake_win32" - } else { - cppCompiler.args '-I', "$javaHome/include/win32" - } - cppCompiler.args "-fpermissive" - cppCompiler.args "-static" - cppCompiler.args "-Ofast" + if (os == "osx") { + cppCompiler.args '-I', "$javaHome/include/darwin" + cppCompiler.args "-O3" + cppCompiler.args "-U_FORTIFY_SOURCE" + } else if (os == "linux") { + cppCompiler.args "-fvisibility=hidden" + cppCompiler.args '-I', "$javaHome/include/linux" + cppCompiler.args "-fPIC" + cppCompiler.args "-O3" cppCompiler.args "-U_FORTIFY_SOURCE" - linker.args "-static" - linker.args "-Wl,--exclude-all-symbols" + cppCompiler.args "-fpermissive" + linker.args "-fvisibility=hidden" + } else if (os == "windows") { + if (toolChain in Gcc) { + if (toolChain.name.startsWith('mingw')) cppCompiler.args '-I', "$projectDir/src/native/cpp/fake_win32" + else cppCompiler.args '-I', "$javaHome/include/win32" + cppCompiler.args "-fpermissive" + cppCompiler.args "-static" + cppCompiler.args "-O3" + cppCompiler.args "-U_FORTIFY_SOURCE" + linker.args "-static" + linker.args "-Wl,--exclude-all-symbols" + } else if (toolChain in VisualCpp) { + cppCompiler.args "/I$javaHome\\include\\win32" + } + cppCompiler.define('WIN32') } - else if (toolChain in VisualCpp) { - cppCompiler.args "/I$javaHome\\include\\win32" + tasks.all { + dependsOn unzipBulletIfNeeded + dependsOn ':jme3-bullet:compileJava' } - cppCompiler.define('WIN32') - } - tasks.all { - dependsOn unzipBulletIfNeeded - dependsOn ':jme3-bullet:generateNativeHeaders' - } + task "copyBinaryToLibs${targetPlatform.name}"(type: Copy, dependsOn: tasks) { + from sharedLibraryFile + into "${rootPath}/build/native/bullet/native/${os}/${arch}" + } - // Add output to jar file - jar.into("native/${os}/${arch}") { - from sharedLibraryFile - } + // Add depend on copy + jar.dependsOn("copyBinaryToLibs${targetPlatform.name}") - // Add depend on build - jar.dependsOn tasks - // Add output to libs folder - task "copyBinaryToLibs${targetPlatform.name}"(type: Copy, dependsOn: tasks) { - from sharedLibraryFile - into "libs/native/${os}/${arch}" } - // Add depend on copy - jar.dependsOn("copyBinaryToLibs${targetPlatform.name}") } withType(StaticLibraryBinarySpec) { buildable = false @@ -172,10 +197,6 @@ model { architecture "x86_64" operatingSystem "windows" } - Mac32 { - architecture "x86" - operatingSystem "osx" - } Mac64 { architecture "x86_64" operatingSystem "osx" @@ -188,6 +209,18 @@ model { architecture "x86_64" operatingSystem "linux" } + LinuxArm { + architecture "arm" + operatingSystem "linux" + } + LinuxArmHF { + architecture "armhf" + operatingSystem "linux" + } + LinuxArm64 { + architecture "aarch64" + operatingSystem "linux" + } } } @@ -197,6 +230,9 @@ sourceSets { java { srcDir 'src/native/cpp' } + resources { + srcDir file(Paths.get(rootPath, 'build', 'native', 'bullet')) + } } } 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 d0ea174f71..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 8cad657b2a..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 d85332e863..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 62733e513d..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 f826da28f7..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 9468e84681..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_collision_shapes_GImpactCollisionShape.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp index f416e46b4a..71a3d3742b 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp +++ b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp @@ -55,6 +55,18 @@ extern "C" { return reinterpret_cast(shape); } + /* + * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape + * Method: recalcAabb + * Signature: (J)V + */ + JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_GImpactCollisionShape_recalcAabb + (JNIEnv *env, jobject object, jlong shapeId) { + btGImpactMeshShape *pShape + = reinterpret_cast (shapeId); + pShape->updateBound(); + } + /* * Class: com_jme3_bullet_collision_shapes_GImpactCollisionShape * Method: finalizeNative @@ -65,7 +77,7 @@ extern "C" { btTriangleIndexVertexArray* array = reinterpret_cast (meshId); delete(array); } - + #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-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp index 1d726038fd..3c05e124fb 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp +++ b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_objects_PhysicsRigidBody.cpp @@ -52,9 +52,7 @@ extern "C" { btMotionState* motionState = reinterpret_cast(motionstatId); btCollisionShape* shape = reinterpret_cast(shapeId); btVector3 localInertia = btVector3(); - if(mass > 0){ - shape->calculateLocalInertia(mass, localInertia); - } + shape->calculateLocalInertia(mass, localInertia); btRigidBody* body = new btRigidBody(mass, motionState, shape, localInertia); body->setUserPointer(NULL); return reinterpret_cast(body); 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/collision/shapes/GImpactCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java index 0f9cfc4256..2e5dc9704e 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java @@ -36,6 +36,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; @@ -153,6 +154,23 @@ public void read(JmeImporter im) throws IOException { vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0])); createShape(); } + + /** + * Alter the scaling factors of this shape. + *

+ * Note that if the shape is shared (between collision objects and/or + * compound shapes) changes can have unintended consequences. + * + * @param scale the desired scaling factor for each local axis (not null, no + * negative component, unaffected, default=(1,1,1)) + */ + @Override + public void setScale(Vector3f scale) { + super.setScale(scale); + recalcAabb(objectId); + } + + native private void recalcAabb(long shapeId); /** * Instantiate the configured shape in Bullet. diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java index f4fcaef6d6..0a4140bdbb 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java @@ -147,7 +147,7 @@ protected void createCollisionHeightfield(float[] heightmap, Vector3f worldScale this.maxHeight = max; this.upAxis = 1; - this.flipQuadEdges = false; + flipQuadEdges = true; heightStickWidth = (int) FastMath.sqrt(heightfieldData.length); heightStickLength = heightStickWidth; 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/audio/AudioNode.java b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java index becc7fa19e..fead019aea 100644 --- a/jme3-core/src/main/java/com/jme3/audio/AudioNode.java +++ b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java @@ -158,6 +158,7 @@ public AudioNode(AssetManager assetManager, String name, DataType type) { * * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead */ + @Deprecated public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) { this.audioKey = new AudioKey(name, stream, streamCache); this.data = (AudioData) assetManager.loadAsset(audioKey); @@ -173,6 +174,7 @@ public AudioNode(AssetManager assetManager, String name, boolean stream, boolean * * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead */ + @Deprecated public AudioNode(AssetManager assetManager, String name, boolean stream) { this(assetManager, name, stream, true); // Always streamCached } @@ -186,6 +188,7 @@ public AudioNode(AssetManager assetManager, String name, boolean stream) { * * @deprecated AudioRenderer parameter is ignored. */ + @Deprecated public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) { this(assetManager, name, DataType.Buffer); } @@ -197,6 +200,7 @@ public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String * @param name The filename of the audio file * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType) } instead */ + @Deprecated public AudioNode(AssetManager assetManager, String name) { this(assetManager, name, DataType.Buffer); } @@ -722,7 +726,7 @@ public void updateLogicalState(float tpf) { @Override public void updateGeometricState() { super.updateGeometricState(); - if (channel < 0 || this.getParent() == null) return; + if (channel < 0) return; Vector3f currentWorldTranslation = worldTransform.getTranslation(); if (!previousWorldTranslation.equals(currentWorldTranslation)) { getRenderer().updateSourceParam(this, AudioParam.Position); diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java index c087950a38..6e422dea42 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java @@ -79,9 +79,9 @@ public BoundingBox() { * Instantiate a BoundingBox with given center and extents. * * @param c the coordinates of the center of the box (not null, not altered) - * @param x the X-extent of the box (>=0, may be +Infinity) - * @param y the Y-extent of the box (>=0, may be +Infinity) - * @param z the Z-extent of the box (>=0, may be +Infinity) + * @param x the X-extent of the box (0 or greater, may be +Infinity) + * @param y the Y-extent of the box (0 or greater, may be +Infinity) + * @param z the Z-extent of the box (0 or greater, may be +Infinity) */ public BoundingBox(Vector3f c, float x, float y, float z) { this.center.set(c); diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java index 3137f2cbf7..7936c72244 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java @@ -173,11 +173,11 @@ public void computeFromTris(Triangle[] tris, int start, int end) { /** * Calculates a minimum bounding sphere for the set of points. The algorithm - * was originally found in C++ at - *

- * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1
broken link

- *

and translated to java by Cep21

- * + * was originally found in C++ at
+ * + * http://flipcode.com/archives/Smallest_Enclosing_Spheres.shtml
+ * and translated to java by Cep21 + * * @param points * The points to calculate the minimum bounds from. */ 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/cinematic/events/CameraEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java index 91b9ee5e18..8d2b708942 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java @@ -41,7 +41,7 @@ /** * - * @author Rickard + * @author Rickard (neph1 @ github) */ public class CameraEvent extends AbstractCinematicEvent{ diff --git a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java index 020803c876..19268b8175 100644 --- a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java +++ b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java @@ -783,7 +783,7 @@ public float getParticlesPerSec() { */ public void setParticlesPerSec(float particlesPerSec) { this.particlesPerSec = particlesPerSec; - timeDifference = 0; + timeDifference = Math.min(timeDifference,1f / particlesPerSec); //prevent large accumulated timeDifference from causing a huge number of particles to be emitted } /** 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/input/AbstractJoystick.java b/jme3-core/src/main/java/com/jme3/input/AbstractJoystick.java index 5ced607ffe..ee0d4b9e86 100644 --- a/jme3-core/src/main/java/com/jme3/input/AbstractJoystick.java +++ b/jme3-core/src/main/java/com/jme3/input/AbstractJoystick.java @@ -98,6 +98,7 @@ public void rumble(float amount){ * @deprecated Use JoystickButton.assignButton() instead. */ @Override + @Deprecated public void assignButton(String mappingName, int buttonId){ if (buttonId < 0 || buttonId >= getButtonCount()) throw new IllegalArgumentException(); @@ -116,6 +117,7 @@ public void assignButton(String mappingName, int buttonId){ * @deprecated Use JoystickAxis.assignAxis() instead. */ @Override + @Deprecated public void assignAxis(String positiveMapping, String negativeMapping, int axisId){ // For backwards compatibility diff --git a/jme3-core/src/main/java/com/jme3/input/InputManager.java b/jme3-core/src/main/java/com/jme3/input/InputManager.java index eb15f1c8b6..a8f56c3b9a 100644 --- a/jme3-core/src/main/java/com/jme3/input/InputManager.java +++ b/jme3-core/src/main/java/com/jme3/input/InputManager.java @@ -776,6 +776,7 @@ public void setSimulateMouse(boolean value) { * Returns state of simulation of mouse events. Used for touchscreen input only. * */ + @Deprecated public boolean getSimulateMouse() { if (touch != null) { return touch.isSimulateMouse(); diff --git a/jme3-core/src/main/java/com/jme3/input/Joystick.java b/jme3-core/src/main/java/com/jme3/input/Joystick.java index 8c55c906ed..e172224c2f 100644 --- a/jme3-core/src/main/java/com/jme3/input/Joystick.java +++ b/jme3-core/src/main/java/com/jme3/input/Joystick.java @@ -57,6 +57,7 @@ public interface Joystick { * @see Joystick#getButtonCount() * @deprecated Use JoystickButton.assignButton() instead. */ + @Deprecated public void assignButton(String mappingName, int buttonId); /** @@ -69,6 +70,7 @@ public interface Joystick { * @see Joystick#getAxisCount() * @deprecated Use JoystickAxis.assignAxis() instead. */ + @Deprecated public void assignAxis(String positiveMapping, String negativeMapping, int axisId); /** diff --git a/jme3-core/src/main/java/com/jme3/light/LightProbe.java b/jme3-core/src/main/java/com/jme3/light/LightProbe.java index c2e011c6c1..a53c4ddbd7 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightProbe.java +++ b/jme3-core/src/main/java/com/jme3/light/LightProbe.java @@ -120,7 +120,7 @@ public void setPrefilteredMap(TextureCubeMap prefileteredEnvMap) { // 1 | ay by cy | py | probe position // 2 | az bz cz | pz | ) // --|----------| - // 3 | sx sy sz sp | -> 1/probe radius + nbMipMaps + // 3 | sx sy sz sp | 1/probe radius + nbMipMaps // --scale-- *

* (ax, ay, az) is the pitch rotation axis diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index ea0ac70b91..15bd3c17df 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -537,9 +537,11 @@ public void setTextureParam(String name, VarType type, Texture value) { MatParamTexture val = getTextureParam(name); if (val == null) { checkTextureParamColorSpace(name, value); - paramValues.put(name, new MatParamTexture(type, name, value, null)); + paramValues.put(name, new MatParamTexture(type, name, value, value.getImage() != null ? value.getImage().getColorSpace() : null)); } else { + checkTextureParamColorSpace(name, value); val.setTextureValue(value); + val.setColorSpace(value.getImage() != null ? value.getImage().getColorSpace() : null); } if (technique != null) { diff --git a/jme3-core/src/main/java/com/jme3/material/RenderState.java b/jme3-core/src/main/java/com/jme3/material/RenderState.java index fea4d26e21..5413c3880c 100644 --- a/jme3-core/src/main/java/com/jme3/material/RenderState.java +++ b/jme3-core/src/main/java/com/jme3/material/RenderState.java @@ -272,27 +272,27 @@ public enum BlendMode { /** * Additive blending. For use with glows and particle emitters. *

- * Result = Source Color + Destination Color -> (GL_ONE, GL_ONE) + * Result = Source Color + Destination Color -> (GL_ONE, GL_ONE) */ Additive, /** * Premultiplied alpha blending, for use with premult alpha textures. *

- * Result = Source Color + (Dest Color * (1 - Source Alpha) ) -> (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) + * Result = Source Color + (Dest Color * (1 - Source Alpha) ) -> (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) */ PremultAlpha, /** * Additive blending that is multiplied with source alpha. * For use with glows and particle emitters. *

- * Result = (Source Alpha * Source Color) + Dest Color -> (GL_SRC_ALPHA, GL_ONE) + * Result = (Source Alpha * Source Color) + Dest Color -> (GL_SRC_ALPHA, GL_ONE) */ AlphaAdditive, /** * Color blending, blends in color from dest color * using source color. *

- * Result = Source Color + (1 - Source Color) * Dest Color -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR) + * Result = Source Color + (1 - Source Color) * Dest Color -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR) */ Color, /** @@ -300,7 +300,7 @@ public enum BlendMode { * using source alpha. *

* Result = Source Alpha * Source Color + - * (1 - Source Alpha) * Dest Color -> (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + * (1 - Source Alpha) * Dest Color -> (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) */ Alpha, /** @@ -309,28 +309,28 @@ public enum BlendMode { * The resulting alpha is the sum between the source alpha and the destination alpha. *

* Result.rgb = Source Alpha * Source Color + - * (1 - Source Alpha) * Dest Color -> (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - * Result.a = 1 * Source Alpha + 1 * Dest Alpha -> (GL_ONE, GL_ONE) + * (1 - Source Alpha) * Dest Color -> (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + * Result.a = 1 * Source Alpha + 1 * Dest Alpha -> (GL_ONE, GL_ONE) * */ AlphaSumA, /** * Multiplies the source and dest colors. *

- * Result = Source Color * Dest Color -> (GL_DST_COLOR, GL_ZERO) + * Result = Source Color * Dest Color -> (GL_DST_COLOR, GL_ZERO) */ Modulate, /** * Multiplies the source and dest colors then doubles the result. *

- * Result = 2 * Source Color * Dest Color -> (GL_DST_COLOR, GL_SRC_COLOR) + * Result = 2 * Source Color * Dest Color -> (GL_DST_COLOR, GL_SRC_COLOR) */ ModulateX2, /** * Opposite effect of Modulate/Multiply. Invert both colors, multiply and * then invert the result. *

- * Result = 1 - (1 - Source Color) * (1 - Dest Color) -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR) + * Result = 1 - (1 - Source Color) * (1 - Dest Color) -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR) */ Screen, /** @@ -338,7 +338,7 @@ public enum BlendMode { * operation. This is directly equivalent to Photoshop's "Exclusion" blend. *

* Result = (Source Color * (1 - Dest Color)) + (Dest Color * (1 - Source Color)) - * -> (GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR) + * -> (GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR) */ Exclusion, /** @@ -1242,6 +1242,7 @@ public boolean isPointSprite() { * material parameter. * @return false */ + @Deprecated public boolean isAlphaTest() { return false; } diff --git a/jme3-core/src/main/java/com/jme3/material/Technique.java b/jme3-core/src/main/java/com/jme3/material/Technique.java index 5b02d77e73..a2fb1a0371 100644 --- a/jme3-core/src/main/java/com/jme3/material/Technique.java +++ b/jme3-core/src/main/java/com/jme3/material/Technique.java @@ -169,7 +169,7 @@ void render(RenderManager renderManager, Shader shader, Geometry geometry, Light /** * Get the {@link DefineList} for dynamic defines. * - * Dynamic defines are used to implement material parameter -> define + * Dynamic defines are used to implement material parameter -- define * bindings as well as {@link TechniqueDefLogic} specific functionality. * * @return all dynamic defines. diff --git a/jme3-core/src/main/java/com/jme3/math/FastMath.java b/jme3-core/src/main/java/com/jme3/math/FastMath.java index 1bbf37fca0..49c993a091 100644 --- a/jme3-core/src/main/java/com/jme3/math/FastMath.java +++ b/jme3-core/src/main/java/com/jme3/math/FastMath.java @@ -91,7 +91,7 @@ public static boolean isPowerOfTwo(int number) { * Get the next power of two of the given number. * * E.g. for an input 100, this returns 128. - * Returns 1 for all numbers <= 1. + * Returns 1 for all numbers less than or equal to 1. * * @param number The number to obtain the POT for. * @return The next power of two. @@ -942,7 +942,8 @@ public static boolean approximateEquals(float a, float b) { * Converts a single precision (32 bit) floating point value * into half precision (16 bit). * - *

Source: + * ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf * * @param half The half floating point value as a short. * @return floating point value of the half. @@ -992,4 +993,16 @@ public static short convertFloatToHalf(float flt) { | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) | ((f >> 13) & 0x03ff)); } + + /** + * Converts a range of min/max to a 0-1 range. + * @param value the value between min-max (inclusive). + * @param min the minimum of the range. + * @param max the maximum of the range. + * @return A value between 0-1 if the given value is between min/max. + */ + public static float unInterpolateLinear(float value, float min, float max) { + return (value - min) / (max - min); + } + } diff --git a/jme3-core/src/main/java/com/jme3/math/Ray.java b/jme3-core/src/main/java/com/jme3/math/Ray.java index 8b2ab216b7..81e54d99ff 100644 --- a/jme3-core/src/main/java/com/jme3/math/Ray.java +++ b/jme3-core/src/main/java/com/jme3/math/Ray.java @@ -43,8 +43,9 @@ /** * Ray defines a line segment which has an origin and a direction. * That is, a point and an infinite ray is cast from this point. The ray is - * defined by the following equation: R(t) = origin + t*direction for t >= 0. - * + * defined by the following equation: {@literal + * R(t) = origin + t*direction for t >= 0. + * } * @author Mark Powell * @author Joshua Slack */ 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 b930863a6f..7bda5f4ae8 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Caps.java @@ -238,7 +238,7 @@ public enum Caps { TextureBuffer, /** - * Supports floating point & half textures (Format.RGB16F) + * Supports floating point and half textures (Format.RGB16F) */ FloatTexture, @@ -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/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index 095cc76de9..ee11088789 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -443,5 +443,18 @@ public interface Renderer { * @return true if the results of the task with the given task id are available. */ public boolean isTaskResultAvailable(int taskId); + + + /** + * Gets the alpha to coverage state. + * + */ + public boolean getAlphaToCoverage(); + + /** + * Get the default anisotropic filter level for textures. + * + */ + public int getDefaultAnisotropicFilter(); } 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 b2a57736f9..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(); @@ -991,7 +992,7 @@ public void glCompressedTexSubImage2D(int target, int level, int xoffset, int yo *

* Defines the scissor rectangle for all viewports. The scissor test is enabled or disabled for all viewports using {@link #glEnable Enable} or {@link #glDisable Disable} * with the symbolic constant {@link #GL_SCISSOR_TEST SCISSOR_TEST}. When disabled, it is as if the scissor test always passes. When enabled, if - * left <= xw < left + width and bottom <= yw < bottom + height for the scissor rectangle, then the scissor + * left <= xw < left + width and bottom <= yw < bottom + height for the scissor rectangle, then the scissor * test passes. Otherwise, the test fails and the fragment is discarded. * * @param x the left scissor rectangle coordinate. 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 6f222eb02d..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,4 +3217,18 @@ 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)) { + return gl.glIsEnabled(GLExt.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + + } + return false; + } + + @Override + public int getDefaultAnisotropicFilter() { + return this.defaultAnisotropicFilter; + } } 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/Geometry.java b/jme3-core/src/main/java/com/jme3/scene/Geometry.java index 005d0e8c75..adc1d51a55 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Geometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/Geometry.java @@ -601,6 +601,22 @@ public void setMorphState(float[] state) { this.dirtyMorph = true; } + /** + * Set the state of the morph with the given name. + * + * If the name of the morph is not found, no state will be set. + * + * @param morphTarget The name of the morph to set the state of + * @param state The state to set the morph to + */ + public void setMorphState(String morphTarget, float state) { + int index = mesh.getMorphIndex(morphTarget); + if (index >= 0) { + morphState[index] = state; + this.dirtyMorph = true; + } + } + /** * returns true if the morph state has changed on the last frame. * @return true if changed, otherwise false @@ -629,6 +645,20 @@ public float[] getMorphState() { } return morphState; } + + /** + * Get the state of a morph + * @param morphTarget the name of the morph to get the state of + * @return the state of the morph, or -1 if the morph is not found + */ + public float getMorphState(String morphTarget) { + int index = mesh.getMorphIndex(morphTarget); + if (index < 0) { + return -1; + } else { + return morphState[index]; + } + } /** * Return the number of morph targets that can be handled on the GPU simultaneously for this geometry. diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index caffca063d..1e3195e003 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -1529,10 +1529,46 @@ public void addMorphTarget(MorphTarget target) { public MorphTarget[] getMorphTargets() { return morphTargets.getArray(); } + + /** + * Get the name of all morphs in order. + * Morphs without names will be null + * @return an array + */ + public String[] getMorphTargetNames() { + + MorphTarget[] nbMorphTargets = getMorphTargets(); + if (nbMorphTargets.length == 0) { + return new String[0]; + } + String[] targets = new String[nbMorphTargets.length]; + + for (int index = 0; index < nbMorphTargets.length; index++) { + targets[index] = nbMorphTargets[index].getName(); + } + return targets; + } public boolean hasMorphTargets() { return morphTargets != null && !morphTargets.isEmpty(); } + + /** + * Get the index of the morph that has the given name. + * @param morphName The name of the morph to search for + * @return The index of the morph, or -1 if not found. + */ + public int getMorphIndex(String morphName) { + int index = -1; + MorphTarget[] nbMorphTargets = getMorphTargets(); + for (int i = 0; i < nbMorphTargets.length; i++) { + if (nbMorphTargets[i].getName().equals(morphName)) { + index = i; + break; + } + } + return index; + } @Override public void write(JmeExporter ex) throws IOException { 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/mesh/IndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java index 3fc4b318e7..dd94761f77 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java @@ -31,12 +31,14 @@ */ package com.jme3.scene.mesh; -import com.jme3.util.BufferUtils; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; +import com.jme3.scene.VertexBuffer.Format; +import com.jme3.util.BufferUtils; + /** * IndexBuffer is an abstraction for integer index buffers, * it is used to retrieve indices without knowing in which format they @@ -59,21 +61,22 @@ public static IndexBuffer wrapIndexBuffer(Buffer buf) { } /** - * Creates an index buffer that can contain the given amount - * of vertices. - * Returns {@link IndexShortBuffer} + * Creates an index buffer that can contain the given amount of vertices. + *
+ * Returns either {@link IndexByteBuffer}, {@link IndexShortBuffer} or + * {@link IndexIntBuffer} * * @param vertexCount The amount of vertices to contain - * @param indexCount The amount of indices - * to contain. - * @return A new index buffer + * @param indexCount The amount of indices to contain + * @return A new, apropriately sized index buffer */ public static IndexBuffer createIndexBuffer(int vertexCount, int indexCount){ - if (vertexCount > 65535){ - return new IndexIntBuffer(BufferUtils.createIntBuffer(indexCount)); - }else{ + if (vertexCount < 128) + return new IndexByteBuffer(BufferUtils.createByteBuffer (indexCount)); + else if (vertexCount < 65536) return new IndexShortBuffer(BufferUtils.createShortBuffer(indexCount)); - } + else + return new IndexIntBuffer(BufferUtils.createIntBuffer(indexCount)); } /** @@ -107,12 +110,31 @@ public int remaining() { public abstract int get(int i); /** - * Puts the vertex index at the index buffer's index. + * Absolute put method. + * + *

Puts the vertex index at the index buffer's index. * Implementations may throw an {@link UnsupportedOperationException} * if modifying the IndexBuffer is not supported (e.g. virtual index - * buffers). + * buffers).

+ * + * @param i The buffer index + * @param value The vertex index + * @return This buffer */ - public abstract void put(int i, int value); + public abstract IndexBuffer put(int i, int value); + + /** + * Relative put method. + * + *

Puts the vertex index at the current position, then increments the + * position. Implementations may throw an + * {@link UnsupportedOperationException} if modifying the IndexBuffer is not + * supported (e.g. virtual index buffers).

+ * + * @param value The vertex index + * @return This buffer + */ + public abstract IndexBuffer put(int value); /** * Returns the size of the index buffer. @@ -129,4 +151,17 @@ public int remaining() { * @return the underlying {@link Buffer}. */ public abstract Buffer getBuffer(); + + /** + * Returns the format of the data stored in this buffer. + * + *

This method can be used to set an {@link IndexBuffer} to a + * {@link com.jme3.scene.Mesh Mesh}:

+ *
+     * mesh.setBuffer(Type.Index, 3, 
+     *     indexBuffer.getFormat(), indexBuffer);
+     * 
+ * @return + */ + public abstract Format getFormat(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java index ab7461c2a9..d27901673f 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java @@ -34,6 +34,8 @@ import java.nio.Buffer; import java.nio.ByteBuffer; +import com.jme3.scene.VertexBuffer.Format; + /** * IndexBuffer implementation for {@link ByteBuffer}s. * @@ -59,8 +61,15 @@ public int get(int i) { } @Override - public void put(int i, int value) { + public IndexByteBuffer put(int i, int value) { buf.put(i, (byte) value); + return this; + } + + @Override + public IndexByteBuffer put(int value) { + buf.put((byte) value); + return this; } @Override @@ -72,5 +81,10 @@ public int size() { public Buffer getBuffer() { return buf; } + + @Override + public Format getFormat () { + return Format.UnsignedByte; + } } diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java index ca109ba660..d5f2b86a91 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java @@ -34,6 +34,8 @@ import java.nio.Buffer; import java.nio.IntBuffer; +import com.jme3.scene.VertexBuffer.Format; + /** * IndexBuffer implementation for {@link IntBuffer}s. * @@ -58,8 +60,15 @@ public int get(int i) { } @Override - public void put(int i, int value) { + public IndexIntBuffer put(int i, int value) { buf.put(i, value); + return this; + } + + @Override + public IndexIntBuffer put(int value) { + buf.put(value); + return this; } @Override @@ -71,4 +80,9 @@ public int size() { public Buffer getBuffer() { return buf; } + + @Override + public Format getFormat () { + return Format.UnsignedInt; + } } diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java index e87b5533fb..3dfd560dea 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java @@ -34,6 +34,8 @@ import java.nio.Buffer; import java.nio.ShortBuffer; +import com.jme3.scene.VertexBuffer.Format; + /** * IndexBuffer implementation for {@link ShortBuffer}s. * @@ -58,8 +60,15 @@ public int get(int i) { } @Override - public void put(int i, int value) { + public IndexShortBuffer put(int i, int value) { buf.put(i, (short) value); + return this; + } + + @Override + public IndexShortBuffer put(int value) { + buf.put((short) value); + return this; } @Override @@ -71,4 +80,9 @@ public int size() { public Buffer getBuffer() { return buf; } + + @Override + public Format getFormat () { + return Format.UnsignedShort; + } } diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java b/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java index 083d04fb7f..fc80e21139 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java @@ -11,6 +11,23 @@ public class MorphTarget implements Savable { private EnumMap buffers = new EnumMap<>(VertexBuffer.Type.class); + private String name = null; + + public MorphTarget() { + + } + + public MorphTarget(String name) { + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } public void setBuffer(VertexBuffer.Type type, FloatBuffer buffer) { buffers.put(type, buffer); @@ -35,6 +52,7 @@ public void write(JmeExporter ex) throws IOException { Buffer roData = entry.getValue().asReadOnlyBuffer(); oc.write((FloatBuffer) roData, entry.getKey().name(),null); } + oc.write(name, "morphName", null); } @Override @@ -46,6 +64,6 @@ public void read(JmeImporter im) throws IOException { setBuffer(type, b); } } - + name = ic.readString("morphName", null); } } diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java index 64aa438eb6..dd57abee43 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java @@ -32,6 +32,8 @@ package com.jme3.scene.mesh; import com.jme3.scene.Mesh.Mode; +import com.jme3.scene.VertexBuffer.Format; + import java.nio.Buffer; /** @@ -138,7 +140,7 @@ public int get(int i) { } @Override - public void put(int i, int value) { + public IndexBuffer put(int i, int value) { throw new UnsupportedOperationException("Does not represent index buffer"); } @@ -152,4 +154,15 @@ public Buffer getBuffer() { return null; } + @Override + public IndexBuffer put (int value) { + throw new UnsupportedOperationException("Does not represent index buffer"); + } + + @Override + public Format getFormat () { + // return largest size + return Format.UnsignedInt; + } + } diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java index a960d2e360..5a91050f2d 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java @@ -107,11 +107,7 @@ public static void convertToList(Mesh mesh){ default: break; } - if (outBuf instanceof IndexIntBuffer){ - mesh.setBuffer(Type.Index, 3, (IntBuffer)outBuf.getBuffer()); - }else{ - mesh.setBuffer(Type.Index, 3, (ShortBuffer)outBuf.getBuffer()); - } + mesh.setBuffer(Type.Index, 3, outBuf.getFormat(), outBuf.getBuffer()); } } 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/scene/shape/PQTorus.java b/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java index 041051dcfb..fad5f731cc 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java @@ -210,6 +210,8 @@ public void updateGeometry(float p, float q, float radius, float width, int step setBuffer(Type.Normal, 3, fnb); setBuffer(Type.TexCoord, 2, ftb); setBuffer(Type.Index, 3, sib); + + updateBound(); } @Override 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/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java index cab197ce8c..bd8db3107c 100644 --- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java +++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java @@ -70,6 +70,19 @@ public final class AppSettings extends HashMap { */ public static final String LWJGL_OPENGL2 = "LWJGL-OpenGL2"; + /** + * Use LWJGL as the display system and force using the core OpenGL3.0 renderer. + *

+ * If the underlying system does not support OpenGL3.0, then the context + * initialization will throw an exception. Note that currently jMonkeyEngine + * does not have any shaders that support OpenGL3.0 therefore this + * option is not useful. + *

+ * + * @see AppSettings#setRenderer(java.lang.String) + */ + public static final String LWJGL_OPENGL30 = "LWJGL-OpenGL30"; + /** * Use LWJGL as the display system and force using the core OpenGL3.2 renderer. *

@@ -80,10 +93,26 @@ public final class AppSettings extends HashMap { *

* Note: OpenGL 3.2 is used to give 3.x support to Mac users. * + * @deprecated Previously meant 3.2, use LWJGL_OPENGL32 or LWJGL_OPENGL30 * @see AppSettings#setRenderer(java.lang.String) */ + @Deprecated public static final String LWJGL_OPENGL3 = "LWJGL-OpenGL3"; + /** + * Use LWJGL as the display system and force using the core OpenGL3.2 renderer. + *

+ * If the underlying system does not support OpenGL3.2, then the context + * initialization will throw an exception. Note that currently jMonkeyEngine + * does not have any shaders that support OpenGL3.2 therefore this + * option is not useful. + *

+ * Note: OpenGL 3.2 is used to give 3.x support to Mac users. + * + * @see AppSettings#setRenderer(java.lang.String) + */ + public static final String LWJGL_OPENGL32 = LWJGL_OPENGL3; + /** * Use LWJGL as the display system and force using the OpenGL3.3 renderer. *

@@ -100,10 +129,22 @@ public final class AppSettings extends HashMap { * If the underlying system does not support OpenGL4.0, then the context * initialization will throw an exception. * + * @deprecated Use LWJGL_OPENGL40 * @see AppSettings#setRenderer(java.lang.String) */ + @Deprecated public static final String LWJGL_OPENGL4 = "LWJGL-OpenGL4"; + /** + * Use LWJGL as the display system and force using the OpenGL4.0 renderer. + *

+ * If the underlying system does not support OpenGL4.0, then the context + * initialization will throw an exception. + * + * @see AppSettings#setRenderer(java.lang.String) + */ + public static final String LWJGL_OPENGL40 = LWJGL_OPENGL4; + /** * Use LWJGL as the display system and force using the OpenGL4.1 renderer. *

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/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index 3c289ecc38..76b3fa1463 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java +++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java @@ -212,4 +212,14 @@ public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image. @Override public void setDefaultAnisotropicFilter(int level) { } + + @Override + public boolean getAlphaToCoverage() { + return false; + } + + @Override + public int getDefaultAnisotropicFilter() { + return 0; + } } 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/system/SystemListener.java b/jme3-core/src/main/java/com/jme3/system/SystemListener.java index e2e604a4b0..132f475234 100644 --- a/jme3-core/src/main/java/com/jme3/system/SystemListener.java +++ b/jme3-core/src/main/java/com/jme3/system/SystemListener.java @@ -32,7 +32,7 @@ package com.jme3.system; /** - * The ContextListener> provides a means for an application + * The {@code SystemListener} provides a means for an application * to receive events relating to a context. */ public interface SystemListener { 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/Texture.java b/jme3-core/src/main/java/com/jme3/texture/Texture.java index 253423d004..65563928f6 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture.java @@ -248,6 +248,7 @@ public enum WrapMode { * * @deprecated Not supported by OpenGL 3 */ + @Deprecated MirrorEdgeClamp; } @@ -278,21 +279,23 @@ public enum ShadowCompareMode { */ Off, - /** + /** + * {@code * Compares the 3rd texture coordinate R to the value * in this depth texture. If R <= texture value then result is 1.0, * otherwise, result is 0.0. If filtering is set to bilinear or trilinear * the implementation may sample the texture multiple times to provide - * smoother results in the range [0, 1]. + * smoother results in the range [0, 1].} */ LessOrEqual, /** + * {@code * Compares the 3rd texture coordinate R to the value * in this depth texture. If R >= texture value then result is 1.0, * otherwise, result is 0.0. If filtering is set to bilinear or trilinear * the implementation may sample the texture multiple times to provide - * smoother results in the range [0, 1]. + * smoother results in the range [0, 1].} */ GreaterOrEqual } diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java b/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java index e2447fac28..f1a0f7d3bd 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java @@ -44,14 +44,14 @@ * Describes a cubemap texture. * The image specified by setImage must contain 6 data units, * each data contains a 2D image representing a cube's face. - * The slices are specified in this order:
- *
- * 0 => Positive X (+x)
- * 1 => Negative X (-x)
- * 2 => Positive Y (+y)
- * 3 => Negative Y (-y)
- * 4 => Positive Z (+z)
- * 5 => Negative Z (-z)
+ * The slices are specified in this order:
+ *
+ * 0 -- Positive X (+x)
+ * 1 -- Negative X (-x)
+ * 2 -- Positive Y (+y)
+ * 3 -- Negative Y (-y)
+ * 4 -- Positive Z (+z)
+ * 5 -- Negative Z (-z)
* * @author Joshua Slack */ 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/java/com/jme3/util/ListSort.java b/jme3-core/src/main/java/com/jme3/util/ListSort.java index c45353a0cb..517f2dd6b2 100644 --- a/jme3-core/src/main/java/com/jme3/util/ListSort.java +++ b/jme3-core/src/main/java/com/jme3/util/ListSort.java @@ -52,12 +52,13 @@ * for optimal performance, but can be called several times if the length of the * list changes * + * {@code * Disclaimer : I was intrigued by the use of val >>> 1 in java 7 Timsort class * instead of val / 2 (integer division). Micro benching revealed that val >>> 1 * is twice faster than val / 2 in java 6 and has similar perf in java 7. The * following code uses val >>> 1 when ever a value needs to be divided by 2 and * rounded to its floor - * + * } * * @author Nehon */ @@ -66,7 +67,7 @@ public class ListSort { /** * Threshold for binary sort vs merge. Original algorithm use 64, java7 * TimSort uses 32 and I used 128, see this post for explanations : - * http://hub.jmonkeyengine.org/groups/development-discussion-jme3/forum/topic/i-got-that-sorted-out-huhuhu/ + * https://hub.jmonkeyengine.org/t/i-got-that-sorted-out-huhuhu/24478 */ private static final int MIN_SIZE = 128; private T[] array; diff --git a/jme3-core/src/main/java/com/jme3/util/SortUtil.java b/jme3-core/src/main/java/com/jme3/util/SortUtil.java index 51edc0adb2..7628495f5c 100644 --- a/jme3-core/src/main/java/com/jme3/util/SortUtil.java +++ b/jme3-core/src/main/java/com/jme3/util/SortUtil.java @@ -47,7 +47,7 @@ public class SortUtil { private static final int INSERTION_SORT_THRESHOLD = 7; - /** + /* procedure optimizedGnomeSort(a[]) pos := 1 last := 0 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/export/binary/ByteUtils.java b/jme3-core/src/plugins/java/com/jme3/export/binary/ByteUtils.java index f66a7fc7e1..882cb529fd 100644 --- a/jme3-core/src/plugins/java/com/jme3/export/binary/ByteUtils.java +++ b/jme3-core/src/plugins/java/com/jme3/export/binary/ByteUtils.java @@ -452,7 +452,7 @@ public static boolean convertBooleanFromBytes(byte[] byteArray, int offset) { * of bytes have been read. * * @param store - * the byte array to store in. Should have a length > bytes + * the byte array to store in. Array length must be greater than bytes param. * @param bytes * the number of bytes to read. * @param is diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java index 07569ce90b..2e1ff1aa27 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java @@ -79,13 +79,11 @@ public static void main(String argv[]) { * additionally this methods updates the formattedExpression with uppercased * defines names * - * supported expression syntax example: - * - * "(LightMap && SeparateTexCoord) || !ColorMap" - * "#if (defined(LightMap) && defined(SeparateTexCoord)) || !defined(ColorMap)" - * "#ifdef LightMap" - * "#ifdef (LightMap && SeparateTexCoord) || !ColorMap" - * + * supported expression syntax example:

+ * {@code "(LightMap && SeparateTexCoord) || !ColorMap"}

+ * {@code "#if (defined(LightMap) && defined(SeparateTexCoord)) || !defined(ColorMap)"}

+ * {@code "#ifdef LightMap"}

+ * {@code "#ifdef (LightMap && SeparateTexCoord) || !ColorMap"}
* * @param expression the expression to parse * @return the list of defines diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java index c8c6740463..3e64d0d785 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java @@ -1058,11 +1058,11 @@ public void storeVaryings(ShaderNode node, ShaderNodeVariable variable) { } /** - * merges 2 condition with the given operator + * Merges 2 conditions with the given operator * * @param condition1 the first condition * @param condition2 the second condition - * @param operator the operator ("&&" or "||&) + * @param operator the operator {@literal ("&&" or "||&)} * @return the merged condition */ public String mergeConditions(String condition1, String condition2, String operator) { 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/test/java/com/jme3/material/MaterialTest.java b/jme3-core/src/test/java/com/jme3/material/MaterialTest.java index 05a9ef3781..17aea47755 100644 --- a/jme3-core/src/test/java/com/jme3/material/MaterialTest.java +++ b/jme3-core/src/test/java/com/jme3/material/MaterialTest.java @@ -36,8 +36,15 @@ import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; +import com.jme3.shader.VarType; import com.jme3.system.NullRenderer; import com.jme3.system.TestUtil; +import com.jme3.texture.Image; +import com.jme3.texture.Texture2D; +import com.jme3.texture.Image.Format; +import com.jme3.texture.image.ColorSpace; +import com.jme3.util.BufferUtils; + import java.util.Arrays; import java.util.EnumSet; import static org.junit.Assert.*; @@ -120,6 +127,30 @@ public void testSelectNamedTechnique_GLSL150Cap() { checkRequiredCaps(Caps.GLSL150); } + @Test + public void testForcedColorSpace(){ + + Image img=new Image(Format.RGBA8,2,2,BufferUtils.createByteBuffer(16),null,ColorSpace.sRGB); + Image img2=new Image(Format.RGBA8,2,2,BufferUtils.createByteBuffer(16),null,ColorSpace.sRGB); + Texture2D tx=new Texture2D(img); + Texture2D tx2=new Texture2D(img2); + + assertTrue(tx2.getImage().getColorSpace()==ColorSpace.sRGB); + assertTrue(tx2.getImage().getColorSpace()==ColorSpace.sRGB); + + AssetManager assetManager = TestUtil.createAssetManager(); + MaterialDef def=new MaterialDef(assetManager,"test"); + def.addMaterialParamTexture(VarType.Texture2D, "ColorMap",ColorSpace.Linear, null); + Material mat=new Material(def); + + mat.setTexture("ColorMap",tx); + assertTrue(tx.getImage().getColorSpace()==ColorSpace.Linear); + + mat.setTexture("ColorMap",tx2); + assertTrue(tx2.getImage().getColorSpace()==ColorSpace.Linear); + + } + @Test public void testSelectNamedTechnique_GLSL100Cap() { supportGlsl(100); diff --git a/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.java b/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.java new file mode 100644 index 0000000000..b87d618d8f --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.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.scene.shape; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; +import com.jme3.bounding.BoundingVolume; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.VertexBuffer; +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests that all shapes have had a world bound calculated, and that vertices are within those bounds. Test + * added for issue #1121. This is a clear failure: BoundingBox [Center: (0.0, 0.0, 0.0) xExtent: 0.0 yExtent: + * 0.0 zExtent: 0.0] + * + * @author lou + */ +public class ShapeBoundsTest { + + @Test + public void testBox() { + Box shape = new Box(2f, 3f, 0.8f); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void testCurve() { + Vector3f[] controlp = new Vector3f[4]; + controlp[0] = new Vector3f(0, 0, 0); + controlp[1] = new Vector3f(1, 1, 1); + controlp[2] = new Vector3f(2, 1, 1); + controlp[3] = new Vector3f(3, 2, 1); + Curve shape = new Curve(controlp, 32); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void testCylinder() { + Cylinder shape = new Cylinder(16, 16, 2f, 3f); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void testDome() { + Dome shape = new Dome(16, 16, 5f); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void testLine() { + Line shape = new Line(new Vector3f(0, 0, 0), new Vector3f(1, 2, 3)); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void testPqTorus() { + PQTorus shape = new PQTorus(2f, 3f, 0.8f, 0.2f, 64, 16); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void testQuad() { + Quad shape = new Quad(64, 16); + Geometry geometry = new Geometry("geom", shape); + BoundingVolume bv = geometry.getWorldBound(); + BoundingBox bb = (BoundingBox) bv; + //Quad z extent 0 is normal, so not using testBounds() here. + Assert.assertTrue(bb.getXExtent() > 0 && bb.getYExtent() > 0); + testVertices(geometry); + } + + @Test + public void Sphere() { + Sphere shape = new Sphere(32, 32, 5f); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void StripBox() { + StripBox shape = new StripBox(0.2f, 64f, 16f); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + @Test + public void Torus() { + Torus shape = new Torus(32, 32, 2f, 3f); + Geometry geometry = new Geometry("geom", shape); + testBounds(geometry); + } + + private void testBounds(Geometry geometry) { + BoundingVolume bv = geometry.getWorldBound(); + + if (bv instanceof BoundingBox) { + BoundingBox bb = (BoundingBox) bv; + Assert.assertTrue(bb.getXExtent() > 0 && bb.getYExtent() > 0 && bb.getZExtent() > 0); + } else if (bv instanceof BoundingSphere) { + BoundingSphere bs = (BoundingSphere) bv; + Assert.assertTrue(bs.getRadius() > 1f); + } + + testVertices(geometry); + } + + private void testVertices(Geometry geometry) { + BoundingVolume bv = geometry.getWorldBound(); + Assert.assertNotNull(bv); + + for (int e = 0; e < geometry.getVertexCount(); e++) { + float x = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 0); + float y = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 1); + float z = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 2); + Vector3f vertex = new Vector3f(x, y, z); + Assert.assertTrue("Vertex outside world bound: " + vertex, bv.intersects(vertex)); + } + } +} 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/AWTContext.java b/jme3-desktop/src/main/java/com/jme3/system/AWTContext.java index 78cde8f744..d72ee71c82 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/AWTContext.java +++ b/jme3-desktop/src/main/java/com/jme3/system/AWTContext.java @@ -126,7 +126,7 @@ public void setWidth(final int width) { */ protected AppSettings createSettings() { final AppSettings settings = new AppSettings(true); - settings.setRenderer(AppSettings.LWJGL_OPENGL3); + settings.setRenderer(AppSettings.LWJGL_OPENGL32); return settings; } @@ -145,7 +145,7 @@ public Type getType() { @Override public void setSettings(AppSettings settings) { this.settings.copyFrom(settings); - this.settings.setRenderer(AppSettings.LWJGL_OPENGL3); + this.settings.setRenderer(AppSettings.LWJGL_OPENGL32); this.backgroundContext.setSettings(settings); } 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 707937e78a..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"); @@ -624,8 +626,8 @@ public static void loadNativeLibrary(String name, boolean isRequired) { in = conn.getInputStream(); } catch (IOException ex) { // Maybe put more detail here? Not sure.. - throw new UnsatisfiedLinkError("Failed to open file: '" + url + - "'. Error: " + ex); + throw new UncheckedIOException("Failed to open file: '" + url + + "'. Error: " + ex, ex); } File targetFile = new File(extactionDirectory, loadedAsFileName); @@ -665,8 +667,8 @@ public static void loadNativeLibrary(String name, boolean isRequired) { if (ex.getMessage().contains("used by another process")) { return; } else { - throw new UnsatisfiedLinkError("Failed to extract native " - + "library to: " + targetFile); + throw new UncheckedIOException("Failed to extract native " + + "library to: " + targetFile, ex); } } finally { // XXX: HACK. Vary loading method based on library name.. diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/CrossHatchFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/CrossHatchFilter.java index 4f50e7dd13..9e94513556 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/CrossHatchFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/CrossHatchFilter.java @@ -157,7 +157,7 @@ public void setColorInfluencePaper(float colorInfluencePaper) { } /** - * Sets line/paper color ratio for areas with values < luminance5, + * Sets line/paper color ratio for areas with values less than luminance5, * really dark areas get no lines but a filled blob instead * @param fillValue */ diff --git a/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java b/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java index a628514ceb..677cc6d20a 100644 --- a/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java +++ b/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java @@ -81,7 +81,7 @@ * //attaching the water to the root node * rootNode.attachChild(water); *
- * @author Normen Hansen & Rémy Bouquet + * @author Normen Hansen and Rémy Bouquet */ public class SimpleWaterProcessor implements SceneProcessor { 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/build.gradle b/jme3-examples/build.gradle index 08e2a8dadd..7989235c48 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -22,9 +22,12 @@ dependencies { compile project(':jme3-core') compile project(':jme3-desktop') compile project(':jme3-effects') -// compile project(':jme3-bullet') -// compile project(':jme3-bullet-native') - compile project(':jme3-jbullet') + + // EITHER use jme3-bullet + jme3-bullet-native OR ELSE jme3-jbullet + compile project(':jme3-bullet') + compile project(':jme3-bullet-native') +// compile project(':jme3-jbullet') + compile project(':jme3-jogg') compile project(':jme3-jogl') compile project(':jme3-lwjgl') 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/PhysicsHoverControl.java b/jme3-examples/src/main/java/jme3test/bullet/PhysicsHoverControl.java index f914f5da56..901b3433c3 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/PhysicsHoverControl.java +++ b/jme3-examples/src/main/java/jme3test/bullet/PhysicsHoverControl.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 @@ -146,6 +146,7 @@ public void prePhysicsTick(PhysicsSpace space, float f) { Vector3f dir = getForwardVector(tempVect2).multLocal(1, 0, 1).normalizeLocal(); getLinearVelocity(tempVect3); Vector3f linearVelocity = tempVect3.multLocal(1, 0, 1); + float groundSpeed = linearVelocity.length(); if (steeringValue != 0) { if (rotationVelocity < 1 && rotationVelocity > -1) { @@ -164,16 +165,18 @@ public void prePhysicsTick(PhysicsSpace space, float f) { // if we are not going where we want to go. // this will prevent "drifting" and thus improve control // of the vehicle - float d = dir.dot(linearVelocity.normalize()); - Vector3f counter = dir.project(linearVelocity).normalizeLocal().negateLocal().multLocal(1 - d); - applyForce(counter.multLocal(mass * 10), Vector3f.ZERO); + if (groundSpeed > FastMath.ZERO_TOLERANCE) { + float d = dir.dot(linearVelocity.normalize()); + Vector3f counter = dir.project(linearVelocity).normalizeLocal().negateLocal().multLocal(1 - d); + applyForce(counter.multLocal(mass * 10), Vector3f.ZERO); + } if (linearVelocity.length() < 30) { applyForce(dir.multLocal(accelerationValue), Vector3f.ZERO); } } else { // counter the acceleration value - if (linearVelocity.length() > FastMath.ZERO_TOLERANCE) { + if (groundSpeed > FastMath.ZERO_TOLERANCE) { linearVelocity.normalizeLocal().negateLocal(); applyForce(linearVelocity.mult(mass * 10), Vector3f.ZERO); } diff --git a/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java b/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java index c0fa6f0653..4d8a5a92f9 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java +++ b/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java @@ -36,6 +36,7 @@ import com.jme3.asset.TextureKey; import com.jme3.bullet.PhysicsSpace; import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.collision.shapes.GImpactCollisionShape; import com.jme3.bullet.collision.shapes.MeshCollisionShape; import com.jme3.bullet.control.RigidBodyControl; import com.jme3.input.MouseInput; @@ -44,13 +45,18 @@ import com.jme3.light.AmbientLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; import com.jme3.scene.Node; +import com.jme3.scene.VertexBuffer; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; import com.jme3.texture.Texture; +import com.jme3.util.BufferUtils; /** * @@ -59,8 +65,7 @@ public class PhysicsTestHelper { /** - * creates a simple physics test world with a floor, an obstacle and some - * test boxes + * creates a simple physics test world with a floor, an obstacle and some test boxes * * @param rootNode where lights and geometries should be added * @param assetManager for loading assets @@ -107,7 +112,7 @@ public static void createPhysicsTestWorld(Node rootNode, AssetManager assetManag space.add(sphereGeometry); } - + public static void createPhysicsTestWorldSoccer(Node rootNode, AssetManager assetManager, PhysicsSpace space) { AmbientLight light = new AmbientLight(); light.setColor(ColorRGBA.LightGray); @@ -140,24 +145,24 @@ public static void createPhysicsTestWorldSoccer(Node rootNode, AssetManager asse space.add(ballGeometry); } { - //immovable Box with mesh collision shape - Box box = new Box(1, 1, 1); - Geometry boxGeometry = new Geometry("Box", box); - boxGeometry.setMaterial(material); - boxGeometry.setLocalTranslation(4, 1, 2); - boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0)); - rootNode.attachChild(boxGeometry); - space.add(boxGeometry); + //immovable Box with mesh collision shape + Box box = new Box(1, 1, 1); + Geometry boxGeometry = new Geometry("Box", box); + boxGeometry.setMaterial(material); + boxGeometry.setLocalTranslation(4, 1, 2); + boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0)); + rootNode.attachChild(boxGeometry); + space.add(boxGeometry); } { - //immovable Box with mesh collision shape - Box box = new Box(1, 1, 1); - Geometry boxGeometry = new Geometry("Box", box); - boxGeometry.setMaterial(material); - boxGeometry.setLocalTranslation(4, 3, 4); - boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0)); - rootNode.attachChild(boxGeometry); - space.add(boxGeometry); + //immovable Box with mesh collision shape + Box box = new Box(1, 1, 1); + Geometry boxGeometry = new Geometry("Box", box); + boxGeometry.setMaterial(material); + boxGeometry.setLocalTranslation(4, 3, 4); + boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0)); + rootNode.attachChild(boxGeometry); + space.add(boxGeometry); } } @@ -211,8 +216,7 @@ public static Node createPhysicsTestNode(AssetManager manager, CollisionShape sh } /** - * creates the necessary inputlistener and action to shoot balls from the - * camera + * creates the necessary inputlistener and action to shoot balls from the camera * * @param app the application that's running * @param rootNode where ball geometries should be added @@ -246,4 +250,116 @@ public void onAction(String name, boolean keyPressed, float tpf) { app.getInputManager().addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); app.getInputManager().addListener(actionListener, "shoot"); } + + /** + * Creates a curved "floor" with a GImpactCollisionShape provided as the RigidBodyControl's collision + * shape. Surface has four slightly concave corners to allow for multiple tests and minimize falling off + * the edge of the floor. + * + * @param assetManager for loading assets + * @param floorDimensions width/depth of the "floor" (X/Z) + * @param position sets the floor's local translation + * @return + */ + public static Geometry createGImpactTestFloor(AssetManager assetManager, float floorDimensions, Vector3f position) { + Geometry floor = createTestFloor(assetManager, floorDimensions, position, ColorRGBA.Red); + RigidBodyControl floorControl = new RigidBodyControl(new GImpactCollisionShape(floor.getMesh()), 0); + floor.addControl(floorControl); + return floor; + } + + /** + * Creates a curved "floor" with a MeshCollisionShape provided as the RigidBodyControl's collision shape. + * Surface has four slightly concave corners to allow for multiple tests and minimize falling off the edge + * of the floor. + * + * @param assetManager for loading assets + * @param floorDimensions width/depth of the "floor" (X/Z) + * @param position sets the floor's local translation + * @return + */ + public static Geometry createMeshTestFloor(AssetManager assetManager, float floorDimensions, Vector3f position) { + Geometry floor = createTestFloor(assetManager, floorDimensions, position, new ColorRGBA(0.5f, 0.5f, 0.9f, 1)); + RigidBodyControl floorControl = new RigidBodyControl(new MeshCollisionShape(floor.getMesh()), 0); + floor.addControl(floorControl); + return floor; + } + + private static Geometry createTestFloor(AssetManager assetManager, float floorDimensions, Vector3f position, ColorRGBA color) { + Geometry floor = new Geometry("floor", createFloorMesh(20, floorDimensions)); + Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + material.getAdditionalRenderState().setWireframe(true); + material.setColor("Color", color); + floor.setMaterial(material); + floor.setLocalTranslation(position); + return floor; + } + + private static Mesh createFloorMesh(int meshDetail, float floorDimensions) { + if (meshDetail < 10) { + meshDetail = 10; + } + int numVertices = meshDetail * meshDetail * 2 * 3;//width * depth * two tris * 3 verts per tri + + int[] indexBuf = new int[numVertices]; + int i = 0; + for (int x = 0; x < meshDetail; x++) { + for (int z = 0; z < meshDetail; z++) { + indexBuf[i] = i++; + indexBuf[i] = i++; + indexBuf[i] = i++; + indexBuf[i] = i++; + indexBuf[i] = i++; + indexBuf[i] = i++; + } + } + + float[] vertBuf = new float[numVertices * 3]; + float xIncrement = floorDimensions / meshDetail; + float zIncrement = floorDimensions / meshDetail; + int j = 0; + for (int x = 0; x < meshDetail; x++) { + float xPos = x * xIncrement; + for (int z = 0; z < meshDetail; z++) { + float zPos = z * zIncrement; + //First tri + vertBuf[j++] = xPos; + vertBuf[j++] = getY(xPos, zPos, floorDimensions); + vertBuf[j++] = zPos; + vertBuf[j++] = xPos; + vertBuf[j++] = getY(xPos, zPos + zIncrement, floorDimensions); + vertBuf[j++] = zPos + zIncrement; + vertBuf[j++] = xPos + xIncrement; + vertBuf[j++] = getY(xPos + xIncrement, zPos, floorDimensions); + vertBuf[j++] = zPos; + //Second tri + vertBuf[j++] = xPos; + vertBuf[j++] = getY(xPos, zPos + zIncrement, floorDimensions); + vertBuf[j++] = zPos + zIncrement; + vertBuf[j++] = xPos + xIncrement; + vertBuf[j++] = getY(xPos + xIncrement, zPos + zIncrement, floorDimensions); + vertBuf[j++] = zPos + zIncrement; + vertBuf[j++] = xPos + xIncrement; + vertBuf[j++] = getY(xPos + xIncrement, zPos, floorDimensions); + vertBuf[j++] = zPos; + } + } + + Mesh m = new Mesh(); + m.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(indexBuf)); + m.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertBuf)); + m.updateBound(); + return m; + } + + private static float getY(float x, float z, float max) { + float yMaxHeight = 8; + float xv = FastMath.unInterpolateLinear(FastMath.abs(x - (max / 2)), 0, max) * FastMath.TWO_PI; + float zv = FastMath.unInterpolateLinear(FastMath.abs(z - (max / 2)), 0, max) * FastMath.TWO_PI; + + float xComp = (FastMath.sin(xv) + 1) * 0.5f; + float zComp = (FastMath.sin(zv) + 1) * 0.5f; + + return -yMaxHeight * xComp * zComp; + } } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java b/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java index c247bfe854..922ce973b2 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.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 @@ -80,6 +80,15 @@ public class TestHoveringTank extends SimpleApplication implements AnalogListene PointLight pl; Geometry lightMdl; Geometry collisionMarker; + /** + * initial location of the tank (in world/physics-space coordinates) + */ + final private Vector3f startLocation = new Vector3f(-140f, 50f, -23f); + /** + * initial orientation of the tank (in world/physics-space coordinates) + */ + final private Quaternion startOrientation + = new Quaternion(new float[]{0f, 0.01f, 0f}); public static void main(String[] args) { TestHoveringTank app = new TestHoveringTank(); @@ -143,8 +152,8 @@ private void buildPlayer() { spaceCraft = assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml"); CollisionShape colShape = CollisionShapeFactory.createDynamicMeshShape(spaceCraft); spaceCraft.setShadowMode(ShadowMode.CastAndReceive); - spaceCraft.setLocalTranslation(new Vector3f(-140, 50, -23)); - spaceCraft.setLocalRotation(new Quaternion(new float[]{0, 0.01f, 0})); + spaceCraft.setLocalTranslation(startLocation); + spaceCraft.setLocalRotation(startOrientation); hoverControl = new PhysicsHoverControl(colShape, 500); @@ -206,8 +215,10 @@ public void onAction(String binding, boolean value, float tpf) { } else if (binding.equals("Reset")) { if (value) { System.out.println("Reset"); - hoverControl.setPhysicsLocation(new Vector3f(-140, 14, -23)); - hoverControl.setPhysicsRotation(new Matrix3f()); + hoverControl.setPhysicsLocation(startLocation); + hoverControl.setPhysicsRotation(startOrientation); + hoverControl.setAngularVelocity(Vector3f.ZERO); + hoverControl.setLinearVelocity(Vector3f.ZERO); hoverControl.clearForces(); } else { } @@ -272,7 +283,7 @@ private void createTerrain() { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); - matRock.setTexture("NormalMap_1", normalMap2); + matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestIssue1120.java b/jme3-examples/src/main/java/jme3test/bullet/TestIssue1120.java new file mode 100644 index 0000000000..e4a14344ad --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/bullet/TestIssue1120.java @@ -0,0 +1,216 @@ +/* + * 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.bullet; + +import com.jme3.app.SimpleApplication; +import com.jme3.bullet.BulletAppState; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.collision.shapes.GImpactCollisionShape; +import com.jme3.bullet.control.RigidBodyControl; +import com.jme3.bullet.debug.BulletDebugAppState; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Cylinder; +import com.jme3.system.AppSettings; +import java.util.ArrayList; +import java.util.List; + +/** + * Test demonstrating a GImpactCollisionShape falling through a curved mesh, when using JBullet. Bullet native + * does not experience this issue at the time this test was created. + * + * @author lou + */ +public class TestIssue1120 extends SimpleApplication { + + private static TestIssue1120 test; + private BulletAppState bulletAppState; + private final boolean physicsDebug = true; + protected BitmapFont font; + private final BitmapText[] testInfo = new BitmapText[2]; + private BitmapText speedText; + private final List testObjects = new ArrayList<>(); + private static final boolean SKIP_SETTINGS = false;//Used for repeated runs of this test during dev + private float bulletSpeed = 0.5f; + + public static void main(String[] args) { + test = new TestIssue1120(); + test.setSettings(new AppSettings(true)); + test.settings.setFrameRate(60); + if (SKIP_SETTINGS) { + test.settings.setWidth(1920); + test.settings.setHeight(1150); + test.showSettings = !SKIP_SETTINGS; + } + test.start(); + } + + @Override + public void simpleInitApp() { + cam.setLocation(new Vector3f(-7.285349f, -2.2638104f, 4.954474f)); + cam.setRotation(new Quaternion(0.07345789f, 0.92521834f, -0.2876841f, 0.23624739f)); + getFlyByCamera().setMoveSpeed(5); + + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + dl.setColor(ColorRGBA.Green); + rootNode.addLight(dl); + + //Setup interactive test controls + inputManager.addMapping("restart", new KeyTrigger(KeyInput.KEY_SPACE)); + inputManager.addMapping("pause", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); + inputManager.addMapping("+", new KeyTrigger(KeyInput.KEY_ADD), new KeyTrigger(KeyInput.KEY_EQUALS)); + inputManager.addMapping("-", new KeyTrigger(KeyInput.KEY_SUBTRACT), new KeyTrigger(KeyInput.KEY_MINUS)); + inputManager.addListener((ActionListener) (String name, boolean isPressed, float tpf) -> { + if (!isPressed) { + return; + } + switch (name) { + case "restart": + cleanup(); + initializeNewTest(); + break; + case "pause": + bulletAppState.setSpeed(bulletAppState.getSpeed() > 0.1 ? 0 : bulletSpeed); + break; + case "+": + bulletSpeed += 0.1f; + if (bulletSpeed > 1f) { + bulletSpeed = 1f; + } + bulletAppState.setSpeed(bulletSpeed); + break; + case "-": + bulletSpeed -= 0.1f; + if (bulletSpeed < 0.1f) { + bulletSpeed = 0.1f; + } + bulletAppState.setSpeed(bulletSpeed); + break; + } + }, "pause", "restart", "+", "-"); + + guiNode = getGuiNode(); + font = assetManager.loadFont("Interface/Fonts/Default.fnt"); + testInfo[0] = new BitmapText(font); + testInfo[1] = new BitmapText(font); + speedText = new BitmapText(font); + + float lineHeight = testInfo[0].getLineHeight(); + testInfo[0].setText("Camera move: W/A/S/D/Q/Z +/-: Increase/Decrease Speed"); + testInfo[0].setLocalTranslation(5, test.settings.getHeight(), 0); + guiNode.attachChild(testInfo[0]); + testInfo[1].setText("Left Click: Toggle pause Space: Restart test"); + testInfo[1].setLocalTranslation(5, test.settings.getHeight() - lineHeight, 0); + guiNode.attachChild(testInfo[1]); + + speedText.setLocalTranslation(202, lineHeight * 1, 0); + guiNode.attachChild(speedText); + + initializeNewTest(); + } + + private void initializeNewTest() { + bulletAppState = new BulletAppState(); + bulletAppState.setDebugEnabled(physicsDebug); + stateManager.attach(bulletAppState); + + bulletAppState.setSpeed(bulletSpeed); + + dropTest(); + + Geometry leftFloor = PhysicsTestHelper.createMeshTestFloor(assetManager, 20, new Vector3f(-11, -5, -10)); + addObject(leftFloor); + + //Hide physics debug visualization for floors + if (physicsDebug) { + BulletDebugAppState bulletDebugAppState = stateManager.getState(BulletDebugAppState.class); + bulletDebugAppState.setFilter((Object obj) -> { + return !(obj.equals(leftFloor.getControl(RigidBodyControl.class))); + }); + } + } + + private void addObject(Spatial s) { + testObjects.add(s); + rootNode.attachChild(s); + physicsSpace().add(s); + } + + private void dropTest() { + attachTestObject(new Cylinder(2, 16, 0.2f, 2f, true), new Vector3f(0f, 2f, -5f), 2); + attachTestObject(new Cylinder(2, 16, 0.2f, 2f, true), new Vector3f(-1f, 2f, -5f), 2); + attachTestObject(new Cylinder(2, 16, 0.2f, 2f, true), new Vector3f(-2f, 2f, -5f), 2); + attachTestObject(new Cylinder(2, 16, 0.2f, 2f, true), new Vector3f(-3f, 2f, -5f), 2); + } + + private void attachTestObject(Mesh mesh, Vector3f position, float mass) { + Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Geometry g = new Geometry("mesh", mesh); + g.setLocalTranslation(position); + g.setMaterial(material); + + RigidBodyControl control = new RigidBodyControl(new GImpactCollisionShape(mesh), mass); + g.addControl(control); + addObject(g); + } + + private PhysicsSpace physicsSpace() { + return bulletAppState.getPhysicsSpace(); + } + + @Override + public void simpleUpdate(float tpf) { + speedText.setText("Speed: " + String.format("%.1f", bulletSpeed)); + } + + private void cleanup() { + stateManager.detach(bulletAppState); + stateManager.detach(stateManager.getState(BulletDebugAppState.class)); + for (Spatial s : testObjects) { + rootNode.detachChild(s); + } + } +} diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestIssue1125.java b/jme3-examples/src/main/java/jme3test/bullet/TestIssue1125.java new file mode 100644 index 0000000000..d39c629f6c --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/bullet/TestIssue1125.java @@ -0,0 +1,229 @@ +/* + * 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.bullet; + +import com.jme3.app.SimpleApplication; +import com.jme3.bullet.BulletAppState; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.control.RigidBodyControl; +import com.jme3.bullet.util.CollisionShapeFactory; +import com.jme3.font.BitmapText; +import com.jme3.material.Material; +import com.jme3.material.RenderState; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.terrain.geomipmap.TerrainQuad; +import java.util.logging.Logger; + +/** + * Test case for JME issue #1125: heightfield collision shapes don't match + * TerrainQuad. + *

+ * If successful, just one set of grid diagonals will be visible. If + * unsuccessful, you'll see both green diagonals (the TerrainQuad) and + * perpendicular blue diagonals (physics debug). + *

+ * Use this test with jme3-bullet only; it can yield false success with + * jme3-jbullet due to JME issue #1129. + * + * @author Stephen Gold sgold@sonic.net + */ +public class TestIssue1125 extends SimpleApplication { + // ************************************************************************* + // constants and loggers + + /** + * message logger for this class + */ + final public static Logger logger + = Logger.getLogger(TestIssue1125.class.getName()); + // ************************************************************************* + // fields + + /** + * height array for a small heightfield + */ + final private float[] nineHeights = new float[9]; + /** + * green wireframe material for the TerrainQuad + */ + private Material quadMaterial; + /** + * space for physics simulation + */ + private PhysicsSpace physicsSpace; + // ************************************************************************* + // new methods exposed + + /** + * Main entry point for the TestIssue1125 application. + * + * @param ignored array of command-line arguments (not null) + */ + public static void main(String[] ignored) { + new TestIssue1125().start(); + } + // ************************************************************************* + // SimpleApplication methods + + /** + * Initialize this application. + */ + @Override + public void simpleInitApp() { + configureCamera(); + configureMaterials(); + viewPort.setBackgroundColor(new ColorRGBA(0.5f, 0.2f, 0.2f, 1f)); + configurePhysics(); + initializeHeightData(); + addTerrain(); + showHints(); + } + // ************************************************************************* + // private methods + + /** + * Add 3x3 terrain to the scene and the PhysicsSpace. + */ + private void addTerrain() { + int patchSize = 3; + int mapSize = 3; + TerrainQuad quad + = new TerrainQuad("terrain", patchSize, mapSize, nineHeights); + rootNode.attachChild(quad); + quad.setMaterial(quadMaterial); + + CollisionShape shape = CollisionShapeFactory.createMeshShape(quad); + float massForStatic = 0f; + RigidBodyControl rbc = new RigidBodyControl(shape, massForStatic); + rbc.setPhysicsSpace(physicsSpace); + quad.addControl(rbc); + } + + /** + * Configure the camera during startup. + */ + private void configureCamera() { + float fHeight = cam.getFrustumTop() - cam.getFrustumBottom(); + float fWidth = cam.getFrustumRight() - cam.getFrustumLeft(); + float fAspect = fWidth / fHeight; + float yDegrees = 45f; + float near = 0.02f; + float far = 20f; + cam.setFrustumPerspective(yDegrees, fAspect, near, far); + + flyCam.setMoveSpeed(5f); + + cam.setLocation(new Vector3f(2f, 4.7f, 0.4f)); + cam.setRotation(new Quaternion(0.348f, -0.64f, 0.4f, 0.556f)); + } + + /** + * Configure materials during startup. + */ + private void configureMaterials() { + quadMaterial = new Material(assetManager, + "Common/MatDefs/Misc/Unshaded.j3md"); + quadMaterial.setColor("Color", ColorRGBA.Green.clone()); + RenderState ars = quadMaterial.getAdditionalRenderState(); + ars.setWireframe(true); + } + + /** + * Configure physics during startup. + */ + private void configurePhysics() { + BulletAppState bulletAppState = new BulletAppState(); + stateManager.attach(bulletAppState); + bulletAppState.setDebugEnabled(true); + physicsSpace = bulletAppState.getPhysicsSpace(); + } + + /** + * Initialize the height data during startup. + */ + private void initializeHeightData() { + nineHeights[0] = 1f; + nineHeights[1] = 0f; + nineHeights[2] = 1f; + nineHeights[3] = 0f; + nineHeights[4] = 0.5f; + nineHeights[5] = 0f; + nineHeights[6] = 1f; + nineHeights[7] = 0f; + nineHeights[8] = 1f; + } + + private void showHints() { + guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); + int numLines = 3; + BitmapText lines[] = new BitmapText[numLines]; + for (int i = 0; i < numLines; ++i) { + lines[i] = new BitmapText(guiFont); + } + + String p = "Test for jMonkeyEngine issue #1125"; + if (isNativeBullet()) { + lines[0].setText(p + " with native Bullet"); + } else { + lines[0].setText(p + " with JBullet (may yield false success)"); + } + lines[1].setText("Use W/A/S/D/Q/Z/arrow keys to move the camera."); + lines[2].setText("F5: render stats, C: camera pos, M: mem stats"); + + float textHeight = guiFont.getCharSet().getLineHeight(); + float viewHeight = cam.getHeight(); + float viewWidth = cam.getWidth(); + for (int i = 0; i < numLines; ++i) { + float left = Math.round((viewWidth - lines[i].getLineWidth()) / 2f); + float top = viewHeight - i * textHeight; + lines[i].setLocalTranslation(left, top, 0f); + guiNode.attachChild(lines[i]); + } + } + + /** + * Determine which physics library is in use. + * + * @return true for C++ Bullet, false for JBullet (jme3-jbullet) + */ + private boolean isNativeBullet() { + try { + Class clazz = Class.forName("com.jme3.bullet.util.NativeMeshUtil"); + return clazz != null; + } catch (ClassNotFoundException exception) { + return false; + } + } +} diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java b/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java index 4359154d12..532052da07 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java @@ -262,7 +262,7 @@ private void createTerrain() { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); - matRock.setTexture("NormalMap_1", normalMap2); + matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; diff --git a/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java b/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java new file mode 100644 index 0000000000..484f0f1774 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/bullet/shape/TestGimpactShape.java @@ -0,0 +1,343 @@ +/* + * 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.bullet.shape; + +import com.jme3.app.SimpleApplication; +import com.jme3.bullet.BulletAppState; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.collision.shapes.GImpactCollisionShape; +import com.jme3.bullet.control.RigidBodyControl; +import com.jme3.bullet.debug.BulletDebugAppState; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.PQTorus; +import com.jme3.scene.shape.Torus; +import com.jme3.system.AppSettings; +import java.util.ArrayList; +import java.util.List; +import jme3test.bullet.PhysicsTestHelper; + +/** + * This test demonstrates various GImpactCollisionShapes colliding against two identical curved surfaces. The + * left surface is a MeshCollisionShape, right surface is another GImpactCollisionShape. An ideal result is + * for all objects to land and change to a blue colored mesh indicating they are inactive. Falling through the + * floor, or never going inactive (bouncing forever) are failure conditions. + *

+ * Observations as of June 2019 (JME v3.3.0-alpha2): + *

    + *
  1. + * With default starting parameters, Native Bullet should pass the test parameters above. JBullet fails due to + * the rocket/MeshCollisionShape never going inactive. + *
  2. + *
  3. + * Native Bullet behaves better than JBullet. JBullet sometimes allows objects to "gain too much energy" after + * a collision, such as the rocket or teapot. Native also does this, to a lesser degree. This generally + * appears to happen at larger object scales. + *
  4. + *
  5. + * JBullet allows some objects to get "stuck" inside the floor, which usually results in a fall-through + * eventually, generally a larger scales for this test. + *
  6. + *
  7. + * Some shapes such as PQTorus & signpost never go inactive at larger scales for both Native and JBullet (test + * at 1.5 and 1.9 scale) + *
  8. + *
+ * + * @author lou + */ +public class TestGimpactShape extends SimpleApplication { + + private static TestGimpactShape test; + private BulletAppState bulletAppState; + private int solverNumIterations = 10; + private BitmapFont font; + private final BitmapText[] testInfo = new BitmapText[2]; + private BitmapText timeElapsedTxt; + private BitmapText solverNumIterationsTxt; + private BitmapText testScale; + private final List testObjects = new ArrayList<>(); + private float testTimer = 0; + private float scaleMod = 1; + private boolean restart = true; + private static final boolean SKIP_SETTINGS = false;//Used for repeated runs of this test during dev + + public static void main(String[] args) { + test = new TestGimpactShape(); + test.setSettings(new AppSettings(true)); + test.settings.setVSync(true); + if (SKIP_SETTINGS) { + test.settings.setWidth(1920); + test.settings.setHeight(1150); + test.showSettings = !SKIP_SETTINGS; + } + test.start(); + } + + @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); + + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + dl.setColor(ColorRGBA.Green); + rootNode.addLight(dl); + + //Setup test instructions + guiNode = getGuiNode(); + font = assetManager.loadFont("Interface/Fonts/Default.fnt"); + testInfo[0] = new BitmapText(font); + testInfo[1] = new BitmapText(font); + timeElapsedTxt = new BitmapText(font); + solverNumIterationsTxt = new BitmapText(font); + testScale = new BitmapText(font); + + float lineHeight = testInfo[0].getLineHeight(); + testInfo[0].setText("Camera move:W/A/S/D/Q/Z Solver iterations: 1=10, 2=20, 3=30"); + testInfo[0].setLocalTranslation(5, test.settings.getHeight(), 0); + guiNode.attachChild(testInfo[0]); + testInfo[1].setText("P: Toggle pause Inc/Dec object scale: +, - Space: Restart test"); + testInfo[1].setLocalTranslation(5, test.settings.getHeight() - lineHeight, 0); + guiNode.attachChild(testInfo[1]); + + timeElapsedTxt.setLocalTranslation(202, lineHeight * 1, 0); + guiNode.attachChild(timeElapsedTxt); + solverNumIterationsTxt.setLocalTranslation(202, lineHeight * 2, 0); + guiNode.attachChild(solverNumIterationsTxt); + testScale.setLocalTranslation(202, lineHeight * 3, 0); + guiNode.attachChild(testScale); + + //Setup interactive test controls + inputManager.addMapping("restart", new KeyTrigger(KeyInput.KEY_SPACE)); + inputManager.addListener((ActionListener) (String name, boolean isPressed, float tpf) -> { + restart = true; + }, "restart"); + + inputManager.addMapping("pause", new KeyTrigger(KeyInput.KEY_P)); + inputManager.addListener((ActionListener) (String name, boolean isPressed, float tpf) -> { + if (!isPressed) { + return; + } + bulletAppState.setSpeed(bulletAppState.getSpeed() > 0.1 ? 0 : 1); + }, "pause"); + + inputManager.addMapping("1", new KeyTrigger(KeyInput.KEY_1)); + inputManager.addMapping("2", new KeyTrigger(KeyInput.KEY_2)); + inputManager.addMapping("3", new KeyTrigger(KeyInput.KEY_3)); + inputManager.addMapping("+", new KeyTrigger(KeyInput.KEY_ADD), new KeyTrigger(KeyInput.KEY_EQUALS)); + inputManager.addMapping("-", new KeyTrigger(KeyInput.KEY_SUBTRACT), new KeyTrigger(KeyInput.KEY_MINUS)); + inputManager.addListener((ActionListener) (String name, boolean isPressed, float tpf) -> { + if (!isPressed) { + return; + } + switch (name) { + case "1": + solverNumIterations = 10; + break; + case "2": + solverNumIterations = 20; + break; + case "3": + solverNumIterations = 30; + break; + case "+": + scaleMod += scaleMod < 1.9f ? 0.1f : 0; + break; + case "-": + scaleMod -= scaleMod > 0.5f ? 0.1f : 0; + break; + } + restart = true; + }, "1", "2", "3", "+", "-"); + + initializeNewTest(); + } + + private void initializeNewTest() { + testScale.setText("Object scale: " + String.format("%.1f", scaleMod)); + solverNumIterationsTxt.setText("Solver Iterations: " + solverNumIterations); + + bulletAppState = new BulletAppState(); + bulletAppState.setDebugEnabled(true); + stateManager.attach(bulletAppState); + bulletAppState.getPhysicsSpace().setSolverNumIterations(solverNumIterations); + + float floorSize = 80; + //Left side test - GImpact objects collide with MeshCollisionShape floor + Vector3f leftFloorPos = new Vector3f(-41, -5, -10); + Vector3f leftFloorCenter = leftFloorPos.add(floorSize / 2, 0, floorSize / 2); + + dropTest1(leftFloorCenter); + dropTest2(leftFloorCenter); + dropPot(leftFloorCenter); + dropSword(leftFloorCenter); + dropSign(leftFloorCenter); + dropRocket(leftFloorCenter); + + Geometry leftFloor = PhysicsTestHelper.createMeshTestFloor(assetManager, floorSize, leftFloorPos); + addObject(leftFloor); + + //Right side test - GImpact objects collide with GImpact floor + Vector3f rightFloorPos = new Vector3f(41, -5, -10); + Vector3f rightFloorCenter = rightFloorPos.add(floorSize / 2, 0, floorSize / 2); + + dropTest1(rightFloorCenter); + dropTest2(rightFloorCenter); + dropPot(rightFloorCenter); + dropSword(rightFloorCenter); + dropSign(rightFloorCenter); + dropRocket(rightFloorCenter); + + Geometry rightFloor = PhysicsTestHelper.createGImpactTestFloor(assetManager, floorSize, rightFloorPos); + addObject(rightFloor); + + //Hide physics debug visualization for floors + BulletDebugAppState bulletDebugAppState = stateManager.getState(BulletDebugAppState.class); + bulletDebugAppState.setFilter((Object obj) -> { + return !(obj.equals(rightFloor.getControl(RigidBodyControl.class)) + || obj.equals(leftFloor.getControl(RigidBodyControl.class))); + }); + } + + private void addObject(Spatial s) { + testObjects.add(s); + rootNode.attachChild(s); + physicsSpace().add(s); + } + + private void dropTest1(Vector3f offset) { + offset = offset.add(-18, 6, -18); + attachTestObject(new Torus(16, 16, 0.15f, 0.5f), new Vector3f(-12f, 0f, 5f).add(offset), 1); + attachTestObject(new PQTorus(2f, 3f, 0.6f, 0.2f, 48, 16), new Vector3f(0, 0, 0).add(offset), 5); + + } + + private void dropTest2(Vector3f offset) { + offset = offset.add(18, 6, -18); + attachTestObject(new Torus(16, 16, 0.3f, 0.8f), new Vector3f(12f, 0f, 5f).add(offset), 3); + attachTestObject(new PQTorus(3f, 5f, 0.8f, 0.2f, 96, 16), new Vector3f(0, 0, 0).add(offset), 10); + } + + private void dropPot(Vector3f offset) { + drop(offset.add(-12, 7, 15), "Models/Teapot/Teapot.mesh.xml", 1.0f, 2); + } + + private void dropSword(Vector3f offset) { + drop(offset.add(-10, 5, 3), "Models/Sinbad/Sword.mesh.xml", 1.0f, 2); + } + + private void dropSign(Vector3f offset) { + drop(offset.add(9, 15, 5), "Models/Sign Post/Sign Post.mesh.xml", 1.0f, 1); + } + + private void dropRocket(Vector3f offset) { + RigidBodyControl c = drop(offset.add(26, 4, 7), "Models/SpaceCraft/Rocket.mesh.xml", 4.0f, 3); + c.setAngularDamping(0.5f); + c.setLinearDamping(0.5f); + } + + private RigidBodyControl drop(Vector3f offset, String model, float scale, float mass) { + scale *= scaleMod; + Node n = (Node) assetManager.loadModel(model); + n.setLocalTranslation(offset); + n.rotate(0, 0, -FastMath.HALF_PI); + + Geometry tp = ((Geometry) n.getChild(0)); + tp.scale(scale); + Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + tp.setMaterial(mat); + + Mesh mesh = tp.getMesh(); + GImpactCollisionShape shape = new GImpactCollisionShape(mesh); + shape.setScale(new Vector3f(scale, scale, scale)); + + RigidBodyControl control = new RigidBodyControl(shape, mass); + n.addControl(control); + addObject(n); + return control; + } + + private void attachTestObject(Mesh mesh, Vector3f position, float mass) { + Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); + Geometry g = new Geometry("mesh", mesh); + g.scale(scaleMod); + g.setLocalTranslation(position); + g.setMaterial(material); + + GImpactCollisionShape shape = new GImpactCollisionShape(mesh); + shape.setScale(new Vector3f(scaleMod, scaleMod, scaleMod)); + RigidBodyControl control = new RigidBodyControl(shape, mass); + g.addControl(control); + addObject(g); + } + + private PhysicsSpace physicsSpace() { + return bulletAppState.getPhysicsSpace(); + } + + @Override + public void simpleUpdate(float tpf) { + testTimer += tpf * bulletAppState.getSpeed(); + + if (restart) { + cleanup(); + initializeNewTest(); + restart = false; + testTimer = 0; + } + timeElapsedTxt.setText("Time Elapsed: " + String.format("%.3f", testTimer)); + } + + private void cleanup() { + stateManager.detach(bulletAppState); + stateManager.detach(stateManager.getState(BulletDebugAppState.class)); + for (Spatial s : testObjects) { + rootNode.detachChild(s); + } + } +} 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/light/TestSpotLightTerrain.java b/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java index 02bd48abbd..43e7beba29 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java +++ b/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java @@ -165,7 +165,7 @@ private void makeTerrain() { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap", normalMap0); - matTerrain.setTexture("NormalMap_1", normalMap2); + matTerrain.setTexture("NormalMap_1", normalMap1); matTerrain.setTexture("NormalMap_2", normalMap2); matTerrain.setTexture("NormalMap_4", normalMap2); diff --git a/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java b/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java index 263d45ff3d..6fee9e404b 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java +++ b/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java @@ -166,7 +166,7 @@ private void createTerrain(Node rootNode) { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); - matRock.setTexture("NormalMap_1", normalMap2); + matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; diff --git a/jme3-examples/src/main/java/jme3test/post/TestFog.java b/jme3-examples/src/main/java/jme3test/post/TestFog.java index 6d3b667aba..3ecac03a0d 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestFog.java +++ b/jme3-examples/src/main/java/jme3test/post/TestFog.java @@ -180,7 +180,7 @@ private void createTerrain(Node rootNode) { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(Texture.WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); - matRock.setTexture("NormalMap_1", normalMap2); + matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestAlphaToCoverage.java b/jme3-examples/src/main/java/jme3test/renderer/TestAlphaToCoverage.java new file mode 100644 index 0000000000..1765e5478f --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/renderer/TestAlphaToCoverage.java @@ -0,0 +1,54 @@ +package jme3test.renderer; +import com.jme3.app.SimpleApplication; + +import com.jme3.renderer.lwjgl.LwjglGL; +import com.jme3.renderer.opengl.GL; +import com.jme3.renderer.opengl.GLExt; +import com.jme3.renderer.opengl.GLFbo; +import com.jme3.renderer.opengl.GLRenderer; +import com.jme3.renderer.lwjgl.LwjglGLExt; +import com.jme3.renderer.lwjgl.LwjglGLFboEXT; +import com.jme3.renderer.Caps; + +import java.util.EnumSet; + +/** + * Simple application to test the getter and setters of AlphaToCoverage and + * DefaultAnisotropicFilter from the GLRenderer class. + * + * Since the app doesn't display anything relevant a stop() has been added + * This starts and closes the app on a successful run + */ +public class TestAlphaToCoverage extends SimpleApplication { + + public static void main(String[] args) { + new TestAlphaToCoverage().start(); + } + + public GL gl = new LwjglGL(); + public GLExt glext = new LwjglGLExt(); + public GLFbo glfbo = new LwjglGLFboEXT(); + private GLRenderer glRenderer= new GLRenderer(gl,glext,glfbo); + + public EnumSet caps = glRenderer.getCaps(); + + + + @Override + public void simpleInitApp() { + glRenderer.setAlphaToCoverage(false); + assert !glRenderer.getAlphaToCoverage(); + + caps.add(Caps.Multisample); + glRenderer.setAlphaToCoverage(true); + assert glRenderer.getAlphaToCoverage(); + glRenderer.setAlphaToCoverage(false); + assert !glRenderer.getAlphaToCoverage(); + + glRenderer.setDefaultAnisotropicFilter(1); + assert glRenderer.getDefaultAnisotropicFilter() == 1; + + stop(); + } + +} diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java index ef0f6c1bbe..cf5e9d75c1 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java @@ -294,7 +294,9 @@ public void onAction(final String name, final boolean keyPressed, final float tp TerrainGridAlphaMapTest.this.down = false; } } else if (name.equals("Jumps")) { - TerrainGridAlphaMapTest.this.player3.jump(); + if (usePhysics && keyPressed) { + player3.jump(); + } } } }; 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/terrain/TerrainTestReadWrite.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java index b6bba1c82c..8610962985 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java @@ -130,7 +130,7 @@ private void createMap() { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap", normalMap0); - matTerrain.setTexture("NormalMap_1", normalMap2); + matTerrain.setTexture("NormalMap_1", normalMap1); matTerrain.setTexture("NormalMap_2", normalMap2); matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 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/java/jme3test/water/TestMultiPostWater.java b/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java index 2611d9696d..9e9caf9d0a 100644 --- a/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java +++ b/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java @@ -160,7 +160,7 @@ private void createTerrain(Node rootNode) { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); - matRock.setTexture("NormalMap_1", normalMap2); + matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; diff --git a/jme3-examples/src/main/java/jme3test/water/TestPostWater.java b/jme3-examples/src/main/java/jme3test/water/TestPostWater.java index 21e11d153a..3d650c170b 100644 --- a/jme3-examples/src/main/java/jme3test/water/TestPostWater.java +++ b/jme3-examples/src/main/java/jme3test/water/TestPostWater.java @@ -276,7 +276,7 @@ private void createTerrain(Node rootNode) { Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); - matRock.setTexture("NormalMap_1", normalMap2); + matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; 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-jbullet/src/main/java/com/jme3/bullet/PhysicsSpace.java b/jme3-jbullet/src/main/java/com/jme3/bullet/PhysicsSpace.java index 2099f7f23f..42ac5040a5 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/PhysicsSpace.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/PhysicsSpace.java @@ -788,7 +788,7 @@ public float addSingleResult(LocalRayResult lrr, boolean bln) { /** * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults
- * You have to use different Transforms for start and end (at least distance > 0.4f). + * You have to use different Transforms for start and end (at least distance greater than 0.4f). * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center. */ public List sweepTest(CollisionShape shape, Transform start, Transform end) { @@ -804,7 +804,7 @@ public List sweepTest(CollisionShape shape, Transform st /** * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults
- * You have to use different Transforms for start and end (at least distance > 0.4f). + * You have to use different Transforms for start and end (at least distance greater than 0.4f). * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center. */ public List sweepTest(CollisionShape shape, Transform start, Transform end, List results) { @@ -917,7 +917,7 @@ public void setWorldMax(Vector3f worldMax) { * * The default is 10. Use 4 for low quality, 20 for high quality. * - * @param numIterations The number of iterations used by the contact & constraint solver. + * @param numIterations The number of iterations used by the contact and constraint solver. */ public void setSolverNumIterations(int numIterations) { dynamicsWorld.getSolverInfo().numIterations = numIterations; diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java b/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java index 5a7921fc0e..8dfe2b2f63 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java @@ -125,9 +125,14 @@ protected void createShape() { TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride); cShape = new GImpactMeshShape(tiv); cShape.setLocalScaling(Converter.convert(worldScale)); - ((GImpactMeshShape)cShape).updateBound(); cShape.setLocalScaling(Converter.convert(getScale())); cShape.setMargin(margin); + ((GImpactMeshShape) cShape).updateBound(); } + @Override + public void setScale(Vector3f scale) { + super.setScale(scale); + ((GImpactMeshShape) cShape).updateBound(); + } } diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java b/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java index 529cb9c86e..b0bccaa4b3 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java @@ -301,7 +301,7 @@ public float getSuspensionCompression() { * After adding the wheel, use direct wheel access.
* The damping coefficient for when the suspension is compressed. * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.
- * k = 0.0 undamped & bouncy, k = 1.0 critical damping
+ * k = 0.0 undamped/bouncy, k = 1.0 critical damping
* 0.1 to 0.3 are good values * @param suspensionCompression the suspensionCompression to set */ @@ -312,7 +312,7 @@ public void setSuspensionCompression(float suspensionCompression) { /** * The damping coefficient for when the suspension is compressed. * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.
- * k = 0.0 undamped & bouncy, k = 1.0 critical damping
+ * k = 0.0 undamped/bouncy, k = 1.0 critical damping
* 0.1 to 0.3 are good values * @param wheel * @param suspensionCompression diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java b/jme3-jbullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java index 789e0c6828..0490626a96 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java @@ -178,7 +178,7 @@ public float getWheelsDampingCompression() { /** * the damping coefficient for when the suspension is compressed. * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.
- * k = 0.0 undamped & bouncy, k = 1.0 critical damping
+ * k = 0.0 undamped/bouncy, k = 1.0 critical damping
* 0.1 to 0.3 are good values * @param wheelsDampingCompression */ diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index 624ad3e027..4d2d776c52 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -113,12 +113,11 @@ protected int[] getGLVersion(String renderer) { maj = 2; min = 0; break; - // case AppSettings.LWJGL_OPENGL30: - // maj=3; - // min=0; - // break; - case AppSettings.LWJGL_OPENGL3: - // case AppSettings.LWJGL_OPENGL32: + case AppSettings.LWJGL_OPENGL30: + maj = 3; + min = 0; + break; + case AppSettings.LWJGL_OPENGL32: maj = 3; min = 2; break; @@ -126,8 +125,7 @@ protected int[] getGLVersion(String renderer) { maj = 3; min = 3; break; - case AppSettings.LWJGL_OPENGL4: - // case AppSettings.LWJGL_OPENGL40: + case AppSettings.LWJGL_OPENGL40: maj = 4; min = 0; break; 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-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index a34ce795f5..8f3afca85a 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -105,9 +105,10 @@ public abstract class LwjglContext implements JmeContext { private static final Set SUPPORTED_RENDERS = new HashSet<>(Arrays.asList( AppSettings.LWJGL_OPENGL2, - AppSettings.LWJGL_OPENGL3, + AppSettings.LWJGL_OPENGL30, + AppSettings.LWJGL_OPENGL32, AppSettings.LWJGL_OPENGL33, - AppSettings.LWJGL_OPENGL4, + AppSettings.LWJGL_OPENGL40, AppSettings.LWJGL_OPENGL41, AppSettings.LWJGL_OPENGL42, AppSettings.LWJGL_OPENGL43, diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index a5d8254dba..e84c5c3ce6 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -76,7 +76,11 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { private static final Map RENDER_CONFIGS = new HashMap<>(); static { - RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL3, () -> { + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL30, () -> { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL32, () -> { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); }); @@ -84,7 +88,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); }); - RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL4, () -> { + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL40, () -> { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); }); 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 a9130f8386..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/serializing/Serializer.java b/jme3-networking/src/main/java/com/jme3/network/serializing/Serializer.java index 26374762eb..4e560fbfaa 100644 --- a/jme3-networking/src/main/java/com/jme3/network/serializing/Serializer.java +++ b/jme3-networking/src/main/java/com/jme3/network/serializing/Serializer.java @@ -153,7 +153,7 @@ public static void initialize() { /** * When set to true, classes that do not have intrinsic IDs in their - * @Serializable will not be auto-registered during write. Defaults + * {@code @Serializable } will not be auto-registered during write. Defaults * to true since this is almost never desired behavior with the way * this code works. Set to false to get the old permissive behavior. */ 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/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java index 7b9c8d93d1..cc9b54d454 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java @@ -397,10 +397,23 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { mesh.generateBindPose(); } + //Read morph target names + LinkedList targetNames = new LinkedList<>(); + if (meshData.has("extras") && meshData.getAsJsonObject("extras").has("targetNames")) { + JsonArray targetNamesJson = meshData.getAsJsonObject("extras").getAsJsonArray("targetNames"); + for (JsonElement target : targetNamesJson) { + targetNames.add(target.getAsString()); + } + } + + //Read morph targets JsonArray targets = meshObject.getAsJsonArray("targets"); if(targets != null){ for (JsonElement target : targets) { MorphTarget morphTarget = new MorphTarget(); + if (targetNames.size() > 0) { + morphTarget.setName(targetNames.pop()); + } for (Map.Entry entry : target.getAsJsonObject().entrySet()) { String bufferType = entry.getKey(); VertexBuffer.Type type = getVertexBufferType(bufferType); @@ -412,7 +425,8 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { mesh.addMorphTarget(morphTarget); } } - + + //Read mesh extras mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh); Geometry geom = new Geometry(null, mesh); 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/main/java/com/jme3/terrain/noise/fractal/FractalSum.java b/jme3-terrain/src/main/java/com/jme3/terrain/noise/fractal/FractalSum.java index 5fe5dcb4ac..df2fb235af 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/noise/fractal/FractalSum.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/noise/fractal/FractalSum.java @@ -36,7 +36,7 @@ /** * FractalSum is the simplest form of fractal functions summing up a few octaves - * of the noise value with an ever decreasing (0 < roughness < 1) amplitude + * of the noise value with an ever decreasing roughness (0 to 1) amplitude * * lacunarity = 2.0f is the classical octave distance * 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/VRApplication.java b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java index 0ea29d6455..c81d9b8825 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java @@ -763,8 +763,8 @@ public void start() { // set opengl mode if( tryOpenGL3 ) { - logger.config("Using LWJGL OpenGL 3 renderer."); - settings.setRenderer(AppSettings.LWJGL_OPENGL3); + logger.config("Using LWJGL OpenGL 3.2 renderer."); + settings.setRenderer(AppSettings.LWJGL_OPENGL32); } else { logger.config("Using LWJGL OpenGL 2 renderer."); settings.setRenderer(AppSettings.LWJGL_OPENGL2); 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/VRInputAPI.java b/jme3-vr/src/main/java/com/jme3/input/vr/VRInputAPI.java index 6cb9afb41a..0a2443a686 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/VRInputAPI.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/VRInputAPI.java @@ -123,7 +123,7 @@ public interface VRInputAPI { public Object getRawControllerState(int index); /** - * Swap the two hands (exchange the hands controller 1 & 2 indices). + * Swap the two hands (exchange the hands controller 1 and 2 indices). */ public void swapHands(); 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/oculus/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java index 0c12b98d24..a3a506d5c1 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java @@ -33,7 +33,7 @@ *
  • Vectors should have their X and Z axes flipped, but apparently not Y.
  • * * - * @author Campbell Suter + * @author Campbell Suter (znix@znix.xyz) */ public class OculusVR implements VRAPI { diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusViewManager.java b/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusViewManager.java index 0a008af419..1d7baee09c 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusViewManager.java @@ -55,7 +55,7 @@ /** * A rendering system for Oculus's LibOVR API. * - * @author Campbell Suter + * @author Campbell Suter (znix@znix.xyz) */ public class OculusViewManager extends AbstractVRViewManager { 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/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java b/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java index e578e6e768..35b2cd95b6 100644 --- a/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java +++ b/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java @@ -43,7 +43,7 @@ public CartoonSSAO(boolean instancedRendering) { /** * Create a Screen Space Ambient Occlusion Filter. - * @param downsample factor to divide resolution by for filter, >1 increases speed but degrades quality. + * @param downsample factor to divide resolution by for filter, >1 increases speed but degrades quality. * @param instancedRendering true if this filter has to use instance rendering and false (default) otherwise. */ public CartoonSSAO(float downsample, boolean instancedRendering) { diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/CameraVideoStreamFrameHeader_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/CameraVideoStreamFrameHeader_t.java index b24ad98e6a..d8476324dd 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/CameraVideoStreamFrameHeader_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/CameraVideoStreamFrameHeader_t.java @@ -27,7 +27,7 @@ protected List getFieldOrder() { return Arrays.asList("eFrameType", "nWidth", "nHeight", "nBytesPerPixel", "nFrameSequence", "standingTrackedDevicePose"); } /** - * @param eFrameType @see EVRTrackedCameraFrameType
    + * @param eFrameType @see JOpenVRLibrary.EVRTrackedCameraFrameType
    * C type : EVRTrackedCameraFrameType
    * @param standingTrackedDevicePose C type : TrackedDevicePose_t */ diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/Texture_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/Texture_t.java index 1999af2d19..ad0cc5c139 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/Texture_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/Texture_t.java @@ -32,9 +32,9 @@ protected List getFieldOrder() { /** * @param handle void *
    * C type : void*
    - * @param eType @see ETextureType
    + * @param eType @see JOpenVRLibrary.ETextureType
    * C type : ETextureType
    - * @param eColorSpace @see EColorSpace
    + * @param eColorSpace @see JOpenVRLibrary.EColorSpace
    * C type : EColorSpace */ public Texture_t(int handle, int eType, int eColorSpace) { diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/TrackedDevicePose_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/TrackedDevicePose_t.java index fc0f3fd9f2..81eaf41517 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/TrackedDevicePose_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/TrackedDevicePose_t.java @@ -32,7 +32,7 @@ protected List getFieldOrder() { * @param mDeviceToAbsoluteTracking C type : HmdMatrix34_t
    * @param vVelocity C type : HmdVector3_t
    * @param vAngularVelocity C type : HmdVector3_t
    - * @param eTrackingResult @see ETrackingResult
    + * @param eTrackingResult @see JOpenVRLibrary.ETrackingResult
    * C type : ETrackingResult */ public TrackedDevicePose_t(HmdMatrix34_t mDeviceToAbsoluteTracking, HmdVector3_t vVelocity, HmdVector3_t vAngularVelocity, int eTrackingResult, byte bPoseIsValid, byte bDeviceIsConnected) { diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_DualAnalog_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_DualAnalog_t.java index 8bcb1a3730..a1fb2a523f 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_DualAnalog_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_DualAnalog_t.java @@ -25,7 +25,7 @@ protected List getFieldOrder() { return Arrays.asList("x", "y", "transformedX", "transformedY", "which"); } /** - * @param which @see EDualAnalogWhich
    + * @param which @see JOpenVRLibrary.EDualAnalogWhich
    * C type : EDualAnalogWhich */ public VREvent_DualAnalog_t(float x, float y, float transformedX, float transformedY, int which) { diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_Property_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_Property_t.java index bf8d6e3194..945d766cf9 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_Property_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VREvent_Property_t.java @@ -24,7 +24,7 @@ protected List getFieldOrder() { } /** * @param container C type : PropertyContainerHandle_t
    - * @param prop @see ETrackedDeviceProperty
    + * @param prop @see JOpenVRLibrary.ETrackedDeviceProperty
    * C type : ETrackedDeviceProperty */ public VREvent_Property_t(long container, int prop) { diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionMaskPrimitive_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionMaskPrimitive_t.java index 6996ec78aa..63ec6c5b78 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionMaskPrimitive_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionMaskPrimitive_t.java @@ -23,7 +23,7 @@ protected List getFieldOrder() { return Arrays.asList("m_nPrimitiveType", "m_Primitive"); } /** - * @param m_nPrimitiveType @see EVROverlayIntersectionMaskPrimitiveType
    + * @param m_nPrimitiveType @see JOpenVRLibrary.EVROverlayIntersectionMaskPrimitiveType
    * C type : EVROverlayIntersectionMaskPrimitiveType
    * @param m_Primitive C type : VROverlayIntersectionMaskPrimitive_Data_t */ diff --git a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionParams_t.java b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionParams_t.java index 7b2ff945ce..872e6609fe 100644 --- a/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionParams_t.java +++ b/jme3-vr/src/main/java/com/jme3/system/jopenvr/VROverlayIntersectionParams_t.java @@ -27,7 +27,7 @@ protected List getFieldOrder() { /** * @param vSource C type : HmdVector3_t
    * @param vDirection C type : HmdVector3_t
    - * @param eOrigin @see ETrackingUniverseOrigin
    + * @param eOrigin @see JOpenVRLibrary.ETrackingUniverseOrigin
    * C type : ETrackingUniverseOrigin */ public VROverlayIntersectionParams_t(HmdVector3_t vSource, HmdVector3_t vDirection, int eOrigin) { diff --git a/jme3-vr/src/main/java/com/jme3/system/lwjgl/LwjglContextVR.java b/jme3-vr/src/main/java/com/jme3/system/lwjgl/LwjglContextVR.java index dfe19e906b..1c461e6f36 100644 --- a/jme3-vr/src/main/java/com/jme3/system/lwjgl/LwjglContextVR.java +++ b/jme3-vr/src/main/java/com/jme3/system/lwjgl/LwjglContextVR.java @@ -147,14 +147,14 @@ protected int getNumSamplesToUse() { } protected void initContextFirstTime() { - final GLCapabilities capabilities = createCapabilities(settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)); + final GLCapabilities capabilities = createCapabilities(settings.getRenderer().equals(AppSettings.LWJGL_OPENGL32)); if (!capabilities.OpenGL20) { throw new RendererException("OpenGL 2.0 or higher is required for jMonkeyEngine"); } if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2) - || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) { + || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL32)) { GL gl = new LwjglGL(); GLExt glext = new LwjglGLExt(); GLFbo glfbo; diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ProjectionMatrix.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ProjectionMatrix.java index 16a1dc6b41..7b9d66e3fe 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ProjectionMatrix.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ProjectionMatrix.java @@ -13,7 +13,7 @@ public class OSVR_ProjectionMatrix extends Structure { public double right; public double top; public double bottom; - /** < Cannot name "near" because Visual Studio keyword */ + /** Cannot name "near" because Visual Studio keyword */ public double nearClip; public double farClip; public OSVR_ProjectionMatrix() { @@ -22,7 +22,7 @@ public OSVR_ProjectionMatrix() { protected List getFieldOrder() { return Arrays.asList("left", "right", "top", "bottom", "nearClip", "farClip"); } - /** @param nearClip < Cannot name "near" because Visual Studio keyword */ + /** @param nearClip Cannot name "near" because Visual Studio keyword */ public OSVR_ProjectionMatrix(double left, double right, double top, double bottom, double nearClip, double farClip) { super(); this.left = left; diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_RenderParams.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_RenderParams.java index 742175010c..262b7ddb28 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_RenderParams.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_RenderParams.java @@ -10,12 +10,12 @@ */ public class OSVR_RenderParams extends Structure { /** - * < Room space to insert
    + * Room space to insert
    * C type : OSVR_PoseState* */ public Pointer worldFromRoomAppend; /** - * < Overrides head space
    + * Overrides head space
    * C type : OSVR_PoseState* */ public Pointer roomFromHeadReplace; @@ -28,9 +28,9 @@ protected List getFieldOrder() { return Arrays.asList("worldFromRoomAppend", "roomFromHeadReplace", "nearClipDistanceMeters", "farClipDistanceMeters"); } /** - * @param worldFromRoomAppend < Room space to insert
    + * @param worldFromRoomAppend Room space to insert
    * C type : OSVR_PoseState*
    - * @param roomFromHeadReplace < Overrides head space
    + * @param roomFromHeadReplace Overrides head space
    * C type : OSVR_PoseState* */ public OSVR_RenderParams(Pointer worldFromRoomAppend, Pointer roomFromHeadReplace, double nearClipDistanceMeters, double farClipDistanceMeters) { diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ViewportDescription.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ViewportDescription.java index e3528fad01..11f05bda7c 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ViewportDescription.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanager/OSVR_ViewportDescription.java @@ -9,13 +9,13 @@ * For help, please visit NativeLibs4Java , Rococoa, or JNA. */ public class OSVR_ViewportDescription extends Structure { - /** < Left side of the viewport in pixels */ + /** Left side of the viewport in pixels */ public double left; - /** < First pixel in the viewport at the bottom. */ + /** First pixel in the viewport at the bottom. */ public double lower; - /** < Last pixel in the viewport at the top */ + /** Last pixel in the viewport at the top */ public double width; - /** < Last pixel on the right of the viewport in pixels */ + /** Last pixel on the right of the viewport in pixels */ public double height; public OSVR_ViewportDescription() { super(); @@ -24,10 +24,10 @@ protected List getFieldOrder() { return Arrays.asList("left", "lower", "width", "height"); } /** - * @param left < Left side of the viewport in pixels
    - * @param lower < First pixel in the viewport at the bottom.
    - * @param width < Last pixel in the viewport at the top
    - * @param height < Last pixel on the right of the viewport in pixels + * @param left Left side of the viewport in pixels
    + * @param lower First pixel in the viewport at the bottom.
    + * @param width Last pixel in the viewport at the top
    + * @param height Last pixel on the right of the viewport in pixels */ public OSVR_ViewportDescription(double left, double lower, double width, double height) { super(); diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_OpenResultsOpenGL.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_OpenResultsOpenGL.java index e2bd8e7d6d..e4da1e6da8 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_OpenResultsOpenGL.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_OpenResultsOpenGL.java @@ -24,7 +24,7 @@ protected List getFieldOrder() { return Arrays.asList("status", "library", "buffers"); } /** - * @param status @see OSVR_OpenStatus
    + * @param status @see OsvrRenderManagerOpenGLLibrary.OSVR_OpenStatus
    * C type : OSVR_OpenStatus
    * @param library C type : OSVR_GraphicsLibraryOpenGL
    * @param buffers C type : OSVR_RenderBufferOpenGL diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ProjectionMatrix.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ProjectionMatrix.java index 5aabd88447..21df22bdba 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ProjectionMatrix.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ProjectionMatrix.java @@ -13,7 +13,7 @@ public class OSVR_ProjectionMatrix extends Structure { public double right; public double top; public double bottom; - /** < Cannot name "near" because Visual Studio keyword */ + /** Cannot name "near" because Visual Studio keyword */ public double nearClip; public double farClip; public OSVR_ProjectionMatrix() { @@ -22,7 +22,7 @@ public OSVR_ProjectionMatrix() { protected List getFieldOrder() { return Arrays.asList("left", "right", "top", "bottom", "nearClip", "farClip"); } - /** @param nearClip < Cannot name "near" because Visual Studio keyword */ + /** @param nearClip Cannot name "near" because Visual Studio keyword */ public OSVR_ProjectionMatrix(double left, double right, double top, double bottom, double nearClip, double farClip) { super(); this.left = left; diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_RenderParams.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_RenderParams.java index ef560519d0..2b63b7b3cf 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_RenderParams.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_RenderParams.java @@ -10,12 +10,12 @@ */ public class OSVR_RenderParams extends Structure { /** - * < Room space to insert
    + * Room space to insert
    * C type : OSVR_PoseState* */ public Pointer worldFromRoomAppend; /** - * < Overrides head space
    + * Overrides head space
    * C type : OSVR_PoseState* */ public Pointer roomFromHeadReplace; @@ -28,9 +28,9 @@ protected List getFieldOrder() { return Arrays.asList("worldFromRoomAppend", "roomFromHeadReplace", "nearClipDistanceMeters", "farClipDistanceMeters"); } /** - * @param worldFromRoomAppend < Room space to insert
    + * @param worldFromRoomAppend Room space to insert
    * C type : OSVR_PoseState*
    - * @param roomFromHeadReplace < Overrides head space
    + * @param roomFromHeadReplace Overrides head space
    * C type : OSVR_PoseState* */ public OSVR_RenderParams(Pointer worldFromRoomAppend, Pointer roomFromHeadReplace, double nearClipDistanceMeters, double farClipDistanceMeters) { diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ViewportDescription.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ViewportDescription.java index d9ff621bef..0df38e45f5 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ViewportDescription.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrrendermanageropengl/OSVR_ViewportDescription.java @@ -9,13 +9,13 @@ * For help, please visit NativeLibs4Java , Rococoa, or JNA. */ public class OSVR_ViewportDescription extends Structure { - /** < Left side of the viewport in pixels */ + /** Left side of the viewport in pixels */ public double left; - /** < First pixel in the viewport at the bottom. */ + /** First pixel in the viewport at the bottom. */ public double lower; - /** < Last pixel in the viewport at the top */ + /** Last pixel in the viewport at the top */ public double width; - /** < Last pixel on the right of the viewport in pixels */ + /** Last pixel on the right of the viewport in pixels */ public double height; public OSVR_ViewportDescription() { super(); @@ -24,10 +24,10 @@ protected List getFieldOrder() { return Arrays.asList("left", "lower", "width", "height"); } /** - * @param left < Left side of the viewport in pixels
    - * @param lower < First pixel in the viewport at the bottom.
    - * @param width < Last pixel in the viewport at the top
    - * @param height < Last pixel on the right of the viewport in pixels + * @param left Left side of the viewport in pixels
    + * @param lower First pixel in the viewport at the bottom.
    + * @param width Last pixel in the viewport at the top
    + * @param height Last pixel on the right of the viewport in pixels */ public OSVR_ViewportDescription(double left, double lower, double width, double height) { super(); diff --git a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrtimevalue/OsvrTimeValueLibrary.java b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrtimevalue/OsvrTimeValueLibrary.java index 36382c7ae5..9af6a39af2 100644 --- a/jme3-vr/src/main/java/com/jme3/system/osvr/osvrtimevalue/OsvrTimeValueLibrary.java +++ b/jme3-vr/src/main/java/com/jme3/system/osvr/osvrtimevalue/OsvrTimeValueLibrary.java @@ -73,7 +73,7 @@ public class OsvrTimeValueLibrary implements Library { /** * Compares two time values (assumed to be normalized), returning * the same values as strcmp
    - * @return <0 if A is earlier than B, 0 if they are the same, and >0 if A
    + * @return <0 if A is earlier than B, 0 if they are the same, and >0 if A
    * is later than B.
    * Original signature : int osvrTimeValueCmp(const OSVR_TimeValue*, const OSVR_TimeValue*) */ @@ -95,17 +95,17 @@ public class OsvrTimeValueLibrary implements Library { public static native byte osvrTimeValueGreater(OSVR_TimeValue tvA, OSVR_TimeValue tvB); /** * Returns true if the time value is normalized. Typically used in assertions.
    - * Original signature : bool osvrTimeValueIsNormalized(const OSVR_TimeValue&) + * Original signature : bool osvrTimeValueIsNormalized(const OSVR_TimeValue&) */ public static native byte osvrTimeValueIsNormalized(OSVR_TimeValue tv); /** - * Operator > overload for time values
    - * Original signature : bool operator>(const OSVR_TimeValue&, const OSVR_TimeValue&) + * Operator > overload for time values
    + * Original signature : bool operator>(const OSVR_TimeValue&, const OSVR_TimeValue&) */ public static native byte operatorGreater(OSVR_TimeValue tvA, OSVR_TimeValue tvB); /** * Operator == overload for time values
    - * Original signature : bool operator==(const OSVR_TimeValue&, const OSVR_TimeValue&) + * Original signature : bool operator==(const OSVR_TimeValue&, const OSVR_TimeValue&) */ public static native byte operatorIsEqual(OSVR_TimeValue tvA, OSVR_TimeValue tvB); public static class timeval extends PointerType { diff --git a/jme3-vr/src/main/java/test/TestInitHmd.java b/jme3-vr/src/main/java/test/TestInitHmd.java index 4869d4c11e..a15ea732d1 100644 --- a/jme3-vr/src/main/java/test/TestInitHmd.java +++ b/jme3-vr/src/main/java/test/TestInitHmd.java @@ -37,7 +37,7 @@ /** * Testing OpenVR environment. - * @author Rickard + * @author Rickard (neph1 @ github) */ public class TestInitHmd { 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 + ". " + \