Compare commits
4 Commits
master
...
shader-nod
Author | SHA1 | Date | |
---|---|---|---|
|
523087a82f | ||
|
b57ecf35ea | ||
|
9f9edee332 | ||
|
7055de4531 |
93
.github/actions/tools/bintray.sh
vendored
93
.github/actions/tools/bintray.sh
vendored
@ -1,93 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# bintray_createPackage [REPO] [PACKAGE] [USER] [PASSWORD] [GIT REPO] [LICENSE]
|
|
||||||
function bintray_createPackage {
|
|
||||||
repo="$1"
|
|
||||||
package="$2"
|
|
||||||
user="$3"
|
|
||||||
password="$4"
|
|
||||||
srcrepo="$5"
|
|
||||||
license="$6"
|
|
||||||
|
|
||||||
repoUrl="https://api.bintray.com/packages/$repo"
|
|
||||||
if [ "`curl -u$user:$password -H Content-Type:application/json -H Accept:application/json \
|
|
||||||
--write-out %{http_code} --silent --output /dev/null -X GET \"$repoUrl/$package\"`" != "200" ];
|
|
||||||
then
|
|
||||||
|
|
||||||
if [ "$srcrepo" != "" -a "$license" != "" ];
|
|
||||||
then
|
|
||||||
echo "Package does not exist... create."
|
|
||||||
data="{
|
|
||||||
\"name\": \"${package}\",
|
|
||||||
\"labels\": [],
|
|
||||||
\"licenses\": [\"${license}\"],
|
|
||||||
\"vcs_url\": \"${srcrepo}\"
|
|
||||||
}"
|
|
||||||
|
|
||||||
|
|
||||||
curl -u$user:$password -H "Content-Type:application/json" -H "Accept:application/json" -X POST \
|
|
||||||
-d "${data}" "$repoUrl"
|
|
||||||
else
|
|
||||||
echo "Package does not exist... you need to specify a repo and license for it to be created."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "The package already exists. Skip."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# uploadFile file destination [REPO] "content" [PACKAGE] [USER] [PASSWORD] [SRCREPO] [LICENSE]
|
|
||||||
function bintray_uploadFile {
|
|
||||||
file="$1"
|
|
||||||
dest="$2"
|
|
||||||
|
|
||||||
echo "Upload $file to $dest"
|
|
||||||
|
|
||||||
repo="$3"
|
|
||||||
type="$4"
|
|
||||||
package="$5"
|
|
||||||
|
|
||||||
user="$6"
|
|
||||||
password="$7"
|
|
||||||
|
|
||||||
srcrepo="$8"
|
|
||||||
license="$9"
|
|
||||||
publish="${10}"
|
|
||||||
|
|
||||||
bintray_createPackage $repo $package $user $password $srcrepo $license
|
|
||||||
|
|
||||||
url="https://api.bintray.com/$type/$repo/$package/$dest"
|
|
||||||
if [ "$publish" = "true" ]; then url="$url;publish=1"; fi
|
|
||||||
|
|
||||||
curl -T "$file" -u$user:$password "$url"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function bintray_uploadAll {
|
|
||||||
path="$1"
|
|
||||||
destpath="$2"
|
|
||||||
repo="$3"
|
|
||||||
type="$4"
|
|
||||||
package="$5"
|
|
||||||
|
|
||||||
user="$6"
|
|
||||||
password="$7"
|
|
||||||
|
|
||||||
srcrepo="$8"
|
|
||||||
license="$9"
|
|
||||||
publish="${10}"
|
|
||||||
|
|
||||||
cdir="$PWD"
|
|
||||||
cd "$path"
|
|
||||||
|
|
||||||
files="`find . -type f -print`"
|
|
||||||
IFS="
|
|
||||||
"
|
|
||||||
set -f
|
|
||||||
for f in $files; do
|
|
||||||
destfile="$destpath/${f:2}"
|
|
||||||
bintray_uploadFile $f $destfile $repo $type $package $user $password $srcrepo $license $publish
|
|
||||||
done
|
|
||||||
set +f
|
|
||||||
unset IFS
|
|
||||||
cd "$cdir"
|
|
||||||
}
|
|
85
.github/actions/tools/uploadToMaven.sh
vendored
85
.github/actions/tools/uploadToMaven.sh
vendored
@ -1,85 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
}
|
|
554
.github/workflows/main.yml
vendored
554
.github/workflows/main.yml
vendored
@ -1,554 +0,0 @@
|
|||||||
######################################################################################
|
|
||||||
# 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@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: Validate the Gradle wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
# Build
|
|
||||||
# Note: since this is crossbuild we use the buildForPlatforms filter to tell
|
|
||||||
# the buildscript wich platforms it should build for.
|
|
||||||
./gradlew -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@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: Validate the Gradle wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./gradlew -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@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Prepare java environment
|
|
||||||
uses: actions/setup-java@v1
|
|
||||||
with:
|
|
||||||
java-version: ${{ matrix.jdk }}
|
|
||||||
architecture: x64
|
|
||||||
- name: Validate the Gradle wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
- 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
|
|
||||||
./gradlew -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@v2
|
|
||||||
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: Validate the Gradle wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
- name: Build Engine
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Build
|
|
||||||
./gradlew -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
|
|
||||||
./gradlew -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/
|
|
||||||
./gradlew -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
|
|
||||||
# The bulletUrl (in gradle.properties) might have changed.
|
|
||||||
if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- gradle.properties)"; 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@v2
|
|
||||||
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 . || true
|
|
||||||
git commit -m "$version" || true
|
|
||||||
|
|
||||||
branch="gh-pages"
|
|
||||||
git push origin "$branch" --force || true
|
|
||||||
|
|
||||||
fi
|
|
23
.gitignore
vendored
23
.gitignore
vendored
@ -2,7 +2,6 @@
|
|||||||
**/.classpath
|
**/.classpath
|
||||||
**/.settings
|
**/.settings
|
||||||
**/.project
|
**/.project
|
||||||
**/.vscode
|
|
||||||
**/out/
|
**/out/
|
||||||
/.gradle/
|
/.gradle/
|
||||||
/.nb-gradle/
|
/.nb-gradle/
|
||||||
@ -19,30 +18,30 @@
|
|||||||
*.jnilib
|
*.jnilib
|
||||||
*.dylib
|
*.dylib
|
||||||
*.iml
|
*.iml
|
||||||
*.class
|
|
||||||
*.jtxt
|
|
||||||
.gradletasknamecache
|
.gradletasknamecache
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/jme3-core/src/main/resources/com/jme3/system/version.properties
|
/jme3-core/src/main/resources/com/jme3/system/version.properties
|
||||||
/jme3-*/build/
|
/jme3-*/build/
|
||||||
/jme3-*/bin/
|
/jme3-bullet-native/bullet.zip
|
||||||
/jme3-bullet-native/bullet3.zip
|
/jme3-bullet-native/bullet3.zip
|
||||||
/jme3-bullet-native/bullet3-*/
|
/jme3-bullet-native/bullet-2.82-r2704/
|
||||||
/jme3-bullet-native/src/native/cpp/com_jme3_bullet_*.h
|
/jme3-bullet-native/bullet3-2.83.7/
|
||||||
/jme3-android-native/openal-soft/
|
/jme3-android-native/openal-soft/
|
||||||
/jme3-android-native/OpenALSoft.zip
|
/jme3-android-native/OpenALSoft.zip
|
||||||
/jme3-android-native/src/native/jme_decode/STBI/
|
/jme3-android-native/src/native/jme_decode/STBI/
|
||||||
/jme3-android-native/src/native/jme_decode/Tremor/
|
/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-android-native/stb_image.h
|
||||||
/jme3-examples/private/
|
!/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-vr/src/main/resources/**/*.dylib
|
!/jme3-vr/src/main/resources/**/*.dylib
|
||||||
!/jme3-vr/src/main/resources/**/*.so
|
!/jme3-vr/src/main/resources/**/*.so
|
||||||
!/jme3-vr/src/main/resources/**/*.so.dbg
|
!/jme3-vr/src/main/resources/**/*.so.dbg
|
||||||
!/jme3-vr/src/main/resources/**/*.dll
|
!/jme3-vr/src/main/resources/**/*.dll
|
||||||
!/jme3-vr/src/main/resources/**/*.pdb
|
!/jme3-vr/src/main/resources/**/*.pdb
|
||||||
/buildMaven.bat
|
/buildMaven.bat
|
||||||
/private
|
|
||||||
.travis.yml
|
|
||||||
appveyor.yml
|
|
||||||
javadoc_deploy
|
|
||||||
javadoc_deploy.pub
|
|
86
.travis.yml
Normal file
86
.travis.yml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
language: java
|
||||||
|
sudo: false
|
||||||
|
dist: precise
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- v3.1
|
||||||
|
- /^v3.2.0-.*$/
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
jdk: oraclejdk8
|
||||||
|
env: UPLOAD=true UPLOAD_NATIVE=true
|
||||||
|
- os: linux
|
||||||
|
jdk: openjdk7
|
||||||
|
- os: osx
|
||||||
|
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
|
||||||
|
|
||||||
|
|
@ -9,8 +9,6 @@ Communication always comes first. **All** code changes and other contributions s
|
|||||||
|
|
||||||
### New Contributors
|
### 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).
|
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.
|
- Do not commit your code until you have received proper feedback.
|
||||||
@ -24,15 +22,6 @@ 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.
|
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
|
## Building the engine
|
||||||
|
|
||||||
1. Install [Gradle](http://www.gradle.org/)
|
1. Install [Gradle](http://www.gradle.org/)
|
||||||
|
29
LICENSE
29
LICENSE
@ -1,29 +0,0 @@
|
|||||||
Copyright (c) 2009-2020 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.
|
|
26
README.md
26
README.md
@ -1,9 +1,9 @@
|
|||||||
jMonkeyEngine
|
jMonkeyEngine
|
||||||
=============
|
=============
|
||||||
|
|
||||||
[](https://github.com/jMonkeyEngine/jmonkeyengine/actions)
|
[](https://travis-ci.org/jMonkeyEngine/jmonkeyengine)
|
||||||
|
|
||||||
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.
|
jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.0 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:
|
The engine is used by several commercial game studios and computer-science courses. Here's a taste:
|
||||||
|
|
||||||
@ -11,18 +11,16 @@ 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)
|
- [jME powered games on IndieDB](http://www.indiedb.com/engines/jmonkeyengine/games)
|
||||||
- [Maker's Tale](http://steamcommunity.com/sharedfiles/filedetails/?id=93461954t)
|
- [Maker's Tale](http://steamcommunity.com/sharedfiles/filedetails/?id=93461954t)
|
||||||
- [Boardtastic 2](https://boardtastic-2.fileplanet.com/apk)
|
- [Boardtastic 2](https://play.google.com/store/apps/details?id=com.boardtastic.skateboarding)
|
||||||
- [Attack of the Gelatinous Blob](https://attack-gelatinous-blob.softwareandgames.com/)
|
- [Copod](http://herebeben.com/copod)
|
||||||
- [Mythruna](http://mythruna.com/)
|
- [Attack of the Gelatinous Blob](http://attackofthegelatinousblob.com/)
|
||||||
- [PirateHell (on Steam)](https://store.steampowered.com/app/321080/Pirate_Hell/)
|
- [Chaos](http://4realms.net/)
|
||||||
- [3089 (on Steam)](http://store.steampowered.com/app/263360/)
|
- [Mythruna](https://mythruna.com/)
|
||||||
- [3079 (on Steam)](http://store.steampowered.com/app/259620/)
|
- [PirateHell](http://www.desura.com/games/piratehell)
|
||||||
- [Lightspeed Frontier (on Steam)](https://store.steampowered.com/app/548650/Lightspeed_Frontier/)
|
- [3089 (on steam)](http://store.steampowered.com/app/263360/)
|
||||||
|
- [3079 (on steam)](http://store.steampowered.com/app/259620/)
|
||||||
|
- [Lightspeed Frontier](http://www.lightspeedfrontier.com/)
|
||||||
- [Skullstone](http://www.skullstonegame.com/)
|
- [Skullstone](http://www.skullstonegame.com/)
|
||||||
- [Spoxel (on Steam)](https://store.steampowered.com/app/746880/Spoxel/)
|
|
||||||
- [Nine Circles of Hell (on Steam)](https://store.steampowered.com/app/1200600/Nine_Circles_of_Hell/)
|
|
||||||
- [Leap](https://gamejolt.com/games/leap/313308)
|
|
||||||
- [Jumping Jack Flag](http://timealias.bplaced.net/jack/)
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
@ -37,7 +35,7 @@ Note: The master branch on GitHub is a development version of the engine and is
|
|||||||
- NetBeans Platform
|
- NetBeans Platform
|
||||||
- Gradle
|
- Gradle
|
||||||
|
|
||||||
Plus a bunch of awesome libraries & tight integrations like Bullet, NiftyGUI and other goodies.
|
Plus a bunch of awesome libraries & tight integrations like Bullet, Blender, NiftyGUI and other goodies.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
|
58
appveyor.yml
Normal file
58
appveyor.yml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
version: 1.0.{build}.{branch}
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
|
only_commits:
|
||||||
|
files:
|
||||||
|
- jme3-bullet-native/
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
test: off
|
||||||
|
deploy: off
|
||||||
|
|
||||||
|
on_success:
|
||||||
|
- cmd: >-
|
||||||
|
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
|
98
build.gradle
98
build.gradle
@ -1,30 +1,16 @@
|
|||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
classpath 'com.android.tools.build:gradle:2.1.0'
|
||||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
|
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.5'
|
||||||
classpath 'me.tatarka:gradle-retrolambda:3.7.1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'base'
|
apply plugin: 'base'
|
||||||
apply from: file('version.gradle')
|
apply from: file('version.gradle')
|
||||||
|
|
||||||
apply plugin: 'me.tatarka.retrolambda'
|
|
||||||
|
|
||||||
// This is applied to all sub projects
|
// This is applied to all sub projects
|
||||||
subprojects {
|
subprojects {
|
||||||
if(!project.name.equals('jme3-android-examples')) {
|
if(!project.name.equals('jme3-android-examples')) {
|
||||||
@ -115,8 +101,8 @@ task mergedJavadoc(type: Javadoc, description: 'Creates Javadoc from all the pro
|
|||||||
source subprojects.collect {project ->
|
source subprojects.collect {project ->
|
||||||
project.sourceSets*.allJava
|
project.sourceSets*.allJava
|
||||||
}
|
}
|
||||||
classpath = files(subprojects.collect {project ->
|
// classpath = files(subprojects.collect {project ->
|
||||||
project.sourceSets*.compileClasspath})
|
// project.sourceSets*.compileClasspath})
|
||||||
// source {
|
// source {
|
||||||
// subprojects*.sourceSets*.main*.allSource
|
// subprojects*.sourceSets*.main*.allSource
|
||||||
// }
|
// }
|
||||||
@ -125,15 +111,14 @@ 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){
|
task mergedSource(type: Copy){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task wrapper(type: Wrapper, description: 'Creates and deploys the Gradle wrapper to the current directory.') {
|
||||||
|
gradleVersion = '4.1'
|
||||||
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
ndkCommandPath = ""
|
ndkCommandPath = ""
|
||||||
ndkExists = false
|
ndkExists = false
|
||||||
@ -159,62 +144,6 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assemble.dependsOn extractPrebuiltNatives
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//class IncrementalReverseTask extends DefaultTask {
|
//class IncrementalReverseTask extends DefaultTask {
|
||||||
// @InputDirectory
|
// @InputDirectory
|
||||||
// def File inputDir
|
// def File inputDir
|
||||||
@ -250,14 +179,3 @@ if (skipPrebuildLibraries != "true" && buildNativeProjects != "true") {
|
|||||||
// enableAssertions = true // true by default
|
// enableAssertions = true // true by default
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
wrapper {
|
|
||||||
gradleVersion = '5.6.4'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
retrolambda {
|
|
||||||
javaVersion JavaVersion.VERSION_1_7
|
|
||||||
incremental true
|
|
||||||
jvmArgs '-noverify'
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
group = 'org.jmonkeyengine'
|
group = 'org.jmonkeyengine'
|
||||||
version = jmeFullVersion
|
version = jmeVersion + '-' + jmeVersionTag
|
||||||
|
|
||||||
sourceCompatibility = '1.6'
|
sourceCompatibility = '1.6'
|
||||||
|
|
||||||
|
@ -3,21 +3,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'groovy'
|
|
||||||
apply plugin: 'maven'
|
apply plugin: 'maven'
|
||||||
|
|
||||||
group = 'org.jmonkeyengine'
|
group = 'org.jmonkeyengine'
|
||||||
version = jmeFullVersion
|
version = jmePomVersion
|
||||||
|
|
||||||
sourceCompatibility = '1.8'
|
sourceCompatibility = '1.7'
|
||||||
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
||||||
|
|
||||||
gradle.projectsEvaluated {
|
|
||||||
tasks.withType(JavaCompile) { // compile-time options:
|
|
||||||
options.compilerArgs << '-Xlint:unchecked'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
maven {
|
||||||
@ -31,22 +24,10 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
// Adding dependencies here will add the dependencies to each subproject.
|
// Adding dependencies here will add the dependencies to each subproject.
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
testCompile group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
|
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.0.28-beta'
|
||||||
testCompile group: 'org.easytesting', name: 'fest-assert-core', version: '2.0M10'
|
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 {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
attributes 'Implementation-Title': 'jMonkeyEngine',
|
attributes 'Implementation-Title': 'jMonkeyEngine',
|
||||||
@ -57,13 +38,11 @@ jar {
|
|||||||
javadoc {
|
javadoc {
|
||||||
failOnError = false
|
failOnError = false
|
||||||
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
|
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
|
||||||
options.docTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc"
|
options.docTitle = "jMonkeyEngine ${jmeMainVersion} ${project.name} Javadoc"
|
||||||
options.windowTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc"
|
options.windowTitle = "jMonkeyEngine ${jmeMainVersion} ${project.name} Javadoc"
|
||||||
options.header = "<b>jMonkeyEngine ${jmeFullVersion} ${project.name}</b>"
|
options.header = "<b>jMonkeyEngine ${jmeMainVersion} ${project.name}</b>"
|
||||||
options.author = "true"
|
options.author = "true"
|
||||||
options.use = "true"
|
options.use = "true"
|
||||||
options.charSet = "UTF-8"
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
//disable doclint for JDK8, more quiet output
|
//disable doclint for JDK8, more quiet output
|
||||||
if (JavaVersion.current().isJava8Compatible()){
|
if (JavaVersion.current().isJava8Compatible()){
|
||||||
options.addStringOption('Xdoclint:none', '-quiet')
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
# Version number: Major.Minor.SubMinor (e.g. 3.3.0)
|
# Version number used for plugins, only 3 numbers (e.g. 3.1.3)
|
||||||
jmeVersion = 3.4.0
|
jmeVersion = 3.3.0
|
||||||
|
# Version used for application and settings folder, no spaces!
|
||||||
# Leave empty to autogenerate
|
jmeMainVersion = 3.3
|
||||||
# (use -PjmeVersionName="myVersion" from commandline to specify a custom version name )
|
# Version addition pre-alpha-svn, Stable, Beta
|
||||||
jmeVersionName =
|
jmeVersionTag = SNAPSHOT
|
||||||
|
# Increment this each time jmeVersionTag changes but jmeVersion stays the same
|
||||||
# If true, the version name will contain the commit hash
|
jmeVersionTagID = 0
|
||||||
useCommitHashAsVersionName = false
|
|
||||||
|
|
||||||
# Set to true if a non-master branch name should be included in the automatically
|
|
||||||
# generated version.
|
|
||||||
includeBranchInVersion = false
|
|
||||||
|
|
||||||
# specify if JavaDoc should be built
|
# specify if JavaDoc should be built
|
||||||
buildJavaDoc = true
|
buildJavaDoc = true
|
||||||
@ -19,22 +14,18 @@ buildJavaDoc = true
|
|||||||
buildNativeProjects = false
|
buildNativeProjects = false
|
||||||
buildAndroidExamples = false
|
buildAndroidExamples = false
|
||||||
|
|
||||||
buildForPlatforms = Linux64,Linux32,Windows64,Windows32,Mac64
|
|
||||||
# Forcefully ignore prebuilt libraries
|
|
||||||
skipPrebuildLibraries=false
|
|
||||||
# Path to android NDK for building native libraries
|
# Path to android NDK for building native libraries
|
||||||
#ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7
|
#ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7
|
||||||
ndkPath = /opt/android-ndk-r16b
|
ndkPath = /opt/android-ndk-r10c
|
||||||
|
|
||||||
# Path for downloading native Bullet
|
# Path for downloading native Bullet
|
||||||
# 2.89+ (circa 26 April 2020, to avoid jMonkeyEngine issue #1283)
|
bulletUrl = https://github.com/bulletphysics/bullet3/archive/2.86.1.zip
|
||||||
bulletUrl = https://github.com/bulletphysics/bullet3/archive/cd8cf7521cbb8b7808126a6adebd47bb83ea166a.zip
|
bulletFolder = bullet3-2.86.1
|
||||||
bulletFolder = bullet3-cd8cf7521cbb8b7808126a6adebd47bb83ea166a
|
|
||||||
bulletZipFile = bullet3.zip
|
bulletZipFile = bullet3.zip
|
||||||
|
|
||||||
# POM settings
|
# POM settings
|
||||||
POM_NAME=jMonkeyEngine
|
POM_NAME=jMonkeyEngine
|
||||||
POM_DESCRIPTION=jMonkeyEngine is a 3-D game engine for adventurous Java developers
|
POM_DESCRIPTION=jMonkeyEngine is a 3D game engine for adventurous Java developers
|
||||||
POM_URL=http://jmonkeyengine.org
|
POM_URL=http://jmonkeyengine.org
|
||||||
POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine
|
POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine
|
||||||
POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git
|
POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git
|
||||||
@ -47,5 +38,3 @@ POM_INCEPTION_YEAR=2009
|
|||||||
# Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline
|
# Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline
|
||||||
bintray_user=
|
bintray_user=
|
||||||
bintray_api_key=
|
bintray_api_key=
|
||||||
|
|
||||||
PREBUILD_NATIVES_URL=https://dl.bintray.com/jmonkeyengine/files/${natives.snapshot}/jme3-natives.zip
|
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,6 @@
|
|||||||
|
#Sun Sep 17 22:55:30 EDT 2017
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip
|
||||||
|
28
gradlew
vendored
28
gradlew
vendored
@ -1,21 +1,5 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright 2015 the original author or authors.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
@ -44,16 +28,16 @@ APP_NAME="Gradle"
|
|||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
|
|
||||||
warn () {
|
warn ( ) {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
die () {
|
die ( ) {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
@ -125,8 +109,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
if $cygwin ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
@ -171,7 +155,7 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Escape application args
|
||||||
save () {
|
save ( ) {
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@ -1,19 +1,3 @@
|
|||||||
@rem
|
|
||||||
@rem Copyright 2015 the original author or authors.
|
|
||||||
@rem
|
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
@rem you may not use this file except in compliance with the License.
|
|
||||||
@rem You may obtain a copy of the License at
|
|
||||||
@rem
|
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
@rem
|
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
@rem See the License for the specific language governing permissions and
|
|
||||||
@rem limitations under the License.
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@ -30,7 +14,7 @@ set APP_BASE_NAME=%~n0
|
|||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 23
|
||||||
buildToolsVersion "28.0.3"
|
buildToolsVersion "23.0.3"
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
// Fix nifty gui referencing "java.awt" package.
|
// Fix nifty gui referencing "java.awt" package.
|
||||||
@ -13,7 +13,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.jmonkeyengine.jme3androidexamples"
|
applicationId "org.jmonkeyengine.jme3androidexamples"
|
||||||
minSdkVersion 15 // Android 4.0.3 ICE CREAM SANDWICH
|
minSdkVersion 15 // Android 4.0.3 ICE CREAM SANDWICH
|
||||||
targetSdkVersion 28 // Android 9 PIE
|
targetSdkVersion 22 // Android 5.1 LOLLIPOP
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0" // TODO: from settings.gradle
|
versionName "1.0" // TODO: from settings.gradle
|
||||||
}
|
}
|
||||||
@ -25,11 +25,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
@ -47,7 +42,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
compile 'com.android.support:appcompat-v7:28.0.0'
|
compile 'com.android.support:appcompat-v7:23.3.0'
|
||||||
|
|
||||||
compile project(':jme3-core')
|
compile project(':jme3-core')
|
||||||
compile project(':jme3-android')
|
compile project(':jme3-android')
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
package jme3test.android;
|
package jme3test.android;
|
||||||
|
|
||||||
import com.jme3.app.SimpleApplication;
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.light.AmbientLight;
|
||||||
|
import com.jme3.light.PointLight;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.scene.shape.Sphere;
|
||||||
|
import com.jme3.util.TangentBinormalGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test case to look for images stored in the Android drawable and mipmap directories. Image files are
|
* Test case to look for images stored in the Android drawable and mipmap directories. Image files are
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package org.jmonkeyengine.jme3androidexamples;
|
package org.jmonkeyengine.jme3androidexamples;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import com.jme3.app.AndroidHarnessFragment;
|
import com.jme3.app.AndroidHarnessFragment;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.LogManager;
|
import java.util.logging.LogManager;
|
||||||
|
|
||||||
|
import static org.jmonkeyengine.jme3androidexamples.MainActivity.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A placeholder fragment containing a jME GLSurfaceView.
|
* A placeholder fragment containing a the jME GLSurfaceView.
|
||||||
*/
|
*/
|
||||||
public class JmeFragment extends AndroidHarnessFragment {
|
public class JmeFragment extends AndroidHarnessFragment {
|
||||||
|
|
||||||
@ -62,15 +66,15 @@ public class JmeFragment extends AndroidHarnessFragment {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
Bundle bundle=getArguments();
|
Bundle bundle=getArguments();
|
||||||
|
|
||||||
appClass = bundle.getString(MainActivity.SELECTED_APP_CLASS);
|
appClass = bundle.getString(SELECTED_APP_CLASS);
|
||||||
// Log.d(this.getClass().getSimpleName(), "AppClass: " + appClass);
|
// Log.d(this.getClass().getSimpleName(), "AppClass: " + appClass);
|
||||||
joystickEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_JOYSTICK_EVENTS);
|
joystickEventsEnabled = bundle.getBoolean(ENABLE_JOYSTICK_EVENTS);
|
||||||
// Log.d(this.getClass().getSimpleName(), "JoystickEventsEnabled: " + joystickEventsEnabled);
|
// Log.d(this.getClass().getSimpleName(), "JoystickEventsEnabled: " + joystickEventsEnabled);
|
||||||
keyEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_KEY_EVENTS);
|
keyEventsEnabled = bundle.getBoolean(ENABLE_KEY_EVENTS);
|
||||||
// Log.d(this.getClass().getSimpleName(), "KeyEventsEnabled: " + keyEventsEnabled);
|
// Log.d(this.getClass().getSimpleName(), "KeyEventsEnabled: " + keyEventsEnabled);
|
||||||
mouseEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_MOUSE_EVENTS);
|
mouseEventsEnabled = bundle.getBoolean(ENABLE_MOUSE_EVENTS);
|
||||||
// Log.d(this.getClass().getSimpleName(), "MouseEventsEnabled: " + mouseEventsEnabled);
|
// Log.d(this.getClass().getSimpleName(), "MouseEventsEnabled: " + mouseEventsEnabled);
|
||||||
boolean verboseLogging = bundle.getBoolean(MainActivity.VERBOSE_LOGGING);
|
boolean verboseLogging = bundle.getBoolean(VERBOSE_LOGGING);
|
||||||
// Log.d(this.getClass().getSimpleName(), "VerboseLogging: " + verboseLogging);
|
// Log.d(this.getClass().getSimpleName(), "VerboseLogging: " + verboseLogging);
|
||||||
if (verboseLogging) {
|
if (verboseLogging) {
|
||||||
// Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info)
|
// Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.jmonkeyengine.jme3androidexamples;
|
package org.jmonkeyengine.jme3androidexamples;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -286,7 +287,7 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
|
|||||||
private boolean checkClassType(String className) {
|
private boolean checkClassType(String className) {
|
||||||
boolean include = true;
|
boolean include = true;
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = Class.forName(className);
|
Class<?> clazz = (Class<?>) Class.forName(className);
|
||||||
if (Application.class.isAssignableFrom(clazz)) {
|
if (Application.class.isAssignableFrom(clazz)) {
|
||||||
Log.d(TAG, "Class " + className + " is a jME Application");
|
Log.d(TAG, "Class " + className + " is a jME Application");
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package org.jmonkeyengine.jme3androidexamples;
|
package org.jmonkeyengine.jme3androidexamples;
|
||||||
|
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
2
jme3-android-native/.gitignore
vendored
2
jme3-android-native/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
# The headers are autogenerated and nobody should try to commit them...
|
|
||||||
src/native/headers
|
|
@ -1,19 +1,16 @@
|
|||||||
String tremorZipFile = "TremorAndroid.zip"
|
String tremorZipFile = "TremorAndroid.zip"
|
||||||
String stbiUrl = 'https://raw.githubusercontent.com/jMonkeyEngine/stb/0224a44a10564a214595797b4c88323f79a5f934/stb_image.h'
|
String stbiUrl = 'https://raw.githubusercontent.com/nothings/stb/master/stb_image.h'
|
||||||
|
|
||||||
// Working directories for the ndk build.
|
// Working directories for the ndk build.
|
||||||
String decodeBuildDir = "${buildDir}" + File.separator + 'decode'
|
String decodeBuildDir = "${buildDir}" + File.separator + 'decode'
|
||||||
String decodeClassesBuildDir = "${buildDir}" + File.separator + 'decode_classes'
|
|
||||||
String decodeBuildJniDir = decodeBuildDir + File.separator + 'jni'
|
String decodeBuildJniDir = decodeBuildDir + File.separator + 'jni'
|
||||||
String decodeBuildLibsDir = decodeBuildDir + File.separator + 'libs'
|
String decodeBuildLibsDir = decodeBuildDir + File.separator + 'libs'
|
||||||
|
|
||||||
// Pre-compiled libs directory
|
// Pre-compiled libs directory
|
||||||
def rootPath = rootProject.projectDir.absolutePath
|
String decodePreCompiledLibsDir = 'libs' + File.separator + 'decode'
|
||||||
String decodePreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator + 'android' + File.separator + 'decode'
|
|
||||||
|
|
||||||
// jME Android Native source files path
|
// jME Android Native source files path
|
||||||
String decodeSourceDir = 'src/native/jme_decode'
|
String decodeSourceDir = 'src/native/jme_decode'
|
||||||
String jmeHeaders = 'src/native/headers'
|
|
||||||
|
|
||||||
task downloadStbImage(type: MyDownload) {
|
task downloadStbImage(type: MyDownload) {
|
||||||
sourceUrl = stbiUrl
|
sourceUrl = stbiUrl
|
||||||
@ -47,13 +44,17 @@ task copyTremorFiles(type: Copy) {
|
|||||||
into outputDir
|
into outputDir
|
||||||
}
|
}
|
||||||
|
|
||||||
task copyJmeHeadersDecode(type: Copy) {
|
// Generate headers via javah
|
||||||
from file(jmeHeaders)
|
task generateJavahHeaders(type: Exec) {
|
||||||
into file(decodeBuildJniDir + File.separator + "headers")
|
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
|
||||||
|
args '-d', decodeSourceDir
|
||||||
|
args '-classpath', project.projectClassPath
|
||||||
|
args "com.jme3.audio.plugins.NativeVorbisFile"
|
||||||
|
args "com.jme3.texture.plugins.AndroidNativeImageLoader"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy jME Android native files to jni directory
|
// Copy jME Android native files to jni directory
|
||||||
task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, copyJmeHeadersDecode]) {
|
task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, generateJavahHeaders]) {
|
||||||
def sourceDir = file(decodeSourceDir)
|
def sourceDir = file(decodeSourceDir)
|
||||||
def outputDir = file(decodeBuildJniDir)
|
def outputDir = file(decodeBuildJniDir)
|
||||||
|
|
||||||
|
BIN
jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/decode/armeabi/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/armeabi/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/decode/mips/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/mips/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/decode/mips64/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/mips64/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/decode/x86/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/x86/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/decode/x86_64/libdecodejme.so
Executable file
BIN
jme3-android-native/libs/decode/x86_64/libdecodejme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so
Executable file
Binary file not shown.
BIN
jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so
Executable file
BIN
jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so
Executable file
Binary file not shown.
@ -1,25 +1,22 @@
|
|||||||
// OpenAL Soft r1.16
|
// OpenAL Soft r1.16
|
||||||
String openALSoftUrl = 'https://github.com/jMonkeyEngine/openal-soft/archive/e5016f814a265ed592a88acea95cf912c4bfdf12.zip'
|
String openALSoftUrl = 'http://repo.or.cz/w/openal-soft.git/snapshot/e5016f814a265ed592a88acea95cf912c4bfdf12.zip'
|
||||||
String openALSoftZipFile = 'OpenALSoft.zip'
|
String openALSoftZipFile = 'OpenALSoft.zip'
|
||||||
|
|
||||||
// OpenAL Soft directory the download is extracted into
|
// OpenAL Soft directory the download is extracted into
|
||||||
// Typically, the downloaded OpenAL Soft zip file will extract to a directory
|
// Typically, the downloaded OpenAL Soft zip file will extract to a directory
|
||||||
// called "openal-soft"
|
// called "openal-soft"
|
||||||
String openALSoftFolder = 'openal-soft-e5016f814a265ed592a88acea95cf912c4bfdf12'
|
String openALSoftFolder = 'openal-soft-e5016f8'
|
||||||
|
|
||||||
//Working directories for the ndk build.
|
//Working directories for the ndk build.
|
||||||
String openalsoftBuildDir = "${buildDir}" + File.separator + 'openalsoft'
|
String openalsoftBuildDir = "${buildDir}" + File.separator + 'openalsoft'
|
||||||
String openalsoftClassesBuildDir = "${buildDir}" + File.separator + 'openalsoft_classes'
|
|
||||||
String openalsoftBuildJniDir = openalsoftBuildDir + File.separator + 'jni'
|
String openalsoftBuildJniDir = openalsoftBuildDir + File.separator + 'jni'
|
||||||
String openalsoftBuildLibsDir = openalsoftBuildDir + File.separator + 'libs'
|
String openalsoftBuildLibsDir = openalsoftBuildDir + File.separator + 'libs'
|
||||||
|
|
||||||
//Pre-compiled libs directory
|
//Pre-compiled libs directory
|
||||||
def rootPath = rootProject.projectDir.absolutePath
|
String openalsoftPreCompiledLibsDir = 'libs' + File.separator + 'openalsoft'
|
||||||
String openalsoftPreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator + 'android' + File.separator + 'openalsoft'
|
|
||||||
|
|
||||||
// jME Android Native source files path
|
// jME Android Native source files path
|
||||||
String openalsoftJmeAndroidPath = 'src/native/jme_openalsoft'
|
String openalsoftJmeAndroidPath = 'src/native/jme_openalsoft'
|
||||||
String jmeHeaders = 'src/native/headers'
|
|
||||||
|
|
||||||
// Download external source files if not available
|
// Download external source files if not available
|
||||||
task downloadOpenALSoft(type: MyDownload) {
|
task downloadOpenALSoft(type: MyDownload) {
|
||||||
@ -64,14 +61,8 @@ 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
|
// Copy jME Android native files to jni directory
|
||||||
task copyJmeOpenALSoft(type: Copy, dependsOn: [copyOpenALSoft, copyJmeHeadersOpenAL]) {
|
task copyJmeOpenALSoft(type: Copy, dependsOn:copyOpenALSoft) {
|
||||||
def sourceDir = file(openalsoftJmeAndroidPath)
|
def sourceDir = file(openalsoftJmeAndroidPath)
|
||||||
def outputDir = file(openalsoftBuildJniDir)
|
def outputDir = file(openalsoftBuildJniDir)
|
||||||
// println "copyJmeOpenALSoft sourceDir: " + sourceDir
|
// println "copyJmeOpenALSoft sourceDir: " + sourceDir
|
||||||
@ -81,7 +72,16 @@ task copyJmeOpenALSoft(type: Copy, dependsOn: [copyOpenALSoft, copyJmeHeadersOpe
|
|||||||
into outputDir
|
into outputDir
|
||||||
}
|
}
|
||||||
|
|
||||||
task buildOpenAlSoftNativeLib(type: Exec, dependsOn: copyJmeOpenALSoft) {
|
task generateOpenAlSoftHeaders(type:Exec, dependsOn: copyJmeOpenALSoft) {
|
||||||
|
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
|
||||||
|
args '-d', openalsoftJmeAndroidPath
|
||||||
|
args '-classpath', project.projectClassPath
|
||||||
|
args "com.jme3.audio.android.AndroidAL"
|
||||||
|
args "com.jme3.audio.android.AndroidALC"
|
||||||
|
args "com.jme3.audio.android.AndroidEFX"
|
||||||
|
}
|
||||||
|
|
||||||
|
task buildOpenAlSoftNativeLib(type: Exec, dependsOn: generateOpenAlSoftHeaders) {
|
||||||
// println "openalsoft build dir: " + openalsoftBuildDir
|
// println "openalsoft build dir: " + openalsoftBuildDir
|
||||||
// println "ndkCommandPath: " + project.ndkCommandPath
|
// println "ndkCommandPath: " + project.ndkCommandPath
|
||||||
workingDir openalsoftBuildDir
|
workingDir openalsoftBuildDir
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "Tremor/ivorbisfile.h"
|
#include "Tremor/ivorbisfile.h"
|
||||||
|
|
||||||
#include "../headers/com_jme3_audio_plugins_NativeVorbisFile.h"
|
#include "com_jme3_audio_plugins_NativeVorbisFile.h"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "../headers/com_jme3_texture_plugins_AndroidNativeImageLoader.h"
|
#include "com_jme3_texture_plugins_AndroidNativeImageLoader.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "../headers/com_jme3_audio_android_AndroidAL.h"
|
#include "com_jme3_audio_android_AndroidAL.h"
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
|
@ -0,0 +1,173 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_jme3_audio_android_AndroidAL */
|
||||||
|
|
||||||
|
#ifndef _Included_com_jme3_audio_android_AndroidAL
|
||||||
|
#define _Included_com_jme3_audio_android_AndroidAL
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alGetString
|
||||||
|
* Signature: (I)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidAL_alGetString
|
||||||
|
(JNIEnv *, jobject, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alGenSources
|
||||||
|
* Signature: ()I
|
||||||
|
*/
|
||||||
|
JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGenSources
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alGetError
|
||||||
|
* Signature: ()I
|
||||||
|
*/
|
||||||
|
JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGetError
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alDeleteSources
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alDeleteSources
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alGenBuffers
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alGenBuffers
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alDeleteBuffers
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alDeleteBuffers
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourceStop
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceStop
|
||||||
|
(JNIEnv *, jobject, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourcei
|
||||||
|
* Signature: (III)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcei
|
||||||
|
(JNIEnv *, jobject, jint, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alBufferData
|
||||||
|
* Signature: (IILjava/nio/ByteBuffer;II)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alBufferData
|
||||||
|
(JNIEnv *, jobject, jint, jint, jobject, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourcePlay
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcePlay
|
||||||
|
(JNIEnv *, jobject, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourcePause
|
||||||
|
* Signature: (I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcePause
|
||||||
|
(JNIEnv *, jobject, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourcef
|
||||||
|
* Signature: (IIF)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcef
|
||||||
|
(JNIEnv *, jobject, jint, jint, jfloat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSource3f
|
||||||
|
* Signature: (IIFFF)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSource3f
|
||||||
|
(JNIEnv *, jobject, jint, jint, jfloat, jfloat, jfloat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alGetSourcei
|
||||||
|
* Signature: (II)I
|
||||||
|
*/
|
||||||
|
JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGetSourcei
|
||||||
|
(JNIEnv *, jobject, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourceUnqueueBuffers
|
||||||
|
* Signature: (IILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceUnqueueBuffers
|
||||||
|
(JNIEnv *, jobject, jint, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSourceQueueBuffers
|
||||||
|
* Signature: (IILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceQueueBuffers
|
||||||
|
(JNIEnv *, jobject, jint, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alListener
|
||||||
|
* Signature: (ILjava/nio/FloatBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListener
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alListenerf
|
||||||
|
* Signature: (IF)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListenerf
|
||||||
|
(JNIEnv *, jobject, jint, jfloat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alListener3f
|
||||||
|
* Signature: (IFFF)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListener3f
|
||||||
|
(JNIEnv *, jobject, jint, jfloat, jfloat, jfloat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidAL
|
||||||
|
* Method: alSource3i
|
||||||
|
* Signature: (IIIII)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSource3i
|
||||||
|
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "../headers/com_jme3_audio_android_AndroidALC.h"
|
#include "com_jme3_audio_android_AndroidALC.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_jme3_audio_android_AndroidALC */
|
||||||
|
|
||||||
|
#ifndef _Included_com_jme3_audio_android_AndroidALC
|
||||||
|
#define _Included_com_jme3_audio_android_AndroidALC
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: createALC
|
||||||
|
* Signature: ()V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_createALC
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: destroyALC
|
||||||
|
* Signature: ()V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_destroyALC
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: isCreated
|
||||||
|
* Signature: ()Z
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidALC_isCreated
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: alcGetString
|
||||||
|
* Signature: (I)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidALC_alcGetString
|
||||||
|
(JNIEnv *, jobject, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: alcIsExtensionPresent
|
||||||
|
* Signature: (Ljava/lang/String;)Z
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidALC_alcIsExtensionPresent
|
||||||
|
(JNIEnv *, jobject, jstring);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: alcGetInteger
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcGetInteger
|
||||||
|
(JNIEnv *, jobject, jint, jobject, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: alcDevicePauseSOFT
|
||||||
|
* Signature: ()V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDevicePauseSOFT
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidALC
|
||||||
|
* Method: alcDeviceResumeSOFT
|
||||||
|
* Signature: ()V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDeviceResumeSOFT
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "../headers/com_jme3_audio_android_AndroidEFX.h"
|
#include "com_jme3_audio_android_AndroidEFX.h"
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_jme3_audio_android_AndroidEFX */
|
||||||
|
|
||||||
|
#ifndef _Included_com_jme3_audio_android_AndroidEFX
|
||||||
|
#define _Included_com_jme3_audio_android_AndroidEFX
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alGenAuxiliaryEffectSlots
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alGenEffects
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenEffects
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alEffecti
|
||||||
|
* Signature: (III)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffecti
|
||||||
|
(JNIEnv *, jobject, jint, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alAuxiliaryEffectSloti
|
||||||
|
* Signature: (III)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alAuxiliaryEffectSloti
|
||||||
|
(JNIEnv *, jobject, jint, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alDeleteEffects
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteEffects
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alDeleteAuxiliaryEffectSlots
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteAuxiliaryEffectSlots
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alGenFilters
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenFilters
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alFilteri
|
||||||
|
* Signature: (III)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alFilteri
|
||||||
|
(JNIEnv *, jobject, jint, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alFilterf
|
||||||
|
* Signature: (IIF)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alFilterf
|
||||||
|
(JNIEnv *, jobject, jint, jint, jfloat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alDeleteFilters
|
||||||
|
* Signature: (ILjava/nio/IntBuffer;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteFilters
|
||||||
|
(JNIEnv *, jobject, jint, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_audio_android_AndroidEFX
|
||||||
|
* Method: alEffectf
|
||||||
|
* Signature: (IIF)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffectf
|
||||||
|
(JNIEnv *, jobject, jint, jint, jfloat);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
@ -1,5 +1,3 @@
|
|||||||
apply plugin: 'java'
|
|
||||||
|
|
||||||
if (!hasProperty('mainClass')) {
|
if (!hasProperty('mainClass')) {
|
||||||
ext.mainClass = ''
|
ext.mainClass = ''
|
||||||
}
|
}
|
||||||
@ -9,8 +7,3 @@ dependencies {
|
|||||||
compile project(':jme3-plugins')
|
compile project(':jme3-plugins')
|
||||||
compileOnly 'android:android'
|
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"]
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ package com.jme3.app;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.NinePatchDrawable;
|
import android.graphics.drawable.NinePatchDrawable;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
@ -88,7 +89,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
protected int eglStencilBits = 0;
|
protected int eglStencilBits = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the desired frame rate. If frameRate higher than 0, the application
|
* Set the desired frame rate. If frameRate > 0, the application
|
||||||
* will be capped at the desired frame rate.
|
* will be capped at the desired frame rate.
|
||||||
* (default = -1, no frame rate cap)
|
* (default = -1, no frame rate cap)
|
||||||
*/
|
*/
|
||||||
@ -239,8 +240,9 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
// Create application instance
|
// Create application instance
|
||||||
try {
|
try {
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
Class clazz = Class.forName(appClass);
|
@SuppressWarnings("unchecked")
|
||||||
app = (LegacyApplication)clazz.newInstance();
|
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
|
||||||
|
app = clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.setSettings(settings);
|
app.setSettings(settings);
|
||||||
@ -361,7 +363,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
* @param dialog
|
* @param dialog
|
||||||
* @param whichButton
|
* @param whichButton
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
if (whichButton != -2) {
|
if (whichButton != -2) {
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
@ -473,7 +474,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
handler.setLevel(Level.ALL);
|
handler.setLevel(Level.ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
app.initialize();
|
app.initialize();
|
||||||
if (handleExitHook) {
|
if (handleExitHook) {
|
||||||
@ -489,12 +489,10 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reshape(int width, int height) {
|
public void reshape(int width, int height) {
|
||||||
app.reshape(width, height);
|
app.reshape(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update() {
|
public void update() {
|
||||||
app.update();
|
app.update();
|
||||||
// call to remove the splash screen, if present.
|
// call to remove the splash screen, if present.
|
||||||
@ -506,12 +504,10 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestClose(boolean esc) {
|
public void requestClose(boolean esc) {
|
||||||
app.requestClose(esc);
|
app.requestClose(esc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
app.destroy();
|
app.destroy();
|
||||||
@ -521,7 +517,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void gainFocus() {
|
public void gainFocus() {
|
||||||
logger.fine("gainFocus");
|
logger.fine("gainFocus");
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
@ -553,7 +548,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loseFocus() {
|
public void loseFocus() {
|
||||||
logger.fine("loseFocus");
|
logger.fine("loseFocus");
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -116,7 +116,7 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
protected int eglStencilBits = 0;
|
protected int eglStencilBits = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the desired frame rate. If frameRate higher than 0, the application
|
* Set the desired frame rate. If frameRate > 0, the application
|
||||||
* will be capped at the desired frame rate.
|
* will be capped at the desired frame rate.
|
||||||
* (default = -1, no frame rate cap)
|
* (default = -1, no frame rate cap)
|
||||||
*/
|
*/
|
||||||
@ -125,11 +125,11 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
/**
|
/**
|
||||||
* Set the maximum resolution for the surfaceview in either the
|
* Set the maximum resolution for the surfaceview in either the
|
||||||
* width or height screen direction depending on the screen size.
|
* width or height screen direction depending on the screen size.
|
||||||
* If the surfaceview is rectangular, the longest side (width or height)
|
* If the surfaceview is retangular, the longest side (width or height)
|
||||||
* will have the resolution set to a maximum of maxResolutionDimension.
|
* will have the resolution set to a maximum of maxResolutionDimension.
|
||||||
* The other direction will be set to a value that maintains the aspect
|
* The other direction will be set to a value that maintains the aspect
|
||||||
* ratio of the surfaceview. </br>
|
* ratio of the surfaceview. </br>
|
||||||
* Any value less than 0 (default = -1) will result in the surfaceview having the
|
* Any value < 0 (default = -1) will result in the surfaceview having the
|
||||||
* same resolution as the view layout (ie. no max resolution).
|
* same resolution as the view layout (ie. no max resolution).
|
||||||
*/
|
*/
|
||||||
protected int maxResolutionDimension = -1;
|
protected int maxResolutionDimension = -1;
|
||||||
@ -257,8 +257,9 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
// Create application instance
|
// Create application instance
|
||||||
try {
|
try {
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
Class clazz = Class.forName(appClass);
|
@SuppressWarnings("unchecked")
|
||||||
app = (LegacyApplication)clazz.newInstance();
|
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
|
||||||
|
app = clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.setSettings(settings);
|
app.setSettings(settings);
|
||||||
@ -275,17 +276,17 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the system to create the View hierarchy associated with this
|
* Called by the system to create the View hierchy associated with this
|
||||||
* Fragment. For jME, this is a FrameLayout that contains the GLSurfaceView
|
* Fragment. For jME, this is a FrameLayout that contains the GLSurfaceView
|
||||||
* and an overlaying SplashScreen Image (if used). The View that is returned
|
* and an overlaying SplashScreen Image (if used). The View that is returned
|
||||||
* will be placed on the screen within the boundaries of the View borders defined
|
* will be placed on the screen within the boundries of the View borders defined
|
||||||
* by the Activity's layout parameters for this Fragment. For jME, we also
|
* by the Activity's layout parameters for this Fragment. For jME, we also
|
||||||
* update the application reference to the new view.
|
* update the application reference to the new view.
|
||||||
*
|
*
|
||||||
* @param inflater
|
* @param inflater
|
||||||
* @param container
|
* @param container
|
||||||
* @param savedInstanceState
|
* @param savedInstanceState
|
||||||
* @return the new view
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
@ -683,10 +684,10 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
if (viewWidth > viewHeight && viewWidth > maxResolutionDimension) {
|
if (viewWidth > viewHeight && viewWidth > maxResolutionDimension) {
|
||||||
// landscape
|
// landscape
|
||||||
fixedSizeWidth = maxResolutionDimension;
|
fixedSizeWidth = maxResolutionDimension;
|
||||||
fixedSizeHeight = (int)(maxResolutionDimension * (viewHeight / (float)viewWidth));
|
fixedSizeHeight = (int)(maxResolutionDimension * ((float)viewHeight / (float)viewWidth));
|
||||||
} else if (viewHeight > viewWidth && viewHeight > maxResolutionDimension) {
|
} else if (viewHeight > viewWidth && viewHeight > maxResolutionDimension) {
|
||||||
// portrait
|
// portrait
|
||||||
fixedSizeWidth = (int)(maxResolutionDimension * (viewWidth / (float)viewHeight));
|
fixedSizeWidth = (int)(maxResolutionDimension * ((float)viewWidth / (float)viewHeight));
|
||||||
fixedSizeHeight = maxResolutionDimension;
|
fixedSizeHeight = maxResolutionDimension;
|
||||||
} else if (viewWidth == viewHeight && viewWidth > maxResolutionDimension) {
|
} else if (viewWidth == viewHeight && viewWidth > maxResolutionDimension) {
|
||||||
fixedSizeWidth = maxResolutionDimension;
|
fixedSizeWidth = maxResolutionDimension;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014-2020 jMonkeyEngine
|
* Copyright (c) 2014 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -63,7 +63,7 @@ import com.jme3.renderer.queue.RenderQueue;
|
|||||||
*
|
*
|
||||||
* <p>This profiler uses the Android Trace class which is only supported
|
* <p>This profiler uses the Android Trace class which is only supported
|
||||||
* on Android SDK rev 18 and higher (ver 4.3 and higher). If the
|
* on Android SDK rev 18 and higher (ver 4.3 and higher). If the
|
||||||
* device is running a version less than rev 18, the logging will
|
* device is running a version < rev 18, the logging will
|
||||||
* be skipped.</p>
|
* be skipped.</p>
|
||||||
*
|
*
|
||||||
* <p>In the MainActivity class, add the following:</p>
|
* <p>In the MainActivity class, add the following:</p>
|
||||||
@ -82,7 +82,6 @@ import com.jme3.renderer.queue.RenderQueue;
|
|||||||
public class DefaultAndroidProfiler implements AppProfiler {
|
public class DefaultAndroidProfiler implements AppProfiler {
|
||||||
private int androidApiLevel = Build.VERSION.SDK_INT;
|
private int androidApiLevel = Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appStep(AppStep appStep) {
|
public void appStep(AppStep appStep) {
|
||||||
if (androidApiLevel >= 18) {
|
if (androidApiLevel >= 18) {
|
||||||
switch(appStep) {
|
switch(appStep) {
|
||||||
@ -136,12 +135,6 @@ public class DefaultAndroidProfiler implements AppProfiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appSubStep(String... additionalInfo) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void vpStep(VpStep vpStep, ViewPort vp, RenderQueue.Bucket bucket) {
|
public void vpStep(VpStep vpStep, ViewPort vp, RenderQueue.Bucket bucket) {
|
||||||
if (androidApiLevel >= 18) {
|
if (androidApiLevel >= 18) {
|
||||||
switch (vpStep) {
|
switch (vpStep) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -478,7 +478,7 @@ public class MjpegFileWriter {
|
|||||||
baos.write(fcc);
|
baos.write(fcc);
|
||||||
baos.write(intBytes(swapInt(cb)));
|
baos.write(intBytes(swapInt(cb)));
|
||||||
for (int i = 0; i < ind.size(); i++) {
|
for (int i = 0; i < ind.size(); i++) {
|
||||||
AVIIndex in = ind.get(i);
|
AVIIndex in = (AVIIndex) ind.get(i);
|
||||||
baos.write(in.toBytes());
|
baos.write(in.toBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -73,7 +73,6 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
private Application app;
|
private Application app;
|
||||||
private ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() {
|
private ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public Thread newThread(Runnable r) {
|
public Thread newThread(Runnable r) {
|
||||||
Thread th = new Thread(r);
|
Thread th = new Thread(r);
|
||||||
th.setName("jME3 Video Processor");
|
th.setName("jME3 Video Processor");
|
||||||
@ -124,6 +123,7 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
* This constructor allows you to specify the output file of the video as well as the quality
|
* This constructor allows you to specify the output file of the video as well as the quality
|
||||||
* @param file the video file
|
* @param file the video file
|
||||||
* @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
|
* @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
|
||||||
|
* @param framerate the frame rate of the resulting video, the application will be locked to this framerate
|
||||||
*/
|
*/
|
||||||
public VideoRecorderAppState(File file, float quality) {
|
public VideoRecorderAppState(File file, float quality) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
@ -240,7 +240,6 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8);
|
renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8);
|
||||||
executor.submit(new Callable<Void>() {
|
executor.submit(new Callable<Void>() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
if (fastMode) {
|
if (fastMode) {
|
||||||
item.data = item.buffer.array();
|
item.data = item.buffer.array();
|
||||||
@ -262,7 +261,6 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(RenderManager rm, ViewPort viewPort) {
|
public void initialize(RenderManager rm, ViewPort viewPort) {
|
||||||
logger.log(Level.INFO, "initialize in VideoProcessor");
|
logger.log(Level.INFO, "initialize in VideoProcessor");
|
||||||
this.camera = viewPort.getCamera();
|
this.camera = viewPort.getCamera();
|
||||||
@ -278,16 +276,13 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reshape(ViewPort vp, int w, int h) {
|
public void reshape(ViewPort vp, int w, int h) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInitialized() {
|
public boolean isInitialized() {
|
||||||
return this.isInitilized;
|
return this.isInitilized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void preFrame(float tpf) {
|
public void preFrame(float tpf) {
|
||||||
if (null == writer) {
|
if (null == writer) {
|
||||||
try {
|
try {
|
||||||
@ -298,17 +293,14 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postQueue(RenderQueue rq) {
|
public void postQueue(RenderQueue rq) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postFrame(FrameBuffer out) {
|
public void postFrame(FrameBuffer out) {
|
||||||
numFrames++;
|
numFrames++;
|
||||||
addImage(renderManager.getRenderer(), out);
|
addImage(renderManager.getRenderer(), out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
logger.log(Level.INFO, "cleanup in VideoProcessor");
|
logger.log(Level.INFO, "cleanup in VideoProcessor");
|
||||||
logger.log(Level.INFO, "VideoProcessor numFrames: {0}", numFrames);
|
logger.log(Level.INFO, "VideoProcessor numFrames: {0}", numFrames);
|
||||||
@ -341,27 +333,22 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
this.ticks = 0;
|
this.ticks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getTime() {
|
public long getTime() {
|
||||||
return (long) (this.ticks * (1.0f / this.framerate) * 1000f);
|
return (long) (this.ticks * (1.0f / this.framerate) * 1000f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getResolution() {
|
public long getResolution() {
|
||||||
return 1000L;
|
return 1000L;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getFrameRate() {
|
public float getFrameRate() {
|
||||||
return this.framerate;
|
return this.framerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getTimePerFrame() {
|
public float getTimePerFrame() {
|
||||||
return 1.0f / this.framerate;
|
return (float) (1.0f / this.framerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update() {
|
public void update() {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
long difference = time - lastTime;
|
long difference = time - lastTime;
|
||||||
@ -378,7 +365,6 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
this.ticks++;
|
this.ticks++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
this.ticks = 0;
|
this.ticks = 0;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import com.jme3.asset.*;
|
|||||||
import com.jme3.system.android.JmeAndroidSystem;
|
import com.jme3.system.android.JmeAndroidSystem;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class AndroidLocator implements AssetLocator {
|
public class AndroidLocator implements AssetLocator {
|
||||||
@ -17,7 +18,6 @@ public class AndroidLocator implements AssetLocator {
|
|||||||
public AndroidLocator() {
|
public AndroidLocator() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setRootPath(String rootPath) {
|
public void setRootPath(String rootPath) {
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
}
|
}
|
||||||
|
@ -10,64 +10,44 @@ public final class AndroidAL implements AL {
|
|||||||
public AndroidAL() {
|
public AndroidAL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public native String alGetString(int parameter);
|
public native String alGetString(int parameter);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native int alGenSources();
|
public native int alGenSources();
|
||||||
|
|
||||||
@Override
|
|
||||||
public native int alGetError();
|
public native int alGetError();
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alDeleteSources(int numSources, IntBuffer sources);
|
public native void alDeleteSources(int numSources, IntBuffer sources);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alGenBuffers(int numBuffers, IntBuffer buffers);
|
public native void alGenBuffers(int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alDeleteBuffers(int numBuffers, IntBuffer buffers);
|
public native void alDeleteBuffers(int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourceStop(int source);
|
public native void alSourceStop(int source);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourcei(int source, int param, int value);
|
public native void alSourcei(int source, int param, int value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);
|
public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourcePlay(int source);
|
public native void alSourcePlay(int source);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourcePause(int source);
|
public native void alSourcePause(int source);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourcef(int source, int param, float value);
|
public native void alSourcef(int source, int param, float value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSource3f(int source, int param, float value1, float value2, float value3);
|
public native void alSource3f(int source, int param, float value1, float value2, float value3);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native int alGetSourcei(int source, int param);
|
public native int alGetSourcei(int source, int param);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);
|
public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);
|
public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alListener(int param, FloatBuffer data);
|
public native void alListener(int param, FloatBuffer data);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alListenerf(int param, float value);
|
public native void alListenerf(int param, float value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alListener3f(int param, float value1, float value2, float value3);
|
public native void alListener3f(int param, float value1, float value2, float value3);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alSource3i(int source, int param, int value1, int value2, int value3);
|
public native void alSource3i(int source, int param, int value1, int value2, int value3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,27 +12,19 @@ public final class AndroidALC implements ALC {
|
|||||||
public AndroidALC() {
|
public AndroidALC() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void createALC();
|
public native void createALC();
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void destroyALC();
|
public native void destroyALC();
|
||||||
|
|
||||||
@Override
|
|
||||||
public native boolean isCreated();
|
public native boolean isCreated();
|
||||||
|
|
||||||
@Override
|
|
||||||
public native String alcGetString(int parameter);
|
public native String alcGetString(int parameter);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native boolean alcIsExtensionPresent(String extension);
|
public native boolean alcIsExtensionPresent(String extension);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alcGetInteger(int param, IntBuffer buffer, int size);
|
public native void alcGetInteger(int param, IntBuffer buffer, int size);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alcDevicePauseSOFT();
|
public native void alcDevicePauseSOFT();
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alcDeviceResumeSOFT();
|
public native void alcDeviceResumeSOFT();
|
||||||
}
|
}
|
||||||
|
@ -8,36 +8,25 @@ public class AndroidEFX implements EFX {
|
|||||||
public AndroidEFX() {
|
public AndroidEFX() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers);
|
public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alGenEffects(int numEffects, IntBuffer buffers);
|
public native void alGenEffects(int numEffects, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alEffecti(int effect, int param, int value);
|
public native void alEffecti(int effect, int param, int value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value);
|
public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alDeleteEffects(int numEffects, IntBuffer buffers);
|
public native void alDeleteEffects(int numEffects, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers);
|
public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alGenFilters(int numFilters, IntBuffer buffers);
|
public native void alGenFilters(int numFilters, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alFilteri(int filter, int param, int value);
|
public native void alFilteri(int filter, int param, int value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alFilterf(int filter, int param, float value);
|
public native void alFilterf(int filter, int param, float value);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alDeleteFilters(int numFilters, IntBuffer buffers);
|
public native void alDeleteFilters(int numFilters, IntBuffer buffers);
|
||||||
|
|
||||||
@Override
|
|
||||||
public native void alEffectf(int effect, int param, float value);
|
public native void alEffectf(int effect, int param, float value);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ public class NativeVorbisLoader implements AssetLoader {
|
|||||||
throw new IOException("Not supported for audio streams");
|
throw new IOException("Not supported for audio streams");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTime(float time) {
|
public void setTime(float time) {
|
||||||
try {
|
try {
|
||||||
file.seekTime(time);
|
file.seekTime(time);
|
||||||
|
@ -36,6 +36,7 @@ import android.view.GestureDetector;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import com.jme3.input.event.TouchEvent;
|
import com.jme3.input.event.TouchEvent;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,6 +42,7 @@ import android.view.View;
|
|||||||
import com.jme3.input.JoyInput;
|
import com.jme3.input.JoyInput;
|
||||||
import com.jme3.input.TouchInput;
|
import com.jme3.input.TouchInput;
|
||||||
import com.jme3.system.AppSettings;
|
import com.jme3.system.AppSettings;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,6 +37,7 @@ import android.view.InputDevice;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,11 +71,10 @@ import java.util.logging.Logger;
|
|||||||
* This is done to allow for battery conservation when sensor data or gamepads
|
* This is done to allow for battery conservation when sensor data or gamepads
|
||||||
* are not required by the application.
|
* are not required by the application.
|
||||||
*
|
*
|
||||||
* {@code
|
|
||||||
* To use the joystick rumble feature, the following line needs to be
|
* To use the joystick rumble feature, the following line needs to be
|
||||||
* added to the Android Manifest File
|
* added to the Android Manifest File
|
||||||
* <uses-permission android:name="android.permission.VIBRATE"/>
|
* <uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
* }
|
*
|
||||||
* @author iwgeric
|
* @author iwgeric
|
||||||
*/
|
*/
|
||||||
public class AndroidJoyInput implements JoyInput {
|
public class AndroidJoyInput implements JoyInput {
|
||||||
|
@ -113,7 +113,7 @@ public class AndroidJoystickJoyInput14 {
|
|||||||
joysticks.clear();
|
joysticks.clear();
|
||||||
joystickIndex.clear();
|
joystickIndex.clear();
|
||||||
|
|
||||||
ArrayList<Integer> gameControllerDeviceIds = new ArrayList<>();
|
ArrayList gameControllerDeviceIds = new ArrayList();
|
||||||
int[] deviceIds = InputDevice.getDeviceIds();
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
for (int deviceId : deviceIds) {
|
for (int deviceId : deviceIds) {
|
||||||
InputDevice dev = InputDevice.getDevice(deviceId);
|
InputDevice dev = InputDevice.getDevice(deviceId);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2018 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -561,7 +561,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (sensorData != null) {
|
||||||
if (!sensorData.haveData) {
|
if (!sensorData.haveData) {
|
||||||
sensorData.haveData = true;
|
sensorData.haveData = true;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -131,8 +131,8 @@ public class AndroidTouchInput implements TouchInput {
|
|||||||
|
|
||||||
// view width and height are 0 until the view is displayed on the screen
|
// view width and height are 0 until the view is displayed on the screen
|
||||||
if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) {
|
if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) {
|
||||||
scaleX = settings.getWidth() / (float)androidInput.getView().getWidth();
|
scaleX = (float)settings.getWidth() / (float)androidInput.getView().getWidth();
|
||||||
scaleY = settings.getHeight() / (float)androidInput.getView().getHeight();
|
scaleY = (float)settings.getHeight() / (float)androidInput.getView().getHeight();
|
||||||
}
|
}
|
||||||
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
|
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
|
||||||
new Object[]{scaleX, scaleY});
|
new Object[]{scaleX, scaleY});
|
||||||
|
@ -36,6 +36,7 @@ import android.view.MotionEvent;
|
|||||||
import com.jme3.input.event.TouchEvent;
|
import com.jme3.input.event.TouchEvent;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -87,7 +87,7 @@ public class TouchEventPool {
|
|||||||
TouchEvent evt = null;
|
TouchEvent evt = null;
|
||||||
int curSize = eventPool.size();
|
int curSize = eventPool.size();
|
||||||
while (curSize > 0) {
|
while (curSize > 0) {
|
||||||
evt = eventPool.pop();
|
evt = (TouchEvent)eventPool.pop();
|
||||||
if (evt.isConsumed()) {
|
if (evt.isConsumed()) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -34,7 +34,6 @@ package com.jme3.renderer.android;
|
|||||||
import android.opengl.*;
|
import android.opengl.*;
|
||||||
import com.jme3.renderer.RendererException;
|
import com.jme3.renderer.RendererException;
|
||||||
import com.jme3.renderer.opengl.*;
|
import com.jme3.renderer.opengl.*;
|
||||||
import com.jme3.util.BufferUtils;
|
|
||||||
|
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -42,11 +41,8 @@ import java.nio.FloatBuffer;
|
|||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
public class AndroidGL implements GL, GLExt, GLFbo {
|
||||||
|
|
||||||
IntBuffer tmpBuff = BufferUtils.createIntBuffer(1);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetStats() {
|
public void resetStats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,12 +83,10 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glActiveTexture(int texture) {
|
public void glActiveTexture(int texture) {
|
||||||
GLES20.glActiveTexture(texture);
|
GLES20.glActiveTexture(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glAttachShader(int program, int shader) {
|
public void glAttachShader(int program, int shader) {
|
||||||
GLES20.glAttachShader(program, shader);
|
GLES20.glAttachShader(program, shader);
|
||||||
}
|
}
|
||||||
@ -102,179 +96,144 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
GLES30.glBeginQuery(target, query);
|
GLES30.glBeginQuery(target, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBindBuffer(int target, int buffer) {
|
public void glBindBuffer(int target, int buffer) {
|
||||||
GLES20.glBindBuffer(target, buffer);
|
GLES20.glBindBuffer(target, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBindTexture(int target, int texture) {
|
public void glBindTexture(int target, int texture) {
|
||||||
GLES20.glBindTexture(target, texture);
|
GLES20.glBindTexture(target, texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBlendFunc(int sfactor, int dfactor) {
|
public void glBlendFunc(int sfactor, int dfactor) {
|
||||||
GLES20.glBlendFunc(sfactor, dfactor);
|
GLES20.glBlendFunc(sfactor, dfactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) {
|
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) {
|
||||||
GLES20.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
GLES20.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferData(int target, FloatBuffer data, int usage) {
|
public void glBufferData(int target, FloatBuffer data, int usage) {
|
||||||
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferData(int target, ShortBuffer data, int usage) {
|
public void glBufferData(int target, ShortBuffer data, int usage) {
|
||||||
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferData(int target, ByteBuffer data, int usage) {
|
public void glBufferData(int target, ByteBuffer data, int usage) {
|
||||||
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferData(int target, long data_size, int usage) {
|
public void glBufferData(int target, long data_size, int usage) {
|
||||||
GLES20.glBufferData(target, (int) data_size, null, usage);
|
GLES20.glBufferData(target, (int) data_size, null, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferSubData(int target, long offset, FloatBuffer data) {
|
public void glBufferSubData(int target, long offset, FloatBuffer data) {
|
||||||
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
|
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferSubData(int target, long offset, ShortBuffer data) {
|
public void glBufferSubData(int target, long offset, ShortBuffer data) {
|
||||||
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
|
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferSubData(int target, long offset, ByteBuffer data) {
|
public void glBufferSubData(int target, long offset, ByteBuffer data) {
|
||||||
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
|
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGetBufferSubData(int target, long offset, ByteBuffer data) {
|
public void glGetBufferSubData(int target, long offset, ByteBuffer data) {
|
||||||
throw new UnsupportedOperationException("OpenGL ES 2 does not support glGetBufferSubData");
|
throw new UnsupportedOperationException("OpenGL ES 2 does not support glGetBufferSubData");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glClear(int mask) {
|
public void glClear(int mask) {
|
||||||
GLES20.glClear(mask);
|
GLES20.glClear(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glClearColor(float red, float green, float blue, float alpha) {
|
public void glClearColor(float red, float green, float blue, float alpha) {
|
||||||
GLES20.glClearColor(red, green, blue, alpha);
|
GLES20.glClearColor(red, green, blue, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
||||||
GLES20.glColorMask(red, green, blue, alpha);
|
GLES20.glColorMask(red, green, blue, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glCompileShader(int shader) {
|
public void glCompileShader(int shader) {
|
||||||
GLES20.glCompileShader(shader);
|
GLES20.glCompileShader(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glCompressedTexImage2D(int target, int level, int internalformat, int width, int height, int border, ByteBuffer data) {
|
public void glCompressedTexImage2D(int target, int level, int internalformat, int width, int height, int border, ByteBuffer data) {
|
||||||
GLES20.glCompressedTexImage2D(target, level, internalformat, width, height, 0, getLimitBytes(data), data);
|
GLES20.glCompressedTexImage2D(target, level, internalformat, width, height, 0, getLimitBytes(data), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glCompressedTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, ByteBuffer data) {
|
public void glCompressedTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, ByteBuffer data) {
|
||||||
GLES20.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, getLimitBytes(data), data);
|
GLES20.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, getLimitBytes(data), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int glCreateProgram() {
|
public int glCreateProgram() {
|
||||||
return GLES20.glCreateProgram();
|
return GLES20.glCreateProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int glCreateShader(int shaderType) {
|
public int glCreateShader(int shaderType) {
|
||||||
return GLES20.glCreateShader(shaderType);
|
return GLES20.glCreateShader(shaderType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glCullFace(int mode) {
|
public void glCullFace(int mode) {
|
||||||
GLES20.glCullFace(mode);
|
GLES20.glCullFace(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDeleteBuffers(IntBuffer buffers) {
|
public void glDeleteBuffers(IntBuffer buffers) {
|
||||||
checkLimit(buffers);
|
checkLimit(buffers);
|
||||||
GLES20.glDeleteBuffers(buffers.limit(), buffers);
|
GLES20.glDeleteBuffers(buffers.limit(), buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDeleteProgram(int program) {
|
public void glDeleteProgram(int program) {
|
||||||
GLES20.glDeleteProgram(program);
|
GLES20.glDeleteProgram(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDeleteShader(int shader) {
|
public void glDeleteShader(int shader) {
|
||||||
GLES20.glDeleteShader(shader);
|
GLES20.glDeleteShader(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDeleteTextures(IntBuffer textures) {
|
public void glDeleteTextures(IntBuffer textures) {
|
||||||
checkLimit(textures);
|
checkLimit(textures);
|
||||||
GLES20.glDeleteTextures(textures.limit(), textures);
|
GLES20.glDeleteTextures(textures.limit(), textures);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDepthFunc(int func) {
|
public void glDepthFunc(int func) {
|
||||||
GLES20.glDepthFunc(func);
|
GLES20.glDepthFunc(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDepthMask(boolean flag) {
|
public void glDepthMask(boolean flag) {
|
||||||
GLES20.glDepthMask(flag);
|
GLES20.glDepthMask(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDepthRange(double nearVal, double farVal) {
|
public void glDepthRange(double nearVal, double farVal) {
|
||||||
GLES20.glDepthRangef((float)nearVal, (float)farVal);
|
GLES20.glDepthRangef((float)nearVal, (float)farVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDetachShader(int program, int shader) {
|
public void glDetachShader(int program, int shader) {
|
||||||
GLES20.glDetachShader(program, shader);
|
GLES20.glDetachShader(program, shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDisable(int cap) {
|
public void glDisable(int cap) {
|
||||||
GLES20.glDisable(cap);
|
GLES20.glDisable(cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDisableVertexAttribArray(int index) {
|
public void glDisableVertexAttribArray(int index) {
|
||||||
GLES20.glDisableVertexAttribArray(index);
|
GLES20.glDisableVertexAttribArray(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDrawArrays(int mode, int first, int count) {
|
public void glDrawArrays(int mode, int first, int count) {
|
||||||
GLES20.glDrawArrays(mode, first, count);
|
GLES20.glDrawArrays(mode, first, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
public void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||||
GLES20.glDrawElements(mode, count, type, (int)indices);
|
GLES20.glDrawElements(mode, count, type, (int)indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glEnable(int cap) {
|
public void glEnable(int cap) {
|
||||||
GLES20.glEnable(cap);
|
GLES20.glEnable(cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glEnableVertexAttribArray(int index) {
|
public void glEnableVertexAttribArray(int index) {
|
||||||
GLES20.glEnableVertexAttribArray(index);
|
GLES20.glEnableVertexAttribArray(index);
|
||||||
}
|
}
|
||||||
@ -284,13 +243,11 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
GLES30.glEndQuery(target);
|
GLES30.glEndQuery(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGenBuffers(IntBuffer buffers) {
|
public void glGenBuffers(IntBuffer buffers) {
|
||||||
checkLimit(buffers);
|
checkLimit(buffers);
|
||||||
GLES20.glGenBuffers(buffers.limit(), buffers);
|
GLES20.glGenBuffers(buffers.limit(), buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGenTextures(IntBuffer textures) {
|
public void glGenTextures(IntBuffer textures) {
|
||||||
checkLimit(textures);
|
checkLimit(textures);
|
||||||
GLES20.glGenTextures(textures.limit(), textures);
|
GLES20.glGenTextures(textures.limit(), textures);
|
||||||
@ -301,35 +258,29 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
GLES30.glGenQueries(num, buff);
|
GLES30.glGenQueries(num, buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int glGetAttribLocation(int program, String name) {
|
public int glGetAttribLocation(int program, String name) {
|
||||||
return GLES20.glGetAttribLocation(program, name);
|
return GLES20.glGetAttribLocation(program, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGetBoolean(int pname, ByteBuffer params) {
|
public void glGetBoolean(int pname, ByteBuffer params) {
|
||||||
// GLES20.glGetBoolean(pname, params);
|
// GLES20.glGetBoolean(pname, params);
|
||||||
throw new UnsupportedOperationException("Today is not a good day for this");
|
throw new UnsupportedOperationException("Today is not a good day for this");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int glGetError() {
|
public int glGetError() {
|
||||||
return GLES20.glGetError();
|
return GLES20.glGetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGetInteger(int pname, IntBuffer params) {
|
public void glGetInteger(int pname, IntBuffer params) {
|
||||||
checkLimit(params);
|
checkLimit(params);
|
||||||
GLES20.glGetIntegerv(pname, params);
|
GLES20.glGetIntegerv(pname, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGetProgram(int program, int pname, IntBuffer params) {
|
public void glGetProgram(int program, int pname, IntBuffer params) {
|
||||||
checkLimit(params);
|
checkLimit(params);
|
||||||
GLES20.glGetProgramiv(program, pname, params);
|
GLES20.glGetProgramiv(program, pname, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String glGetProgramInfoLog(int program, int maxLength) {
|
public String glGetProgramInfoLog(int program, int maxLength) {
|
||||||
return GLES20.glGetProgramInfoLog(program);
|
return GLES20.glGetProgramInfoLog(program);
|
||||||
}
|
}
|
||||||
@ -349,63 +300,51 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
return buff.get(0);
|
return buff.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGetShader(int shader, int pname, IntBuffer params) {
|
public void glGetShader(int shader, int pname, IntBuffer params) {
|
||||||
checkLimit(params);
|
checkLimit(params);
|
||||||
GLES20.glGetShaderiv(shader, pname, params);
|
GLES20.glGetShaderiv(shader, pname, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String glGetShaderInfoLog(int shader, int maxLength) {
|
public String glGetShaderInfoLog(int shader, int maxLength) {
|
||||||
return GLES20.glGetShaderInfoLog(shader);
|
return GLES20.glGetShaderInfoLog(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String glGetString(int name) {
|
public String glGetString(int name) {
|
||||||
return GLES20.glGetString(name);
|
return GLES20.glGetString(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int glGetUniformLocation(int program, String name) {
|
public int glGetUniformLocation(int program, String name) {
|
||||||
return GLES20.glGetUniformLocation(program, name);
|
return GLES20.glGetUniformLocation(program, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean glIsEnabled(int cap) {
|
public boolean glIsEnabled(int cap) {
|
||||||
return GLES20.glIsEnabled(cap);
|
return GLES20.glIsEnabled(cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glLineWidth(float width) {
|
public void glLineWidth(float width) {
|
||||||
GLES20.glLineWidth(width);
|
GLES20.glLineWidth(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glLinkProgram(int program) {
|
public void glLinkProgram(int program) {
|
||||||
GLES20.glLinkProgram(program);
|
GLES20.glLinkProgram(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glPixelStorei(int pname, int param) {
|
public void glPixelStorei(int pname, int param) {
|
||||||
GLES20.glPixelStorei(pname, param);
|
GLES20.glPixelStorei(pname, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glPolygonOffset(float factor, float units) {
|
public void glPolygonOffset(float factor, float units) {
|
||||||
GLES20.glPolygonOffset(factor, units);
|
GLES20.glPolygonOffset(factor, units);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) {
|
public void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) {
|
||||||
GLES20.glReadPixels(x, y, width, height, format, type, data);
|
GLES20.glReadPixels(x, y, width, height, format, type, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glScissor(int x, int y, int width, int height) {
|
public void glScissor(int x, int y, int width, int height) {
|
||||||
GLES20.glScissor(x, y, width, height);
|
GLES20.glScissor(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glShaderSource(int shader, String[] string, IntBuffer length) {
|
public void glShaderSource(int shader, String[] string, IntBuffer length) {
|
||||||
if (string.length != 1) {
|
if (string.length != 1) {
|
||||||
throw new UnsupportedOperationException("Today is not a good day");
|
throw new UnsupportedOperationException("Today is not a good day");
|
||||||
@ -413,231 +352,186 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
GLES20.glShaderSource(shader, string[0]);
|
GLES20.glShaderSource(shader, string[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glStencilFuncSeparate(int face, int func, int ref, int mask) {
|
public void glStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||||
GLES20.glStencilFuncSeparate(face, func, ref, mask);
|
GLES20.glStencilFuncSeparate(face, func, ref, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
public void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||||
GLES20.glStencilOpSeparate(face, sfail, dpfail, dppass);
|
GLES20.glStencilOpSeparate(face, sfail, dpfail, dppass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer data) {
|
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, internalFormat, width, height, 0, format, type, data);
|
GLES20.glTexImage2D(target, level, format, width, height, 0, format, type, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glTexParameterf(int target, int pname, float param) {
|
public void glTexParameterf(int target, int pname, float param) {
|
||||||
GLES20.glTexParameterf(target, pname, param);
|
GLES20.glTexParameterf(target, pname, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glTexParameteri(int target, int pname, int param) {
|
public void glTexParameteri(int target, int pname, int param) {
|
||||||
GLES20.glTexParameteri(target, pname, param);
|
GLES20.glTexParameteri(target, pname, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) {
|
public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) {
|
||||||
GLES20.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data);
|
GLES20.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform1(int location, FloatBuffer value) {
|
public void glUniform1(int location, FloatBuffer value) {
|
||||||
GLES20.glUniform1fv(location, getLimitCount(value, 1), value);
|
GLES20.glUniform1fv(location, getLimitCount(value, 1), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform1(int location, IntBuffer value) {
|
public void glUniform1(int location, IntBuffer value) {
|
||||||
GLES20.glUniform1iv(location, getLimitCount(value, 1), value);
|
GLES20.glUniform1iv(location, getLimitCount(value, 1), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform1f(int location, float v0) {
|
public void glUniform1f(int location, float v0) {
|
||||||
GLES20.glUniform1f(location, v0);
|
GLES20.glUniform1f(location, v0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform1i(int location, int v0) {
|
public void glUniform1i(int location, int v0) {
|
||||||
GLES20.glUniform1i(location, v0);
|
GLES20.glUniform1i(location, v0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform2(int location, IntBuffer value) {
|
public void glUniform2(int location, IntBuffer value) {
|
||||||
GLES20.glUniform2iv(location, getLimitCount(value, 2), value);
|
GLES20.glUniform2iv(location, getLimitCount(value, 2), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform2(int location, FloatBuffer value) {
|
public void glUniform2(int location, FloatBuffer value) {
|
||||||
GLES20.glUniform2fv(location, getLimitCount(value, 2), value);
|
GLES20.glUniform2fv(location, getLimitCount(value, 2), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform2f(int location, float v0, float v1) {
|
public void glUniform2f(int location, float v0, float v1) {
|
||||||
GLES20.glUniform2f(location, v0, v1);
|
GLES20.glUniform2f(location, v0, v1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform3(int location, IntBuffer value) {
|
public void glUniform3(int location, IntBuffer value) {
|
||||||
GLES20.glUniform3iv(location, getLimitCount(value, 3), value);
|
GLES20.glUniform3iv(location, getLimitCount(value, 3), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform3(int location, FloatBuffer value) {
|
public void glUniform3(int location, FloatBuffer value) {
|
||||||
GLES20.glUniform3fv(location, getLimitCount(value, 3), value);
|
GLES20.glUniform3fv(location, getLimitCount(value, 3), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform3f(int location, float v0, float v1, float v2) {
|
public void glUniform3f(int location, float v0, float v1, float v2) {
|
||||||
GLES20.glUniform3f(location, v0, v1, v2);
|
GLES20.glUniform3f(location, v0, v1, v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform4(int location, FloatBuffer value) {
|
public void glUniform4(int location, FloatBuffer value) {
|
||||||
GLES20.glUniform4fv(location, getLimitCount(value, 4), value);
|
GLES20.glUniform4fv(location, getLimitCount(value, 4), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform4(int location, IntBuffer value) {
|
public void glUniform4(int location, IntBuffer value) {
|
||||||
GLES20.glUniform4iv(location, getLimitCount(value, 4), value);
|
GLES20.glUniform4iv(location, getLimitCount(value, 4), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniform4f(int location, float v0, float v1, float v2, float v3) {
|
public void glUniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||||
GLES20.glUniform4f(location, v0, v1, v2, v3);
|
GLES20.glUniform4f(location, v0, v1, v2, v3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
public void glUniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
||||||
GLES20.glUniformMatrix3fv(location, getLimitCount(value, 3 * 3), transpose, value);
|
GLES20.glUniformMatrix3fv(location, getLimitCount(value, 3 * 3), transpose, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUniformMatrix4(int location, boolean transpose, FloatBuffer value) {
|
public void glUniformMatrix4(int location, boolean transpose, FloatBuffer value) {
|
||||||
GLES20.glUniformMatrix4fv(location, getLimitCount(value, 4 * 4), transpose, value);
|
GLES20.glUniformMatrix4fv(location, getLimitCount(value, 4 * 4), transpose, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glUseProgram(int program) {
|
public void glUseProgram(int program) {
|
||||||
GLES20.glUseProgram(program);
|
GLES20.glUseProgram(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
|
public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
|
||||||
GLES20.glVertexAttribPointer(index, size, type, normalized, stride, (int)pointer);
|
GLES20.glVertexAttribPointer(index, size, type, normalized, stride, (int)pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glViewport(int x, int y, int width, int height) {
|
public void glViewport(int x, int y, int width, int height) {
|
||||||
GLES20.glViewport(x, y, width, height);
|
GLES20.glViewport(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
||||||
GLES30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
throw new UnsupportedOperationException("FBO blit not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferData(int target, IntBuffer data, int usage) {
|
public void glBufferData(int target, IntBuffer data, int usage) {
|
||||||
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
GLES20.glBufferData(target, getLimitBytes(data), data, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBufferSubData(int target, long offset, IntBuffer data) {
|
public void glBufferSubData(int target, long offset, IntBuffer data) {
|
||||||
GLES20.glBufferSubData(target, (int)offset, getLimitBytes(data), data);
|
GLES20.glBufferSubData(target, (int)offset, getLimitBytes(data), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
|
public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
|
||||||
GLES30.glDrawArraysInstanced(mode, first, count, primcount);
|
throw new UnsupportedOperationException("Instancing not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDrawBuffers(IntBuffer bufs) {
|
public void glDrawBuffers(IntBuffer bufs) {
|
||||||
GLES30.glDrawBuffers(bufs.limit(), bufs);
|
throw new UnsupportedOperationException("MRT not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
|
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
|
||||||
GLES30.glDrawElementsInstanced(mode, indices_count, type, (int)indices_buffer_offset, primcount);
|
throw new UnsupportedOperationException("Instancing not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGetMultisample(int pname, int index, FloatBuffer val) {
|
public void glGetMultisample(int pname, int index, FloatBuffer val) {
|
||||||
GLES31.glGetMultisamplefv(pname, index, val);
|
throw new UnsupportedOperationException("Multisample renderbuffers not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
|
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
|
||||||
GLES30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
|
throw new UnsupportedOperationException("Multisample renderbuffers not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
|
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
|
||||||
GLES31.glTexStorage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
|
throw new UnsupportedOperationException("Multisample textures not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glVertexAttribDivisorARB(int index, int divisor) {
|
public void glVertexAttribDivisorARB(int index, int divisor) {
|
||||||
GLES30.glVertexAttribDivisor(index, divisor);
|
throw new UnsupportedOperationException("Instancing not available on Android");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBindFramebufferEXT(int param1, int param2) {
|
public void glBindFramebufferEXT(int param1, int param2) {
|
||||||
GLES20.glBindFramebuffer(param1, param2);
|
GLES20.glBindFramebuffer(param1, param2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glBindRenderbufferEXT(int param1, int param2) {
|
public void glBindRenderbufferEXT(int param1, int param2) {
|
||||||
GLES20.glBindRenderbuffer(param1, param2);
|
GLES20.glBindRenderbuffer(param1, param2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int glCheckFramebufferStatusEXT(int param1) {
|
public int glCheckFramebufferStatusEXT(int param1) {
|
||||||
return GLES20.glCheckFramebufferStatus(param1);
|
return GLES20.glCheckFramebufferStatus(param1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDeleteFramebuffersEXT(IntBuffer param1) {
|
public void glDeleteFramebuffersEXT(IntBuffer param1) {
|
||||||
checkLimit(param1);
|
checkLimit(param1);
|
||||||
GLES20.glDeleteFramebuffers(param1.limit(), param1);
|
GLES20.glDeleteFramebuffers(param1.limit(), param1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glDeleteRenderbuffersEXT(IntBuffer param1) {
|
public void glDeleteRenderbuffersEXT(IntBuffer param1) {
|
||||||
checkLimit(param1);
|
checkLimit(param1);
|
||||||
GLES20.glDeleteRenderbuffers(param1.limit(), param1);
|
GLES20.glDeleteRenderbuffers(param1.limit(), param1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
|
public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
|
||||||
GLES20.glFramebufferRenderbuffer(param1, param2, param3, param4);
|
GLES20.glFramebufferRenderbuffer(param1, param2, param3, param4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
|
public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
|
||||||
GLES20.glFramebufferTexture2D(param1, param2, param3, param4, param5);
|
GLES20.glFramebufferTexture2D(param1, param2, param3, param4, param5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGenFramebuffersEXT(IntBuffer param1) {
|
public void glGenFramebuffersEXT(IntBuffer param1) {
|
||||||
checkLimit(param1);
|
checkLimit(param1);
|
||||||
GLES20.glGenFramebuffers(param1.limit(), param1);
|
GLES20.glGenFramebuffers(param1.limit(), param1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGenRenderbuffersEXT(IntBuffer param1) {
|
public void glGenRenderbuffersEXT(IntBuffer param1) {
|
||||||
checkLimit(param1);
|
checkLimit(param1);
|
||||||
GLES20.glGenRenderbuffers(param1.limit(), param1);
|
GLES20.glGenRenderbuffers(param1.limit(), param1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glGenerateMipmapEXT(int param1) {
|
public void glGenerateMipmapEXT(int param1) {
|
||||||
GLES20.glGenerateMipmap(param1);
|
GLES20.glGenerateMipmap(param1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
|
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
|
||||||
GLES20.glRenderbufferStorage(param1, param2, param3, param4);
|
GLES20.glRenderbufferStorage(param1, param2, param3, param4);
|
||||||
}
|
}
|
||||||
@ -670,58 +564,6 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void glFramebufferTextureLayerEXT(int target, int attachment, int texture, int level, int layer) {
|
public void glFramebufferTextureLayerEXT(int target, int attachment, int texture, int level, int layer) {
|
||||||
GLES30.glFramebufferTextureLayer(target, attachment, texture, level, layer);
|
throw new UnsupportedOperationException("OpenGL ES 2 does not support texture arrays");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glAlphaFunc(int func, float ref) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glPointSize(float size) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glPolygonMode(int face, int mode) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper to DrawBuffers as there's no DrawBuffer method in GLES
|
|
||||||
@Override
|
|
||||||
public void glDrawBuffer(int mode) {
|
|
||||||
tmpBuff.clear();
|
|
||||||
tmpBuff.put(0, mode);
|
|
||||||
tmpBuff.rewind();
|
|
||||||
glDrawBuffers(tmpBuff);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void glReadBuffer(int mode) {
|
|
||||||
GLES30.glReadBuffer(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2019 jMonkeyEngine
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -38,7 +38,7 @@ import javax.microedition.khronos.egl.EGL10;
|
|||||||
import javax.microedition.khronos.egl.EGL11;
|
import javax.microedition.khronos.egl.EGL11;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class used by the OGLESShaderRenderer and sister
|
* Utility class used by the {@link OGLESShaderRenderer renderer} and sister
|
||||||
* classes.
|
* classes.
|
||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
|
@ -19,7 +19,6 @@ public class AndroidConfigChooser implements EGLConfigChooser {
|
|||||||
private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
|
private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
|
||||||
protected AppSettings settings;
|
protected AppSettings settings;
|
||||||
private final static int EGL_OPENGL_ES2_BIT = 4;
|
private final static int EGL_OPENGL_ES2_BIT = 4;
|
||||||
private final static int EGL_OPENGL_ES3_BIT = 0x40;
|
|
||||||
|
|
||||||
public AndroidConfigChooser(AppSettings settings) {
|
public AndroidConfigChooser(AppSettings settings) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
@ -141,29 +140,12 @@ public class AndroidConfigChooser implements EGLConfigChooser {
|
|||||||
|
|
||||||
int[] num_config = new int[1];
|
int[] num_config = new int[1];
|
||||||
int[] configSpec = new int[]{
|
int[] configSpec = new int[]{
|
||||||
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
|
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||||
EGL10.EGL_NONE};
|
EGL10.EGL_NONE};
|
||||||
boolean gles3=true;
|
|
||||||
|
|
||||||
// Try openGL ES 3
|
if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
|
||||||
try {
|
RendererUtil.checkEGLError(egl);
|
||||||
if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
|
throw new AssertionError();
|
||||||
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];
|
int numConfigs = num_config[0];
|
||||||
|
@ -204,7 +204,6 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
|
|||||||
public void showSoftKeyboard(final boolean show) {
|
public void showSoftKeyboard(final boolean show) {
|
||||||
view.getHandler().post(new Runnable() {
|
view.getHandler().post(new Runnable() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
public void run() {
|
||||||
InputMethodManager manager =
|
InputMethodManager manager =
|
||||||
(InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
(InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2018 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -52,10 +52,13 @@ import com.jme3.input.controls.SoftTextDialogInputListener;
|
|||||||
import com.jme3.input.dummy.DummyKeyInput;
|
import com.jme3.input.dummy.DummyKeyInput;
|
||||||
import com.jme3.input.dummy.DummyMouseInput;
|
import com.jme3.input.dummy.DummyMouseInput;
|
||||||
import com.jme3.renderer.android.AndroidGL;
|
import com.jme3.renderer.android.AndroidGL;
|
||||||
import com.jme3.renderer.opengl.*;
|
import com.jme3.renderer.opengl.GL;
|
||||||
|
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.system.*;
|
||||||
import com.jme3.util.AndroidBufferAllocator;
|
|
||||||
import com.jme3.util.BufferAllocatorFactory;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -78,14 +81,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
protected long minFrameDuration = 0; // No FPS cap
|
protected long minFrameDuration = 0; // No FPS cap
|
||||||
protected long lastUpdateTime = 0;
|
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() {
|
public OGLESContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,13 +99,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
* @return GLSurfaceView The newly created view
|
* @return GLSurfaceView The newly created view
|
||||||
*/
|
*/
|
||||||
public GLSurfaceView createView(Context context) {
|
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.
|
// NOTE: We assume all ICS devices have OpenGL ES 2.0.
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
// below 4.0, check OpenGL ES 2.0 support.
|
// 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) {
|
if (info.reqGlEsVersion < 0x20000) {
|
||||||
throw new UnsupportedOperationException("OpenGL ES 2.0 or better is not supported on this device");
|
throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device");
|
||||||
}
|
}
|
||||||
} else if (Build.VERSION.SDK_INT < 9){
|
} else if (Build.VERSION.SDK_INT < 9){
|
||||||
throw new UnsupportedOperationException("jME3 requires Android 2.3 or later");
|
throw new UnsupportedOperationException("jME3 requires Android 2.3 or later");
|
||||||
@ -131,8 +126,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
|
|
||||||
// setEGLContextClientVersion must be set before calling setRenderer
|
// setEGLContextClientVersion must be set before calling setRenderer
|
||||||
// this means it cannot be set in AndroidConfigChooser (too late)
|
// this means it cannot be set in AndroidConfigChooser (too late)
|
||||||
// use proper openGL ES version
|
view.setEGLContextClientVersion(2);
|
||||||
view.setEGLContextClientVersion(info.reqGlEsVersion>>16);
|
|
||||||
|
|
||||||
view.setFocusableInTouchMode(true);
|
view.setFocusableInTouchMode(true);
|
||||||
view.setFocusable(true);
|
view.setFocusable(true);
|
||||||
@ -196,21 +190,20 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
|
|
||||||
// Setup unhandled Exception Handler
|
// Setup unhandled Exception Handler
|
||||||
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||||
@Override
|
|
||||||
public void uncaughtException(Thread thread, Throwable thrown) {
|
public void uncaughtException(Thread thread, Throwable thrown) {
|
||||||
listener.handleError("Exception thrown in " + thread.toString(), thrown);
|
listener.handleError("Exception thrown in " + thread.toString(), thrown);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
timer = new NanoTimer();
|
timer = new NanoTimer();
|
||||||
GL gl = new AndroidGL();
|
Object gl = new AndroidGL();
|
||||||
if (settings.getBoolean("GraphicsDebug")) {
|
if (settings.getBoolean("GraphicsDebug")) {
|
||||||
gl = (GL) GLDebug.createProxy(gl, gl, GL.class, GL2.class, GLES_30.class, GLFbo.class, GLExt.class);
|
gl = new GLDebugES((GL) gl, (GLExt) gl, (GLFbo) gl);
|
||||||
}
|
}
|
||||||
if (settings.getBoolean("GraphicsTrace")) {
|
if (settings.getBoolean("GraphicsTrace")) {
|
||||||
gl = (GL)GLTracer.createGlesTracer(gl, GL.class, GLES_30.class, GLFbo.class, GLExt.class);
|
gl = GLTracer.createGlesTracer(gl, GL.class, GLFbo.class, GLExt.class);
|
||||||
}
|
}
|
||||||
renderer = new GLRenderer(gl, (GLExt)gl, (GLFbo)gl);
|
renderer = new GLRenderer((GL)gl, (GLExt)gl, (GLFbo)gl);
|
||||||
renderer.initialize();
|
renderer.initialize();
|
||||||
|
|
||||||
JmeSystem.setSoftTextDialogInput(this);
|
JmeSystem.setSoftTextDialogInput(this);
|
||||||
@ -249,7 +242,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (settings.getFrameRate() > 0) {
|
if (settings.getFrameRate() > 0) {
|
||||||
minFrameDuration = (long)(1000d / settings.getFrameRate()); // ms
|
minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms
|
||||||
logger.log(Level.FINE, "Setting min tpf: {0}ms", minFrameDuration);
|
logger.log(Level.FINE, "Setting min tpf: {0}ms", minFrameDuration);
|
||||||
} else {
|
} else {
|
||||||
minFrameDuration = 0;
|
minFrameDuration = 0;
|
||||||
@ -409,7 +402,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) {
|
public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) {
|
||||||
logger.log(Level.FINE, "requestDialog: title: {0}, initialValue: {1}",
|
logger.log(Level.FINE, "requestDialog: title: {0}, initialValue: {1}",
|
||||||
new Object[]{title, initialValue});
|
new Object[]{title, initialValue});
|
||||||
@ -453,7 +445,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
|
|
||||||
AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK",
|
AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK",
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
/* User clicked OK, send COMPLETE action
|
/* User clicked OK, send COMPLETE action
|
||||||
* and text */
|
* and text */
|
||||||
@ -461,7 +452,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
}
|
}
|
||||||
}).setNegativeButton("Cancel",
|
}).setNegativeButton("Cancel",
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
/* User clicked CANCEL, send CANCEL action
|
/* User clicked CANCEL, send CANCEL action
|
||||||
* and text */
|
* and text */
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2020 jMonkeyEngine
|
* Copyright (c) 2009-2014 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -67,7 +67,6 @@ public class AndroidBufferImageLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(AssetInfo assetInfo) throws IOException {
|
public Object load(AssetInfo assetInfo) throws IOException {
|
||||||
Bitmap bitmap = null;
|
Bitmap bitmap = null;
|
||||||
Image.Format format;
|
Image.Format format;
|
||||||
|
@ -25,13 +25,12 @@ public class AndroidNativeImageLoader implements AssetLoader {
|
|||||||
|
|
||||||
private static native Image load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException;
|
private static native Image load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException;
|
||||||
|
|
||||||
@Override
|
|
||||||
public Image load(AssetInfo info) throws IOException {
|
public Image load(AssetInfo info) throws IOException {
|
||||||
boolean flip = ((TextureKey) info.getKey()).isFlipY();
|
boolean flip = ((TextureKey) info.getKey()).isFlipY();
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
in = info.openStream();
|
in = info.openStream();
|
||||||
return load(in, flip, tmpArray);
|
return load(info.openStream(), flip, tmpArray);
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null){
|
if (in != null){
|
||||||
in.close();
|
in.close();
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2019 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jme3.util;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.nio.Buffer;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Jesus Oliver
|
|
||||||
*/
|
|
||||||
public class AndroidBufferAllocator implements BufferAllocator {
|
|
||||||
|
|
||||||
// We make use of the ReflectionAllocator to remove the inner buffer
|
|
||||||
private static final ReflectionAllocator reflectionAllocator = new ReflectionAllocator();
|
|
||||||
|
|
||||||
private static final String[] wrapperClassNames = {
|
|
||||||
"java.nio.ByteBufferAsFloatBuffer",
|
|
||||||
"java.nio.ByteBufferAsIntBuffer",
|
|
||||||
"java.nio.ByteBufferAsDoubleBuffer",
|
|
||||||
"java.nio.ByteBufferAsShortBuffer",
|
|
||||||
"java.nio.ByteBufferAsLongBuffer",
|
|
||||||
"java.nio.ByteBufferAsCharBuffer",
|
|
||||||
};
|
|
||||||
private static final String[] possibleBufferFieldNames = {"bb", "byteBuffer"};
|
|
||||||
|
|
||||||
// Keep track of ByteBuffer field by the wrapper class
|
|
||||||
private static final Map<Class, Field> fieldIndex = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (String className : wrapperClassNames) {
|
|
||||||
try {
|
|
||||||
Class clazz = Class.forName(className);
|
|
||||||
|
|
||||||
// loop for all possible field names in android
|
|
||||||
for (String fieldName : possibleBufferFieldNames) {
|
|
||||||
try {
|
|
||||||
Field field = clazz.getDeclaredField(fieldName);
|
|
||||||
field.setAccessible(true);
|
|
||||||
fieldIndex.put(clazz, field);
|
|
||||||
break;
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
|
||||||
* This function search the inner direct buffer of the android specific wrapped buffer classes
|
|
||||||
* and destroys it using the reflection allocator method.
|
|
||||||
*
|
|
||||||
* @param toBeDestroyed The direct buffer that will be "cleaned".
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void destroyDirectBuffer(Buffer toBeDestroyed) {
|
|
||||||
// If it is a wrapped buffer, get it's inner direct buffer field and destroy it
|
|
||||||
Field field = fieldIndex.get(toBeDestroyed.getClass());
|
|
||||||
if (field != null) {
|
|
||||||
try {
|
|
||||||
ByteBuffer innerBuffer = (ByteBuffer) field.get(toBeDestroyed);
|
|
||||||
if (innerBuffer != null) {
|
|
||||||
// Destroy it using the reflection method
|
|
||||||
reflectionAllocator.destroyDirectBuffer(innerBuffer);
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException ex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// It is not a wrapped buffer, use default reflection allocator to remove it instead.
|
|
||||||
reflectionAllocator.destroyDirectBuffer(toBeDestroyed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer allocate(int size) {
|
|
||||||
return ByteBuffer.allocateDirect(size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -49,7 +49,6 @@ public class RingBuffer<T> implements Iterable<T> {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
public Iterator<T> iterator() {
|
||||||
return new RingBufferIterator();
|
return new RingBufferIterator();
|
||||||
}
|
}
|
||||||
@ -59,17 +58,14 @@ public class RingBuffer<T> implements Iterable<T> {
|
|||||||
|
|
||||||
private int i = 0;
|
private int i = 0;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return i < count;
|
return i < count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public T next() {
|
public T next() {
|
||||||
if (!hasNext()) {
|
if (!hasNext()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
12
jme3-blender/build.gradle
Normal file
12
jme3-blender/build.gradle
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
if (!hasProperty('mainClass')) {
|
||||||
|
ext.mainClass = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':jme3-core')
|
||||||
|
compile project(':jme3-desktop')
|
||||||
|
compile project(':jme3-effects')
|
||||||
|
compile ('org.ejml:core:0.27')
|
||||||
|
compile ('org.ejml:dense64:0.27')
|
||||||
|
compile ('org.ejml:simple:0.27')
|
||||||
|
}
|
733
jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
Normal file
733
jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
Normal file
@ -0,0 +1,733 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2018 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.asset;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.jme3.export.InputCapsule;
|
||||||
|
import com.jme3.export.JmeExporter;
|
||||||
|
import com.jme3.export.JmeImporter;
|
||||||
|
import com.jme3.export.OutputCapsule;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.RenderState.FaceCullMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blender key. Contains path of the blender file and its loading properties.
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class BlenderKey extends ModelKey {
|
||||||
|
protected static final int DEFAULT_FPS = 25;
|
||||||
|
/**
|
||||||
|
* FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
|
||||||
|
* between the frames.
|
||||||
|
*/
|
||||||
|
protected int fps = DEFAULT_FPS;
|
||||||
|
/**
|
||||||
|
* This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
|
||||||
|
*/
|
||||||
|
protected int featuresToLoad = FeaturesToLoad.ALL;
|
||||||
|
/** The variable that tells if content of the file (along with data unlinked to any feature on the scene) should be stored as 'user data' in the result spatial. */
|
||||||
|
protected boolean loadUnlinkedAssets;
|
||||||
|
/** The root path for all the assets. */
|
||||||
|
protected String assetRootPath;
|
||||||
|
/** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
|
||||||
|
protected boolean fixUpAxis = true;
|
||||||
|
/** Generated textures resolution (PPU - Pixels Per Unit). */
|
||||||
|
protected int generatedTexturePPU = 128;
|
||||||
|
/**
|
||||||
|
* The name of world settings that the importer will use. If not set or specified name does not occur in the file
|
||||||
|
* then the first world settings in the file will be used.
|
||||||
|
*/
|
||||||
|
protected String usedWorld;
|
||||||
|
/**
|
||||||
|
* User's default material that is set for objects that have no material definition in blender. The default value is
|
||||||
|
* null. If the value is null the importer will use its own default material (gray color - like in blender).
|
||||||
|
*/
|
||||||
|
protected Material defaultMaterial;
|
||||||
|
/** Face cull mode. By default it is disabled. */
|
||||||
|
protected FaceCullMode faceCullMode = FaceCullMode.Back;
|
||||||
|
/**
|
||||||
|
* Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
|
||||||
|
* If set to -1 then the current layer will be loaded.
|
||||||
|
*/
|
||||||
|
protected int layersToLoad = -1;
|
||||||
|
/** A variable that toggles the object custom properties loading. */
|
||||||
|
protected boolean loadObjectProperties = true;
|
||||||
|
/**
|
||||||
|
* Maximum texture size. Might be dependant on the graphic card.
|
||||||
|
* This value is taken from <b>org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE</b>.
|
||||||
|
*/
|
||||||
|
protected int maxTextureSize = 8192;
|
||||||
|
/** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
|
||||||
|
protected boolean loadGeneratedTextures;
|
||||||
|
/** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
|
||||||
|
protected MipmapGenerationMethod mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
|
||||||
|
/**
|
||||||
|
* If the sky has only generated textures applied then they will have the following size (both width and height). If 2d textures are used then the generated
|
||||||
|
* textures will get their proper size.
|
||||||
|
*/
|
||||||
|
protected int skyGeneratedTextureSize = 1000;
|
||||||
|
/** The radius of a shape that will be used while creating the generated texture for the sky. The higher it is the larger part of the texture will be seen. */
|
||||||
|
protected float skyGeneratedTextureRadius = 1;
|
||||||
|
/** The shape against which the generated texture for the sky will be created. */
|
||||||
|
protected SkyGeneratedTextureShape skyGeneratedTextureShape = SkyGeneratedTextureShape.SPHERE;
|
||||||
|
/**
|
||||||
|
* This field tells if the importer should optimise the use of textures or not. If set to true, then textures of the same mapping type will be merged together
|
||||||
|
* and textures that in the final result will never be visible - will be discarded.
|
||||||
|
*/
|
||||||
|
protected boolean optimiseTextures;
|
||||||
|
/** The method of matching animations to skeletons. The default value is: AT_LEAST_ONE_NAME_MATCH. */
|
||||||
|
protected AnimationMatchMethod animationMatchMethod = AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH;
|
||||||
|
/** The size of points that are loaded and do not belong to any edge of the mesh. */
|
||||||
|
protected float pointsSize = 1;
|
||||||
|
/** The width of edges that are loaded from the mesh and do not belong to any face. */
|
||||||
|
protected float linesWidth = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor used by serialization mechanisms.
|
||||||
|
*/
|
||||||
|
public BlenderKey() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Creates a key for the given file name.
|
||||||
|
* @param name
|
||||||
|
* the name (path) of a file
|
||||||
|
*/
|
||||||
|
public BlenderKey(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
|
||||||
|
* @return the frames per second amount
|
||||||
|
*/
|
||||||
|
public int getFps() {
|
||||||
|
return fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets frames per second amount.
|
||||||
|
* @param fps
|
||||||
|
* the frames per second amount
|
||||||
|
*/
|
||||||
|
public void setFps(int fps) {
|
||||||
|
this.fps = fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the face cull mode.
|
||||||
|
* @return the face cull mode
|
||||||
|
*/
|
||||||
|
public FaceCullMode getFaceCullMode() {
|
||||||
|
return faceCullMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the face cull mode.
|
||||||
|
* @param faceCullMode
|
||||||
|
* the face cull mode
|
||||||
|
*/
|
||||||
|
public void setFaceCullMode(FaceCullMode faceCullMode) {
|
||||||
|
this.faceCullMode = faceCullMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets layers to be loaded.
|
||||||
|
* @param layersToLoad
|
||||||
|
* layers to be loaded
|
||||||
|
*/
|
||||||
|
public void setLayersToLoad(int layersToLoad) {
|
||||||
|
this.layersToLoad = layersToLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns layers to be loaded.
|
||||||
|
* @return layers to be loaded
|
||||||
|
*/
|
||||||
|
public int getLayersToLoad() {
|
||||||
|
return layersToLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the properies loading policy.
|
||||||
|
* By default the value is true.
|
||||||
|
* @param loadObjectProperties
|
||||||
|
* true to load properties and false to suspend their loading
|
||||||
|
*/
|
||||||
|
public void setLoadObjectProperties(boolean loadObjectProperties) {
|
||||||
|
this.loadObjectProperties = loadObjectProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current properties loading properties
|
||||||
|
*/
|
||||||
|
public boolean isLoadObjectProperties() {
|
||||||
|
return loadObjectProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value for this parameter is the same as defined by: org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE.
|
||||||
|
* If by any means this is too large for user's hardware configuration use the 'setMaxTextureSize' method to change that.
|
||||||
|
* @return maximum texture size (width/height)
|
||||||
|
*/
|
||||||
|
public int getMaxTextureSize() {
|
||||||
|
return maxTextureSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the maximum texture size.
|
||||||
|
* @param maxTextureSize
|
||||||
|
* the maximum texture size
|
||||||
|
*/
|
||||||
|
public void setMaxTextureSize(int maxTextureSize) {
|
||||||
|
this.maxTextureSize = maxTextureSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the flag that toggles the generated textures loading.
|
||||||
|
* @param loadGeneratedTextures
|
||||||
|
* <b>true</b> if generated textures should be loaded and <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public void setLoadGeneratedTextures(boolean loadGeneratedTextures) {
|
||||||
|
this.loadGeneratedTextures = loadGeneratedTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return tells if the generated textures should be loaded (<b>false</b> is the default value)
|
||||||
|
*/
|
||||||
|
public boolean isLoadGeneratedTextures() {
|
||||||
|
return loadGeneratedTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not used any more.
|
||||||
|
* This method sets the asset root path.
|
||||||
|
* @param assetRootPath
|
||||||
|
* the assets root path
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setAssetRootPath(String assetRootPath) {
|
||||||
|
this.assetRootPath = assetRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not used any more.
|
||||||
|
* This method returns the asset root path.
|
||||||
|
* @return the asset root path
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public String getAssetRootPath() {
|
||||||
|
return assetRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds features to be loaded.
|
||||||
|
* @param featuresToLoad
|
||||||
|
* bitwise flag of FeaturesToLoad interface values
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void includeInLoading(int featuresToLoad) {
|
||||||
|
this.featuresToLoad |= featuresToLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method removes features from being loaded.
|
||||||
|
* @param featuresNotToLoad
|
||||||
|
* bitwise flag of FeaturesToLoad interface values
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void excludeFromLoading(int featuresNotToLoad) {
|
||||||
|
featuresToLoad &= ~featuresNotToLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public boolean shouldLoad(int featureToLoad) {
|
||||||
|
return (featuresToLoad & featureToLoad) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
|
||||||
|
* the blender file loader.
|
||||||
|
* @return features that will be loaded by the blender file loader
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int getFeaturesToLoad() {
|
||||||
|
return featuresToLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method determines if unlinked assets should be loaded.
|
||||||
|
* If not then only objects on selected layers will be loaded and their assets if required.
|
||||||
|
* If yes then all assets will be loaded even if they are on inactive layers or are not linked
|
||||||
|
* to anything.
|
||||||
|
* @return <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public boolean isLoadUnlinkedAssets() {
|
||||||
|
return loadUnlinkedAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets if unlinked assets should be loaded.
|
||||||
|
* If not then only objects on selected layers will be loaded and their assets if required.
|
||||||
|
* If yes then all assets will be loaded even if they are on inactive layers or are not linked
|
||||||
|
* to anything.
|
||||||
|
* @param loadUnlinkedAssets
|
||||||
|
* <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
|
||||||
|
this.loadUnlinkedAssets = loadUnlinkedAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
|
||||||
|
* is up axis.
|
||||||
|
* @param fixUpAxis
|
||||||
|
* the up axis state variable
|
||||||
|
*/
|
||||||
|
public void setFixUpAxis(boolean fixUpAxis) {
|
||||||
|
this.fixUpAxis = fixUpAxis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
|
||||||
|
* default Y is up axis.
|
||||||
|
* @return the up axis state variable
|
||||||
|
*/
|
||||||
|
public boolean isFixUpAxis() {
|
||||||
|
return fixUpAxis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the generated textures resolution.
|
||||||
|
* @param generatedTexturePPU
|
||||||
|
* the generated textures resolution
|
||||||
|
*/
|
||||||
|
public void setGeneratedTexturePPU(int generatedTexturePPU) {
|
||||||
|
this.generatedTexturePPU = generatedTexturePPU;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the generated textures resolution
|
||||||
|
*/
|
||||||
|
public int getGeneratedTexturePPU() {
|
||||||
|
return generatedTexturePPU;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mipmaps generation method
|
||||||
|
*/
|
||||||
|
public MipmapGenerationMethod getMipmapGenerationMethod() {
|
||||||
|
return mipmapGenerationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mipmapGenerationMethod
|
||||||
|
* mipmaps generation method
|
||||||
|
*/
|
||||||
|
public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) {
|
||||||
|
this.mipmapGenerationMethod = mipmapGenerationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the size of the generated textures for the sky (used if no flat textures are applied)
|
||||||
|
*/
|
||||||
|
public int getSkyGeneratedTextureSize() {
|
||||||
|
return skyGeneratedTextureSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param skyGeneratedTextureSize
|
||||||
|
* the size of the generated textures for the sky (used if no flat textures are applied)
|
||||||
|
*/
|
||||||
|
public void setSkyGeneratedTextureSize(int skyGeneratedTextureSize) {
|
||||||
|
if (skyGeneratedTextureSize <= 0) {
|
||||||
|
throw new IllegalArgumentException("The texture size must be a positive value (the value given as a parameter: " + skyGeneratedTextureSize + ")!");
|
||||||
|
}
|
||||||
|
this.skyGeneratedTextureSize = skyGeneratedTextureSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
|
||||||
|
*/
|
||||||
|
public float getSkyGeneratedTextureRadius() {
|
||||||
|
return skyGeneratedTextureRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param skyGeneratedTextureRadius
|
||||||
|
* the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
|
||||||
|
*/
|
||||||
|
public void setSkyGeneratedTextureRadius(float skyGeneratedTextureRadius) {
|
||||||
|
this.skyGeneratedTextureRadius = skyGeneratedTextureRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the shape against which the generated texture for the sky will be created (by default it is a sphere).
|
||||||
|
*/
|
||||||
|
public SkyGeneratedTextureShape getSkyGeneratedTextureShape() {
|
||||||
|
return skyGeneratedTextureShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param skyGeneratedTextureShape
|
||||||
|
* the shape against which the generated texture for the sky will be created
|
||||||
|
*/
|
||||||
|
public void setSkyGeneratedTextureShape(SkyGeneratedTextureShape skyGeneratedTextureShape) {
|
||||||
|
if (skyGeneratedTextureShape == null) {
|
||||||
|
throw new IllegalArgumentException("The sky generated shape type cannot be null!");
|
||||||
|
}
|
||||||
|
this.skyGeneratedTextureShape = skyGeneratedTextureShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, then textures of the same mapping type will be merged together
|
||||||
|
* and textures that in the final result will never be visible - will be discarded.
|
||||||
|
* @param optimiseTextures
|
||||||
|
* the variable that tells if the textures should be optimised or not
|
||||||
|
*/
|
||||||
|
public void setOptimiseTextures(boolean optimiseTextures) {
|
||||||
|
this.optimiseTextures = optimiseTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the variable that tells if the textures should be optimised or not (by default the optimisation is disabled)
|
||||||
|
*/
|
||||||
|
public boolean isOptimiseTextures() {
|
||||||
|
return optimiseTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the way the animations will be matched with skeletons.
|
||||||
|
*
|
||||||
|
* @param animationMatchMethod
|
||||||
|
* the way the animations will be matched with skeletons
|
||||||
|
*/
|
||||||
|
public void setAnimationMatchMethod(AnimationMatchMethod animationMatchMethod) {
|
||||||
|
this.animationMatchMethod = animationMatchMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the way the animations will be matched with skeletons
|
||||||
|
*/
|
||||||
|
public AnimationMatchMethod getAnimationMatchMethod() {
|
||||||
|
return animationMatchMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the size of points that are loaded and do not belong to any edge of the mesh
|
||||||
|
*/
|
||||||
|
public float getPointsSize() {
|
||||||
|
return pointsSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the size of points that are loaded and do not belong to any edge of the mesh.
|
||||||
|
* @param pointsSize
|
||||||
|
* The size of points that are loaded and do not belong to any edge of the mesh
|
||||||
|
*/
|
||||||
|
public void setPointsSize(float pointsSize) {
|
||||||
|
this.pointsSize = pointsSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the width of edges that are loaded from the mesh and do not belong to any face
|
||||||
|
*/
|
||||||
|
public float getLinesWidth() {
|
||||||
|
return linesWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the width of edges that are loaded from the mesh and do not belong to any face.
|
||||||
|
* @param linesWidth
|
||||||
|
* the width of edges that are loaded from the mesh and do not belong to any face
|
||||||
|
*/
|
||||||
|
public void setLinesWidth(float linesWidth) {
|
||||||
|
this.linesWidth = linesWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the name of the WORLD data block that should be used during file loading. By default the name is
|
||||||
|
* not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
|
||||||
|
* during loading (assuming any exists in the file).
|
||||||
|
* @param usedWorld
|
||||||
|
* the name of the WORLD block used during loading
|
||||||
|
*/
|
||||||
|
public void setUsedWorld(String usedWorld) {
|
||||||
|
this.usedWorld = usedWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the name of the WORLD data block that should be used during file loading.
|
||||||
|
* @return the name of the WORLD block used during loading
|
||||||
|
*/
|
||||||
|
public String getUsedWorld() {
|
||||||
|
return usedWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the default material for objects.
|
||||||
|
* @param defaultMaterial
|
||||||
|
* the default material
|
||||||
|
*/
|
||||||
|
public void setDefaultMaterial(Material defaultMaterial) {
|
||||||
|
this.defaultMaterial = defaultMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the default material.
|
||||||
|
* @return the default material
|
||||||
|
*/
|
||||||
|
public Material getDefaultMaterial() {
|
||||||
|
return defaultMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter e) throws IOException {
|
||||||
|
super.write(e);
|
||||||
|
OutputCapsule oc = e.getCapsule(this);
|
||||||
|
oc.write(fps, "fps", DEFAULT_FPS);
|
||||||
|
oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
|
||||||
|
oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
|
||||||
|
oc.write(assetRootPath, "asset-root-path", null);
|
||||||
|
oc.write(fixUpAxis, "fix-up-axis", true);
|
||||||
|
oc.write(generatedTexturePPU, "generated-texture-ppu", 128);
|
||||||
|
oc.write(usedWorld, "used-world", null);
|
||||||
|
oc.write(defaultMaterial, "default-material", null);
|
||||||
|
oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
|
||||||
|
oc.write(layersToLoad, "layers-to-load", -1);
|
||||||
|
oc.write(mipmapGenerationMethod, "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
|
||||||
|
oc.write(skyGeneratedTextureSize, "sky-generated-texture-size", 1000);
|
||||||
|
oc.write(skyGeneratedTextureRadius, "sky-generated-texture-radius", 1f);
|
||||||
|
oc.write(skyGeneratedTextureShape, "sky-generated-texture-shape", SkyGeneratedTextureShape.SPHERE);
|
||||||
|
oc.write(optimiseTextures, "optimise-textures", false);
|
||||||
|
oc.write(animationMatchMethod, "animation-match-method", AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH);
|
||||||
|
oc.write(pointsSize, "points-size", 1);
|
||||||
|
oc.write(linesWidth, "lines-width", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter e) throws IOException {
|
||||||
|
super.read(e);
|
||||||
|
InputCapsule ic = e.getCapsule(this);
|
||||||
|
fps = ic.readInt("fps", DEFAULT_FPS);
|
||||||
|
featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
|
||||||
|
loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
|
||||||
|
assetRootPath = ic.readString("asset-root-path", null);
|
||||||
|
fixUpAxis = ic.readBoolean("fix-up-axis", true);
|
||||||
|
generatedTexturePPU = ic.readInt("generated-texture-ppu", 128);
|
||||||
|
usedWorld = ic.readString("used-world", null);
|
||||||
|
defaultMaterial = (Material) ic.readSavable("default-material", null);
|
||||||
|
faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
|
||||||
|
layersToLoad = ic.readInt("layers-to=load", -1);
|
||||||
|
mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
|
||||||
|
skyGeneratedTextureSize = ic.readInt("sky-generated-texture-size", 1000);
|
||||||
|
skyGeneratedTextureRadius = ic.readFloat("sky-generated-texture-radius", 1f);
|
||||||
|
skyGeneratedTextureShape = ic.readEnum("sky-generated-texture-shape", SkyGeneratedTextureShape.class, SkyGeneratedTextureShape.SPHERE);
|
||||||
|
optimiseTextures = ic.readBoolean("optimise-textures", false);
|
||||||
|
animationMatchMethod = ic.readEnum("animation-match-method", AnimationMatchMethod.class, AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH);
|
||||||
|
pointsSize = ic.readFloat("points-size", 1);
|
||||||
|
linesWidth = ic.readFloat("lines-width", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = super.hashCode();
|
||||||
|
result = prime * result + (animationMatchMethod == null ? 0 : animationMatchMethod.hashCode());
|
||||||
|
result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
|
||||||
|
result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
|
||||||
|
result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
|
||||||
|
result = prime * result + featuresToLoad;
|
||||||
|
result = prime * result + (fixUpAxis ? 1231 : 1237);
|
||||||
|
result = prime * result + fps;
|
||||||
|
result = prime * result + generatedTexturePPU;
|
||||||
|
result = prime * result + layersToLoad;
|
||||||
|
result = prime * result + (loadGeneratedTextures ? 1231 : 1237);
|
||||||
|
result = prime * result + (loadObjectProperties ? 1231 : 1237);
|
||||||
|
result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
|
||||||
|
result = prime * result + maxTextureSize;
|
||||||
|
result = prime * result + (mipmapGenerationMethod == null ? 0 : mipmapGenerationMethod.hashCode());
|
||||||
|
result = prime * result + (optimiseTextures ? 1231 : 1237);
|
||||||
|
result = prime * result + Float.floatToIntBits(skyGeneratedTextureRadius);
|
||||||
|
result = prime * result + (skyGeneratedTextureShape == null ? 0 : skyGeneratedTextureShape.hashCode());
|
||||||
|
result = prime * result + skyGeneratedTextureSize;
|
||||||
|
result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
|
||||||
|
result = prime * result + (int) pointsSize;
|
||||||
|
result = prime * result + (int) linesWidth;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof BlenderKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BlenderKey other = (BlenderKey) obj;
|
||||||
|
if (animationMatchMethod != other.animationMatchMethod) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (assetRootPath == null) {
|
||||||
|
if (other.assetRootPath != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!assetRootPath.equals(other.assetRootPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (defaultMaterial == null) {
|
||||||
|
if (other.defaultMaterial != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!defaultMaterial.equals(other.defaultMaterial)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (faceCullMode != other.faceCullMode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (featuresToLoad != other.featuresToLoad) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fixUpAxis != other.fixUpAxis) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fps != other.fps) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (generatedTexturePPU != other.generatedTexturePPU) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (layersToLoad != other.layersToLoad) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (loadGeneratedTextures != other.loadGeneratedTextures) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (loadObjectProperties != other.loadObjectProperties) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (maxTextureSize != other.maxTextureSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mipmapGenerationMethod != other.mipmapGenerationMethod) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (optimiseTextures != other.optimiseTextures) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Float.floatToIntBits(skyGeneratedTextureRadius) != Float.floatToIntBits(other.skyGeneratedTextureRadius)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (skyGeneratedTextureShape != other.skyGeneratedTextureShape) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (skyGeneratedTextureSize != other.skyGeneratedTextureSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (usedWorld == null) {
|
||||||
|
if (other.usedWorld != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!usedWorld.equals(other.usedWorld)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pointsSize != other.pointsSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (linesWidth != other.linesWidth) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enum tells the importer if the mipmaps for textures will be generated by jme. <li>NEVER_GENERATE and ALWAYS_GENERATE are quite understandable <li>GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set in blender, mipmaps are generated only when the option is set
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public static enum MipmapGenerationMethod {
|
||||||
|
NEVER_GENERATE, ALWAYS_GENERATE, GENERATE_WHEN_NEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface describes the features of the scene that are to be loaded.
|
||||||
|
* @deprecated this interface is deprecated and is not used anymore; to ensure the loading models consistency
|
||||||
|
* everything must be loaded because in blender one feature might depend on another
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static interface FeaturesToLoad {
|
||||||
|
|
||||||
|
int SCENES = 0x0000FFFF;
|
||||||
|
int OBJECTS = 0x0000000B;
|
||||||
|
int ANIMATIONS = 0x00000004;
|
||||||
|
int MATERIALS = 0x00000003;
|
||||||
|
int TEXTURES = 0x00000001;
|
||||||
|
int CAMERAS = 0x00000020;
|
||||||
|
int LIGHTS = 0x00000010;
|
||||||
|
int WORLD = 0x00000040;
|
||||||
|
int ALL = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shape againts which the sky generated texture will be created.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public static enum SkyGeneratedTextureShape {
|
||||||
|
CUBE, SPHERE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enum describes which animations should be attached to which armature.
|
||||||
|
* Blender does not store the mapping between action and armature. That is why the importer
|
||||||
|
* will try to match those by comparing bone name of the armature with the channel names
|
||||||
|
* int the actions.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public static enum AnimationMatchMethod {
|
||||||
|
/**
|
||||||
|
* Animation is matched with skeleton when at leas one bone name matches the name of the action channel.
|
||||||
|
* All the bones that do not have their corresponding channel in the animation will not get the proper tracks for
|
||||||
|
* this particulat animation.
|
||||||
|
* Also the channel will not be used for the animation if it does not find the proper bone name.
|
||||||
|
*/
|
||||||
|
AT_LEAST_ONE_NAME_MATCH,
|
||||||
|
/**
|
||||||
|
* Animation is matched when all action names are covered by the target names (bone names or the name of the
|
||||||
|
* animated spatial.
|
||||||
|
*/
|
||||||
|
ALL_NAMES_MATCH;
|
||||||
|
}
|
||||||
|
}
|
@ -29,36 +29,41 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* 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;
|
|
||||||
|
|
||||||
|
package com.jme3.asset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This shader Generator can generate Vertex and Fragment shaders from
|
* This key is mostly used to distinguish between textures that are loaded from
|
||||||
* ShaderNodes for GLESSL 3.0
|
* the given assets and those being generated automatically. Every generated
|
||||||
* Nowdays it's just a subclass of Glsl150ShaderGenerator overriding the version
|
* texture will have this kind of key attached.
|
||||||
* string because GLSL 1.5 is mostly compatible with GLESSL 3.0
|
|
||||||
*
|
*
|
||||||
* @author Nehon
|
* @author Marcin Roguski (Kaelthas)
|
||||||
* @author Joliver82
|
|
||||||
*/
|
*/
|
||||||
public class Glsl300ShaderGenerator extends Glsl150ShaderGenerator {
|
public class GeneratedTextureKey extends TextureKey {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Glsl300ShaderGenerator
|
* Constructor. Stores the name. Extension and folder name are empty
|
||||||
|
* strings.
|
||||||
*
|
*
|
||||||
* @param assetManager the assetmanager
|
* @param name
|
||||||
|
* the name of the texture
|
||||||
*/
|
*/
|
||||||
public Glsl300ShaderGenerator(AssetManager assetManager) {
|
public GeneratedTextureKey(String name) {
|
||||||
super(assetManager);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getLanguageAndVersion(ShaderType type) {
|
public String getExtension() {
|
||||||
return "GLSL300";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFolder() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Generated texture [" + name + "]";
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* 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.scene.plugins.blender;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetNotFoundException;
|
||||||
|
import com.jme3.asset.BlenderKey;
|
||||||
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
|
||||||
|
* hold the state of the calculations.
|
||||||
|
* @author Marcin Roguski
|
||||||
|
*/
|
||||||
|
public abstract class AbstractBlenderHelper {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName());
|
||||||
|
|
||||||
|
/** The blender context. */
|
||||||
|
protected BlenderContext blenderContext;
|
||||||
|
/** The version of the blend file. */
|
||||||
|
protected final int blenderVersion;
|
||||||
|
/** This variable indicates if the Y asxis is the UP axis or not. */
|
||||||
|
protected boolean fixUpAxis;
|
||||||
|
/** Quaternion used to rotate data when Y is up axis. */
|
||||||
|
protected Quaternion upAxisRotationQuaternion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
|
||||||
|
* versions.
|
||||||
|
* @param blenderVersion
|
||||||
|
* the version read from the blend file
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public AbstractBlenderHelper(String blenderVersion, BlenderContext blenderContext) {
|
||||||
|
this.blenderVersion = Integer.parseInt(blenderVersion);
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
|
fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
|
||||||
|
if (fixUpAxis) {
|
||||||
|
upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method loads the properties if they are available and defined for the structure.
|
||||||
|
* @param structure
|
||||||
|
* the structure we read the properties from
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return loaded properties or null if they are not available
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when the blend file is somehow corrupted
|
||||||
|
*/
|
||||||
|
protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
Properties properties = null;
|
||||||
|
Structure id = (Structure) structure.getFieldValue("ID");
|
||||||
|
if (id != null) {
|
||||||
|
Pointer pProperties = (Pointer) id.getFieldValue("properties");
|
||||||
|
if (pProperties.isNotNull()) {
|
||||||
|
Structure propertiesStructure = pProperties.fetchData().get(0);
|
||||||
|
properties = new Properties();
|
||||||
|
properties.load(propertiesStructure, blenderContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method applies properties to the given spatial. The Properties
|
||||||
|
* instance cannot be directly applied because the end-user might not have
|
||||||
|
* the blender plugin jar file and thus receive ClassNotFoundException. The
|
||||||
|
* values are set by name instead.
|
||||||
|
*
|
||||||
|
* @param spatial
|
||||||
|
* the spatial that is to have properties applied
|
||||||
|
* @param properties
|
||||||
|
* the properties to be applied
|
||||||
|
*/
|
||||||
|
public void applyProperties(Spatial spatial, Properties properties) {
|
||||||
|
List<String> propertyNames = properties.getSubPropertiesNames();
|
||||||
|
if (propertyNames != null && propertyNames.size() > 0) {
|
||||||
|
for (String propertyName : propertyNames) {
|
||||||
|
Object value = properties.findValue(propertyName);
|
||||||
|
if (value instanceof Savable || value instanceof Boolean || value instanceof String || value instanceof Float || value instanceof Integer || value instanceof Long) {
|
||||||
|
spatial.setUserData(propertyName, value);
|
||||||
|
} else if (value instanceof Double) {
|
||||||
|
spatial.setUserData(propertyName, ((Double) value).floatValue());
|
||||||
|
} else if (value instanceof int[]) {
|
||||||
|
spatial.setUserData(propertyName, Arrays.toString((int[]) value));
|
||||||
|
} else if (value instanceof float[]) {
|
||||||
|
spatial.setUserData(propertyName, Arrays.toString((float[]) value));
|
||||||
|
} else if (value instanceof double[]) {
|
||||||
|
spatial.setUserData(propertyName, Arrays.toString((double[]) value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method loads library of a given ID from linked blender file.
|
||||||
|
* @param id
|
||||||
|
* the ID of the linked feature (it contains its name and blender path)
|
||||||
|
* @return loaded feature or null if none was found
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* and exception is throw when problems with reading a blend file occur
|
||||||
|
*/
|
||||||
|
protected Object loadLibrary(Structure id) throws BlenderFileException {
|
||||||
|
Pointer pLib = (Pointer) id.getFieldValue("lib");
|
||||||
|
if (pLib.isNotNull()) {
|
||||||
|
String fullName = id.getFieldValue("name").toString();// we need full name with the prefix
|
||||||
|
String nameOfFeatureToLoad = id.getName();
|
||||||
|
Structure library = pLib.fetchData().get(0);
|
||||||
|
String path = library.getFieldValue("filepath").toString();
|
||||||
|
|
||||||
|
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
|
||||||
|
Spatial loadedAsset = null;
|
||||||
|
BlenderKey blenderKey = new BlenderKey(path);
|
||||||
|
blenderKey.setLoadUnlinkedAssets(true);
|
||||||
|
try {
|
||||||
|
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
|
||||||
|
} catch (AssetNotFoundException e) {
|
||||||
|
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedAsset != null) {
|
||||||
|
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
|
||||||
|
|
||||||
|
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
|
||||||
|
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
|
||||||
|
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object result = blenderContext.getLinkedFeature(path, fullName);
|
||||||
|
if (result == null) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could NOT find asset named {0} in the library of path: {1}.", new Object[] { nameOfFeatureToLoad, path });
|
||||||
|
} else {
|
||||||
|
blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.STRUCTURE, id);
|
||||||
|
blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
LOGGER.warning("Library link points to nothing!");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,770 @@
|
|||||||
|
/*
|
||||||
|
* 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.scene.plugins.blender;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EmptyStackException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import com.jme3.animation.Animation;
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.asset.BlenderKey;
|
||||||
|
import com.jme3.light.Light;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.post.Filter;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BlenderAction;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.Constraint;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||||
|
import com.jme3.scene.plugins.blender.file.DnaBlockData;
|
||||||
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||||
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||||
|
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class that stores temporary data and manages it during loading the belnd
|
||||||
|
* file. This class is intended to be used in a single loading thread. It holds
|
||||||
|
* the state of loading operations.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class BlenderContext {
|
||||||
|
/** The blender file version. */
|
||||||
|
private int blenderVersion;
|
||||||
|
/** The blender key. */
|
||||||
|
private BlenderKey blenderKey;
|
||||||
|
/** The header of the file block. */
|
||||||
|
private DnaBlockData dnaBlockData;
|
||||||
|
/** The scene structure. */
|
||||||
|
private Structure sceneStructure;
|
||||||
|
/** The input stream of the blend file. */
|
||||||
|
private BlenderInputStream inputStream;
|
||||||
|
/** The asset manager. */
|
||||||
|
private AssetManager assetManager;
|
||||||
|
/** The blocks read from the file. */
|
||||||
|
protected List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
|
||||||
|
/**
|
||||||
|
* A map containing the file block headers. The key is the old memory address.
|
||||||
|
*/
|
||||||
|
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
|
||||||
|
/** A map containing the file block headers. The key is the block code. */
|
||||||
|
private Map<BlockCode, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<BlockCode, List<FileBlockHeader>>();
|
||||||
|
/**
|
||||||
|
* This map stores the loaded features by their old memory address. The
|
||||||
|
* first object in the value table is the loaded structure and the second -
|
||||||
|
* the structure already converted into proper data.
|
||||||
|
*/
|
||||||
|
private Map<Long, Map<LoadedDataType, Object>> loadedFeatures = new HashMap<Long, Map<LoadedDataType, Object>>();
|
||||||
|
/** Features loaded from external blender files. The key is the file path and the value is a map between feature name and loaded feature. */
|
||||||
|
private Map<String, Map<String, Object>> linkedFeatures = new HashMap<String, Map<String, Object>>();
|
||||||
|
/** A stack that hold the parent structure of currently loaded feature. */
|
||||||
|
private Stack<Structure> parentStack = new Stack<Structure>();
|
||||||
|
/** A list of constraints for the specified object. */
|
||||||
|
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
|
||||||
|
/** Animations loaded for features. */
|
||||||
|
private Map<Long, List<Animation>> animations = new HashMap<Long, List<Animation>>();
|
||||||
|
/** Loaded skeletons. */
|
||||||
|
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
|
||||||
|
/** A map between skeleton and node it modifies. */
|
||||||
|
private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>();
|
||||||
|
/** A map of bone contexts. */
|
||||||
|
protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>();
|
||||||
|
/** A map og helpers that perform loading. */
|
||||||
|
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
|
||||||
|
/** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */
|
||||||
|
private Map<String, Map<Object, Object>> markers = new HashMap<String, Map<Object, Object>>();
|
||||||
|
/** A map of blender actions. The key is the action name and the value is the action itself. */
|
||||||
|
private Map<String, BlenderAction> actions = new HashMap<String, BlenderAction>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the blender file version.
|
||||||
|
*
|
||||||
|
* @param blenderVersion
|
||||||
|
* the blender file version
|
||||||
|
*/
|
||||||
|
public void setBlenderVersion(String blenderVersion) {
|
||||||
|
this.blenderVersion = Integer.parseInt(blenderVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the blender file version
|
||||||
|
*/
|
||||||
|
public int getBlenderVersion() {
|
||||||
|
return blenderVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the blender key.
|
||||||
|
*
|
||||||
|
* @param blenderKey
|
||||||
|
* the blender key
|
||||||
|
*/
|
||||||
|
public void setBlenderKey(BlenderKey blenderKey) {
|
||||||
|
this.blenderKey = blenderKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the blender key.
|
||||||
|
*
|
||||||
|
* @return the blender key
|
||||||
|
*/
|
||||||
|
public BlenderKey getBlenderKey() {
|
||||||
|
return blenderKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the dna block data.
|
||||||
|
*
|
||||||
|
* @param dnaBlockData
|
||||||
|
* the dna block data
|
||||||
|
*/
|
||||||
|
public void setBlockData(DnaBlockData dnaBlockData) {
|
||||||
|
this.dnaBlockData = dnaBlockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the dna block data.
|
||||||
|
*
|
||||||
|
* @return the dna block data
|
||||||
|
*/
|
||||||
|
public DnaBlockData getDnaBlockData() {
|
||||||
|
return dnaBlockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the scene structure data.
|
||||||
|
*
|
||||||
|
* @param sceneStructure
|
||||||
|
* the scene structure data
|
||||||
|
*/
|
||||||
|
public void setSceneStructure(Structure sceneStructure) {
|
||||||
|
this.sceneStructure = sceneStructure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the scene structure data.
|
||||||
|
*
|
||||||
|
* @return the scene structure data
|
||||||
|
*/
|
||||||
|
public Structure getSceneStructure() {
|
||||||
|
return sceneStructure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the asset manager.
|
||||||
|
*
|
||||||
|
* @return the asset manager
|
||||||
|
*/
|
||||||
|
public AssetManager getAssetManager() {
|
||||||
|
return assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the asset manager.
|
||||||
|
*
|
||||||
|
* @param assetManager
|
||||||
|
* the asset manager
|
||||||
|
*/
|
||||||
|
public void setAssetManager(AssetManager assetManager) {
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the input stream of the blend file.
|
||||||
|
*
|
||||||
|
* @return the input stream of the blend file
|
||||||
|
*/
|
||||||
|
public BlenderInputStream getInputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the input stream of the blend file.
|
||||||
|
*
|
||||||
|
* @param inputStream
|
||||||
|
* the input stream of the blend file
|
||||||
|
*/
|
||||||
|
public void setInputStream(BlenderInputStream inputStream) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds a file block header to the map. Its old memory address
|
||||||
|
* is the key.
|
||||||
|
*
|
||||||
|
* @param oldMemoryAddress
|
||||||
|
* the address of the block header
|
||||||
|
* @param fileBlockHeader
|
||||||
|
* the block header to store
|
||||||
|
*/
|
||||||
|
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
|
||||||
|
blocks.add(fileBlockHeader);
|
||||||
|
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
|
||||||
|
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
|
||||||
|
if (headers == null) {
|
||||||
|
headers = new ArrayList<FileBlockHeader>();
|
||||||
|
fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers);
|
||||||
|
}
|
||||||
|
headers.add(fileBlockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the block headers
|
||||||
|
*/
|
||||||
|
public List<FileBlockHeader> getBlocks() {
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the block header of a given memory address. If the
|
||||||
|
* header is not present then null is returned.
|
||||||
|
*
|
||||||
|
* @param oldMemoryAddress
|
||||||
|
* the address of the block header
|
||||||
|
* @return loaded header or null if it was not yet loaded
|
||||||
|
*/
|
||||||
|
public FileBlockHeader getFileBlock(Long oldMemoryAddress) {
|
||||||
|
return fileBlockHeadersByOma.get(oldMemoryAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns a list of file blocks' headers of a specified code.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* the code of file blocks
|
||||||
|
* @return a list of file blocks' headers of a specified code
|
||||||
|
*/
|
||||||
|
public List<FileBlockHeader> getFileBlocks(BlockCode code) {
|
||||||
|
return fileBlockHeadersByCode.get(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds a helper instance to the helpers' map.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the helper
|
||||||
|
* @param clazz
|
||||||
|
* helper's class definition
|
||||||
|
* @param helper
|
||||||
|
* the helper instance
|
||||||
|
*/
|
||||||
|
public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
|
||||||
|
helpers.put(clazz.getSimpleName(), helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getHelper(Class<?> clazz) {
|
||||||
|
return (T) helpers.get(clazz.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds a loaded feature to the map. The key is its unique old
|
||||||
|
* memory address.
|
||||||
|
*
|
||||||
|
* @param oldMemoryAddress
|
||||||
|
* the address of the feature
|
||||||
|
* @param featureName
|
||||||
|
* the name of the feature
|
||||||
|
* @param structure
|
||||||
|
* the filled structure of the feature
|
||||||
|
* @param feature
|
||||||
|
* the feature we want to store
|
||||||
|
*/
|
||||||
|
public void addLoadedFeatures(Long oldMemoryAddress, LoadedDataType featureDataType, Object feature) {
|
||||||
|
if (oldMemoryAddress == null || featureDataType == null || feature == null) {
|
||||||
|
throw new IllegalArgumentException("One of the given arguments is null!");
|
||||||
|
}
|
||||||
|
Map<LoadedDataType, Object> map = loadedFeatures.get(oldMemoryAddress);
|
||||||
|
if (map == null) {
|
||||||
|
map = new HashMap<BlenderContext.LoadedDataType, Object>();
|
||||||
|
loadedFeatures.put(oldMemoryAddress, map);
|
||||||
|
}
|
||||||
|
map.put(featureDataType, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the feature of a given memory address. If the feature
|
||||||
|
* is not yet loaded then null is returned.
|
||||||
|
*
|
||||||
|
* @param oldMemoryAddress
|
||||||
|
* the address of the feature
|
||||||
|
* @param loadedFeatureDataType
|
||||||
|
* the type of data we want to retreive it can be either filled
|
||||||
|
* structure or already converted feature
|
||||||
|
* @return loaded feature or null if it was not yet loaded
|
||||||
|
*/
|
||||||
|
public Object getLoadedFeature(Long oldMemoryAddress, LoadedDataType loadedFeatureDataType) {
|
||||||
|
Map<LoadedDataType, Object> result = loadedFeatures.get(oldMemoryAddress);
|
||||||
|
if (result != null) {
|
||||||
|
return result.get(loadedFeatureDataType);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method adds linked content to the blender context.
|
||||||
|
* @param blenderFilePath
|
||||||
|
* the path of linked blender file
|
||||||
|
* @param featureGroup
|
||||||
|
* the linked feature group (ie. scenes, materials, meshes, etc.)
|
||||||
|
* @param feature
|
||||||
|
* the linked feature
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
|
||||||
|
// the method is deprecated and empty at the moment
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method returns linked feature of a given name from the specified blender path.
|
||||||
|
* @param blenderFilePath
|
||||||
|
* the blender file path
|
||||||
|
* @param featureName
|
||||||
|
* the feature name we want to get
|
||||||
|
* @return linked feature or null if none was found
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object getLinkedFeature(String blenderFilePath, String featureName) {
|
||||||
|
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
|
||||||
|
if(linkedFeatures != null) {
|
||||||
|
String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase();
|
||||||
|
featureName = featureName.substring(2);
|
||||||
|
|
||||||
|
if("SC".equals(namePrefix)) {
|
||||||
|
List<Node> scenes = (List<Node>) linkedFeatures.get("scenes");
|
||||||
|
if(scenes != null) {
|
||||||
|
for(Node scene : scenes) {
|
||||||
|
if(featureName.equals(scene.getName())) {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("OB".equals(namePrefix)) {
|
||||||
|
List<Node> features = (List<Node>) linkedFeatures.get("objects");
|
||||||
|
if(features != null) {
|
||||||
|
for(Node feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("ME".equals(namePrefix)) {
|
||||||
|
List<TemporalMesh> temporalMeshes = (List<TemporalMesh>) linkedFeatures.get("meshes");
|
||||||
|
if(temporalMeshes != null) {
|
||||||
|
for(TemporalMesh temporalMesh : temporalMeshes) {
|
||||||
|
if(featureName.equals(temporalMesh.getName())) {
|
||||||
|
return temporalMesh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("MA".equals(namePrefix)) {
|
||||||
|
List<MaterialContext> features = (List<MaterialContext>) linkedFeatures.get("materials");
|
||||||
|
if(features != null) {
|
||||||
|
for(MaterialContext feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("TX".equals(namePrefix)) {
|
||||||
|
List<Texture> features = (List<Texture>) linkedFeatures.get("textures");
|
||||||
|
if(features != null) {
|
||||||
|
for(Texture feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("IM".equals(namePrefix)) {
|
||||||
|
List<Texture> features = (List<Texture>) linkedFeatures.get("images");
|
||||||
|
if(features != null) {
|
||||||
|
for(Texture feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("AC".equals(namePrefix)) {
|
||||||
|
List<Animation> features = (List<Animation>) linkedFeatures.get("animations");
|
||||||
|
if(features != null) {
|
||||||
|
for(Animation feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("CA".equals(namePrefix)) {
|
||||||
|
List<Camera> features = (List<Camera>) linkedFeatures.get("cameras");
|
||||||
|
if(features != null) {
|
||||||
|
for(Camera feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("LA".equals(namePrefix)) {
|
||||||
|
List<Light> features = (List<Light>) linkedFeatures.get("lights");
|
||||||
|
if(features != null) {
|
||||||
|
for(Light feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("FI".equals(featureName)) {
|
||||||
|
List<Filter> features = (List<Filter>) linkedFeatures.get("lights");
|
||||||
|
if(features != null) {
|
||||||
|
for(Filter feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return all linked features for the current blend file
|
||||||
|
*/
|
||||||
|
public Map<String, Map<String, Object>> getLinkedFeatures() {
|
||||||
|
return linkedFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds the structure to the parent stack.
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* the structure to be added to the stack
|
||||||
|
*/
|
||||||
|
public void pushParent(Structure parent) {
|
||||||
|
parentStack.push(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method removes the structure from the top of the parent's stack.
|
||||||
|
*
|
||||||
|
* @return the structure that was removed from the stack
|
||||||
|
*/
|
||||||
|
public Structure popParent() {
|
||||||
|
try {
|
||||||
|
return parentStack.pop();
|
||||||
|
} catch (EmptyStackException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method retreives the structure at the top of the parent's stack but
|
||||||
|
* does not remove it.
|
||||||
|
*
|
||||||
|
* @return the structure from the top of the stack
|
||||||
|
*/
|
||||||
|
public Structure peekParent() {
|
||||||
|
try {
|
||||||
|
return parentStack.peek();
|
||||||
|
} catch (EmptyStackException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds a new modifier to the list.
|
||||||
|
*
|
||||||
|
* @param ownerOMA
|
||||||
|
* the owner's old memory address
|
||||||
|
* @param constraints
|
||||||
|
* the object's constraints
|
||||||
|
*/
|
||||||
|
public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
|
||||||
|
List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
|
||||||
|
if (objectConstraints == null) {
|
||||||
|
objectConstraints = new ArrayList<Constraint>();
|
||||||
|
this.constraints.put(ownerOMA, objectConstraints);
|
||||||
|
}
|
||||||
|
objectConstraints.addAll(constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns constraints applied to the feature of the given OMA.
|
||||||
|
* @param ownerOMA
|
||||||
|
* the constraints' owner OMA
|
||||||
|
* @return a list of constraints or <b>null</b> if no constraints are applied to the feature
|
||||||
|
*/
|
||||||
|
public List<Constraint> getConstraints(Long ownerOMA) {
|
||||||
|
return constraints.get(ownerOMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return all available constraints
|
||||||
|
*/
|
||||||
|
public List<Constraint> getAllConstraints() {
|
||||||
|
List<Constraint> result = new ArrayList<Constraint>();
|
||||||
|
for (Entry<Long, List<Constraint>> entry : constraints.entrySet()) {
|
||||||
|
result.addAll(entry.getValue());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds the animation for the specified OMA of its owner.
|
||||||
|
*
|
||||||
|
* @param ownerOMA
|
||||||
|
* the owner's old memory address
|
||||||
|
* @param animation
|
||||||
|
* the animation for the feature specified by ownerOMA
|
||||||
|
*/
|
||||||
|
public void addAnimation(Long ownerOMA, Animation animation) {
|
||||||
|
List<Animation> animList = animations.get(ownerOMA);
|
||||||
|
if (animList == null) {
|
||||||
|
animList = new ArrayList<Animation>();
|
||||||
|
animations.put(ownerOMA, animList);
|
||||||
|
}
|
||||||
|
animList.add(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the animation data for the specified owner.
|
||||||
|
*
|
||||||
|
* @param ownerOMA
|
||||||
|
* the old memory address of the animation data owner
|
||||||
|
* @return the animation or null if none exists
|
||||||
|
*/
|
||||||
|
public List<Animation> getAnimations(Long ownerOMA) {
|
||||||
|
return animations.get(ownerOMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the skeleton for the specified OMA of its owner.
|
||||||
|
*
|
||||||
|
* @param skeletonOMA
|
||||||
|
* the skeleton's old memory address
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton specified by the given OMA
|
||||||
|
*/
|
||||||
|
public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
|
||||||
|
skeletons.put(skeletonOMA, skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method stores a binding between the skeleton and the proper armature
|
||||||
|
* node.
|
||||||
|
*
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton
|
||||||
|
* @param node
|
||||||
|
* the armature node
|
||||||
|
*/
|
||||||
|
public void setNodeForSkeleton(Skeleton skeleton, Node node) {
|
||||||
|
nodesWithSkeletons.put(skeleton, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the armature node that is defined for the skeleton.
|
||||||
|
*
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton
|
||||||
|
* @return the armature node that defines the skeleton in blender
|
||||||
|
*/
|
||||||
|
public Node getControlledNode(Skeleton skeleton) {
|
||||||
|
return nodesWithSkeletons.get(skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the skeleton for the specified OMA of its owner.
|
||||||
|
*
|
||||||
|
* @param skeletonOMA
|
||||||
|
* the skeleton's old memory address
|
||||||
|
* @return the skeleton specified by the given OMA
|
||||||
|
*/
|
||||||
|
public Skeleton getSkeleton(Long skeletonOMA) {
|
||||||
|
return skeletons.get(skeletonOMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the bone context for the given bone old memory address.
|
||||||
|
* If the context is already set it will be replaced.
|
||||||
|
*
|
||||||
|
* @param boneOMA
|
||||||
|
* the bone's old memory address
|
||||||
|
* @param boneContext
|
||||||
|
* the bones's context
|
||||||
|
*/
|
||||||
|
public void setBoneContext(Long boneOMA, BoneContext boneContext) {
|
||||||
|
boneContexts.put(boneOMA, boneContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the bone context for the given bone old memory
|
||||||
|
* address. If no context exists then <b>null</b> is returned.
|
||||||
|
*
|
||||||
|
* @param boneOMA
|
||||||
|
* the bone's old memory address
|
||||||
|
* @return bone's context
|
||||||
|
*/
|
||||||
|
public BoneContext getBoneContext(Long boneOMA) {
|
||||||
|
return boneContexts.get(boneOMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns bone by given name.
|
||||||
|
*
|
||||||
|
* @param skeletonOMA
|
||||||
|
* the OMA of the skeleton where the bone will be searched
|
||||||
|
* @param name
|
||||||
|
* the name of the bone
|
||||||
|
* @return found bone or null if none bone of a given name exists
|
||||||
|
*/
|
||||||
|
public BoneContext getBoneByName(Long skeletonOMA, String name) {
|
||||||
|
for (Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
|
||||||
|
if (entry.getValue().getArmatureObjectOMA().equals(skeletonOMA)) {
|
||||||
|
Bone bone = entry.getValue().getBone();
|
||||||
|
if (bone != null && name.equals(bone.getName())) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns bone context for the given bone.
|
||||||
|
*
|
||||||
|
* @param bone
|
||||||
|
* the bone
|
||||||
|
* @return the bone's bone context
|
||||||
|
*/
|
||||||
|
public BoneContext getBoneContext(Bone bone) {
|
||||||
|
for (Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
|
||||||
|
if (entry.getValue().getBone().getName().equals(bone.getName())) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Cannot find context for bone: " + bone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This metod returns the default material.
|
||||||
|
*
|
||||||
|
* @return the default material
|
||||||
|
*/
|
||||||
|
public synchronized Material getDefaultMaterial() {
|
||||||
|
if (blenderKey.getDefaultMaterial() == null) {
|
||||||
|
Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
|
||||||
|
blenderKey.setDefaultMaterial(defaultMaterial);
|
||||||
|
}
|
||||||
|
return blenderKey.getDefaultMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a custom marker for scene's feature.
|
||||||
|
*
|
||||||
|
* @param marker
|
||||||
|
* the marker name
|
||||||
|
* @param feature
|
||||||
|
* te scene's feature (can be node, material or texture or
|
||||||
|
* anything else)
|
||||||
|
* @param markerValue
|
||||||
|
* the marker value
|
||||||
|
*/
|
||||||
|
public void addMarker(String marker, Object feature, Object markerValue) {
|
||||||
|
if (markerValue == null) {
|
||||||
|
throw new IllegalArgumentException("The marker's value cannot be null.");
|
||||||
|
}
|
||||||
|
Map<Object, Object> markersMap = markers.get(marker);
|
||||||
|
if (markersMap == null) {
|
||||||
|
markersMap = new HashMap<Object, Object>();
|
||||||
|
markers.put(marker, markersMap);
|
||||||
|
}
|
||||||
|
markersMap.put(feature, markerValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the marker value. The returned value is null if no marker was
|
||||||
|
* defined for the given feature.
|
||||||
|
*
|
||||||
|
* @param marker
|
||||||
|
* the marker name
|
||||||
|
* @param feature
|
||||||
|
* the scene's feature
|
||||||
|
* @return marker value or null if it was not defined
|
||||||
|
*/
|
||||||
|
public Object getMarkerValue(String marker, Object feature) {
|
||||||
|
Map<Object, Object> markersMap = markers.get(marker);
|
||||||
|
return markersMap == null ? null : markersMap.get(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds blender action to the context.
|
||||||
|
* @param action
|
||||||
|
* the action loaded from the blend file
|
||||||
|
*/
|
||||||
|
public void addAction(BlenderAction action) {
|
||||||
|
actions.put(action.getName(), action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a map of blender actions; the key is the action name and the value is action itself
|
||||||
|
*/
|
||||||
|
public Map<String, BlenderAction> getActions() {
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enum defines what loaded data type user wants to retreive. It can be
|
||||||
|
* either filled structure or already converted data.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public static enum LoadedDataType {
|
||||||
|
STRUCTURE, FEATURE, TEMPORAL_MESH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,417 @@
|
|||||||
|
/*
|
||||||
|
* 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.scene.plugins.blender;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.animation.Animation;
|
||||||
|
import com.jme3.asset.AssetInfo;
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetLoader;
|
||||||
|
import com.jme3.asset.AssetLocator;
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.asset.BlenderKey;
|
||||||
|
import com.jme3.asset.ModelKey;
|
||||||
|
import com.jme3.asset.StreamAssetInfo;
|
||||||
|
import com.jme3.light.Light;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.post.Filter;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.scene.CameraNode;
|
||||||
|
import com.jme3.scene.LightNode;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.AnimationHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.cameras.CameraHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.curves.CurvesHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||||
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||||
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.landscape.LandscapeHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.lights.LightHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||||
|
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.meshes.MeshHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
|
||||||
|
import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.textures.TextureHelper;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class BlenderLoader implements AssetLoader {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spatial load(AssetInfo assetInfo) throws IOException {
|
||||||
|
try {
|
||||||
|
BlenderContext blenderContext = this.setup(assetInfo);
|
||||||
|
|
||||||
|
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
|
||||||
|
animationHelper.loadAnimations();
|
||||||
|
|
||||||
|
BlenderKey blenderKey = blenderContext.getBlenderKey();
|
||||||
|
LoadedFeatures loadedFeatures = new LoadedFeatures();
|
||||||
|
for (FileBlockHeader block : blenderContext.getBlocks()) {
|
||||||
|
switch (block.getCode()) {
|
||||||
|
case BLOCK_OB00:
|
||||||
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
|
Node object = (Node) objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
|
||||||
|
if (LOGGER.isLoggable(Level.FINE)) {
|
||||||
|
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() });
|
||||||
|
}
|
||||||
|
if (object.getParent() == null) {
|
||||||
|
loadedFeatures.objects.add(object);
|
||||||
|
}
|
||||||
|
if (object instanceof LightNode && ((LightNode) object).getLight() != null) {
|
||||||
|
loadedFeatures.lights.add(((LightNode) object).getLight());
|
||||||
|
} else if (object instanceof CameraNode && ((CameraNode) object).getCamera() != null) {
|
||||||
|
loadedFeatures.cameras.add(((CameraNode) object).getCamera());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BLOCK_SC00:// Scene
|
||||||
|
loadedFeatures.sceneBlocks.add(block);
|
||||||
|
break;
|
||||||
|
case BLOCK_MA00:// Material
|
||||||
|
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||||
|
MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
|
||||||
|
loadedFeatures.materials.add(materialContext);
|
||||||
|
break;
|
||||||
|
case BLOCK_ME00:// Mesh
|
||||||
|
MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
|
||||||
|
TemporalMesh temporalMesh = meshHelper.toTemporalMesh(block.getStructure(blenderContext), blenderContext);
|
||||||
|
loadedFeatures.meshes.add(temporalMesh);
|
||||||
|
break;
|
||||||
|
case BLOCK_IM00:// Image
|
||||||
|
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
|
||||||
|
Texture image = textureHelper.loadImageAsTexture(block.getStructure(blenderContext), 0, blenderContext);
|
||||||
|
if (image != null && image.getImage() != null) {// render results are stored as images but are not being loaded
|
||||||
|
loadedFeatures.images.add(image);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BLOCK_TE00:
|
||||||
|
Structure textureStructure = block.getStructure(blenderContext);
|
||||||
|
int type = ((Number) textureStructure.getFieldValue("type")).intValue();
|
||||||
|
if (type == TextureHelper.TEX_IMAGE) {
|
||||||
|
TextureHelper texHelper = blenderContext.getHelper(TextureHelper.class);
|
||||||
|
Texture texture = texHelper.getTexture(textureStructure, null, blenderContext);
|
||||||
|
if (texture != null) {// null is returned when texture has no image
|
||||||
|
loadedFeatures.textures.add(texture);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.fine("Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BLOCK_WO00:// World
|
||||||
|
LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class);
|
||||||
|
Structure worldStructure = block.getStructure(blenderContext);
|
||||||
|
|
||||||
|
String worldName = worldStructure.getName();
|
||||||
|
if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
|
||||||
|
|
||||||
|
Light ambientLight = landscapeHelper.toAmbientLight(worldStructure);
|
||||||
|
if (ambientLight != null) {
|
||||||
|
loadedFeatures.objects.add(new LightNode(null, ambientLight));
|
||||||
|
loadedFeatures.lights.add(ambientLight);
|
||||||
|
}
|
||||||
|
loadedFeatures.sky = landscapeHelper.toSky(worldStructure);
|
||||||
|
loadedFeatures.backgroundColor = landscapeHelper.toBackgroundColor(worldStructure);
|
||||||
|
|
||||||
|
Filter fogFilter = landscapeHelper.toFog(worldStructure);
|
||||||
|
if (fogFilter != null) {
|
||||||
|
loadedFeatures.filters.add(landscapeHelper.toFog(worldStructure));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BLOCK_AC00:
|
||||||
|
LOGGER.fine("Loading unlinked animations is not yet supported!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGGER.log(Level.FINEST, "Ommiting the block: {0}.", block.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.fine("Baking constraints after every feature is loaded.");
|
||||||
|
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
|
constraintHelper.bakeConstraints(blenderContext);
|
||||||
|
|
||||||
|
LOGGER.fine("Loading scenes and attaching them to the root object.");
|
||||||
|
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
|
||||||
|
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
|
||||||
|
Node modelRoot = new Node(blenderKey.getName());
|
||||||
|
for (Node scene : loadedFeatures.scenes) {
|
||||||
|
modelRoot.attachChild(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blenderKey.isLoadUnlinkedAssets()) {
|
||||||
|
LOGGER.fine("Setting loaded content as user data in resulting sptaial.");
|
||||||
|
Map<String, Map<String, Object>> linkedData = new HashMap<String, Map<String, Object>>();
|
||||||
|
|
||||||
|
Map<String, Object> thisFileData = new HashMap<String, Object>();
|
||||||
|
thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList<Object>() : loadedFeatures.scenes);
|
||||||
|
thisFileData.put("objects", loadedFeatures.objects == null ? new ArrayList<Object>() : loadedFeatures.objects);
|
||||||
|
thisFileData.put("meshes", loadedFeatures.meshes == null ? new ArrayList<Object>() : loadedFeatures.meshes);
|
||||||
|
thisFileData.put("materials", loadedFeatures.materials == null ? new ArrayList<Object>() : loadedFeatures.materials);
|
||||||
|
thisFileData.put("textures", loadedFeatures.textures == null ? new ArrayList<Object>() : loadedFeatures.textures);
|
||||||
|
thisFileData.put("images", loadedFeatures.images == null ? new ArrayList<Object>() : loadedFeatures.images);
|
||||||
|
thisFileData.put("animations", loadedFeatures.animations == null ? new ArrayList<Object>() : loadedFeatures.animations);
|
||||||
|
thisFileData.put("cameras", loadedFeatures.cameras == null ? new ArrayList<Object>() : loadedFeatures.cameras);
|
||||||
|
thisFileData.put("lights", loadedFeatures.lights == null ? new ArrayList<Object>() : loadedFeatures.lights);
|
||||||
|
thisFileData.put("filters", loadedFeatures.filters == null ? new ArrayList<Object>() : loadedFeatures.filters);
|
||||||
|
thisFileData.put("backgroundColor", loadedFeatures.backgroundColor);
|
||||||
|
thisFileData.put("sky", loadedFeatures.sky);
|
||||||
|
|
||||||
|
linkedData.put("this", thisFileData);
|
||||||
|
linkedData.putAll(blenderContext.getLinkedFeatures());
|
||||||
|
|
||||||
|
modelRoot.setUserData("linkedData", linkedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return modelRoot;
|
||||||
|
} catch (BlenderFileException e) {
|
||||||
|
throw new IOException(e.getLocalizedMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
|
||||||
|
} finally {
|
||||||
|
this.clear(assetInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts the given structure to a scene node.
|
||||||
|
* @param structure
|
||||||
|
* structure of a scene
|
||||||
|
* @param blenderContext the blender context
|
||||||
|
* @return scene's node
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception throw when problems with blender file occur
|
||||||
|
*/
|
||||||
|
private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
|
Node result = new Node(structure.getName());
|
||||||
|
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
|
||||||
|
for (Structure b : base) {
|
||||||
|
Pointer pObject = (Pointer) b.getFieldValue("object");
|
||||||
|
if (pObject.isNotNull()) {
|
||||||
|
Structure objectStructure = pObject.fetchData().get(0);
|
||||||
|
|
||||||
|
Object object = objectHelper.toObject(objectStructure, blenderContext);
|
||||||
|
if (object instanceof LightNode) {
|
||||||
|
result.addLight(((LightNode) object).getLight());// FIXME: check if this is needed !!!
|
||||||
|
result.attachChild((LightNode) object);
|
||||||
|
} else if (object instanceof Node) {
|
||||||
|
if (LOGGER.isLoggable(Level.FINE)) {
|
||||||
|
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
|
||||||
|
}
|
||||||
|
if (((Node) object).getParent() == null) {
|
||||||
|
result.attachChild((Spatial) object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets up the loader.
|
||||||
|
* @param assetInfo
|
||||||
|
* the asset info
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is throw when something wrong happens with blender file
|
||||||
|
*/
|
||||||
|
protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException {
|
||||||
|
// registering loaders
|
||||||
|
ModelKey modelKey = (ModelKey) assetInfo.getKey();
|
||||||
|
BlenderKey blenderKey;
|
||||||
|
if (modelKey instanceof BlenderKey) {
|
||||||
|
blenderKey = (BlenderKey) modelKey;
|
||||||
|
} else {
|
||||||
|
blenderKey = new BlenderKey(modelKey.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// opening stream
|
||||||
|
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
|
||||||
|
|
||||||
|
// reading blocks
|
||||||
|
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
|
||||||
|
FileBlockHeader fileBlock;
|
||||||
|
BlenderContext blenderContext = new BlenderContext();
|
||||||
|
blenderContext.setBlenderVersion(inputStream.getVersionNumber());
|
||||||
|
blenderContext.setAssetManager(assetInfo.getManager());
|
||||||
|
blenderContext.setInputStream(inputStream);
|
||||||
|
blenderContext.setBlenderKey(blenderKey);
|
||||||
|
|
||||||
|
// creating helpers
|
||||||
|
blenderContext.putHelper(AnimationHelper.class, new AnimationHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext));
|
||||||
|
|
||||||
|
// reading the blocks (dna block is automatically saved in the blender context when found)
|
||||||
|
FileBlockHeader sceneFileBlock = null;
|
||||||
|
do {
|
||||||
|
fileBlock = new FileBlockHeader(inputStream, blenderContext);
|
||||||
|
if (!fileBlock.isDnaBlock()) {
|
||||||
|
blocks.add(fileBlock);
|
||||||
|
// save the scene's file block
|
||||||
|
if (fileBlock.getCode() == BlockCode.BLOCK_SC00) {
|
||||||
|
sceneFileBlock = fileBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!fileBlock.isLastBlock());
|
||||||
|
if (sceneFileBlock != null) {
|
||||||
|
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding locator for linked content
|
||||||
|
assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
|
||||||
|
|
||||||
|
return blenderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal data is only needed during loading so make it unreachable so that the GC can release
|
||||||
|
* that memory (which can be quite large amount).
|
||||||
|
*/
|
||||||
|
protected void clear(AssetInfo assetInfo) {
|
||||||
|
assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds the loading results according to the given loading flag.
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
private static class LoadedFeatures {
|
||||||
|
private List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
|
||||||
|
/** The scenes from the file. */
|
||||||
|
private List<Node> scenes = new ArrayList<Node>();
|
||||||
|
/** Objects from all scenes. */
|
||||||
|
private List<Node> objects = new ArrayList<Node>();
|
||||||
|
/** All meshes. */
|
||||||
|
private List<TemporalMesh> meshes = new ArrayList<TemporalMesh>();
|
||||||
|
/** Materials from all objects. */
|
||||||
|
private List<MaterialContext> materials = new ArrayList<MaterialContext>();
|
||||||
|
/** Textures from all objects. */
|
||||||
|
private List<Texture> textures = new ArrayList<Texture>();
|
||||||
|
/** The images stored in the blender file. */
|
||||||
|
private List<Texture> images = new ArrayList<Texture>();
|
||||||
|
/** Animations of all objects. */
|
||||||
|
private List<Animation> animations = new ArrayList<Animation>();
|
||||||
|
/** All cameras from the file. */
|
||||||
|
private List<Camera> cameras = new ArrayList<Camera>();
|
||||||
|
/** All lights from the file. */
|
||||||
|
private List<Light> lights = new ArrayList<Light>();
|
||||||
|
/** Loaded sky. */
|
||||||
|
private Spatial sky;
|
||||||
|
/** Scene filters (ie. FOG). */
|
||||||
|
private List<Filter> filters = new ArrayList<Filter>();
|
||||||
|
/**
|
||||||
|
* The background color of the render loaded from the horizon color of the world. If no world is used than the gray color
|
||||||
|
* is set to default (as in blender editor.
|
||||||
|
*/
|
||||||
|
private ColorRGBA backgroundColor = ColorRGBA.Gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LinkedContentLocator implements AssetLocator {
|
||||||
|
private File rootFolder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRootPath(String rootPath) {
|
||||||
|
rootFolder = new File(rootPath);
|
||||||
|
if(rootFolder.isFile()) {
|
||||||
|
rootFolder = rootFolder.getParentFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Override
|
||||||
|
public AssetInfo locate(AssetManager manager, AssetKey key) {
|
||||||
|
if(key instanceof BlenderKey) {
|
||||||
|
File linkedAbsoluteFile = new File(key.getName());
|
||||||
|
if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) {
|
||||||
|
try {
|
||||||
|
return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName());
|
||||||
|
if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) {
|
||||||
|
try {
|
||||||
|
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName());
|
||||||
|
if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) {
|
||||||
|
try {
|
||||||
|
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2019 jMonkeyEngine
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -29,18 +29,12 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
package com.jme3.renderer.opengl;
|
package com.jme3.scene.plugins.blender;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GL functions and constants only available on vanilla OpenGL ES 3.0.
|
* This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
|
||||||
*
|
* @deprecated this class is deprecated; use BlenderLoader instead
|
||||||
* @author Jesus Oliver
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public interface GLES_30 extends GL {
|
public class BlenderModelLoader extends BlenderLoader {
|
||||||
|
|
||||||
public static final int GL_RGB10_A2 = 0x8059;
|
|
||||||
public static final int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,391 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.animations;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimControl;
|
||||||
|
import com.jme3.animation.Animation;
|
||||||
|
import com.jme3.animation.BoneTrack;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.animation.SkeletonControl;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.asset.BlenderKey.AnimationMatchMethod;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.Ipo.ConstIpo;
|
||||||
|
import com.jme3.scene.plugins.blender.curves.BezierCurve;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||||
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||||
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The helper class that helps in animations loading.
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class AnimationHelper extends AbstractBlenderHelper {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(AnimationHelper.class.getName());
|
||||||
|
|
||||||
|
public AnimationHelper(String blenderVersion, BlenderContext blenderContext) {
|
||||||
|
super(blenderVersion, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all animations that are stored in the blender file. The animations are not yet applied to the scene features.
|
||||||
|
* This should be called before objects are loaded.
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when problems with blender file reading occur
|
||||||
|
*/
|
||||||
|
public void loadAnimations() throws BlenderFileException {
|
||||||
|
LOGGER.info("Loading animations that will be later applied to scene features.");
|
||||||
|
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(BlockCode.BLOCK_AC00);
|
||||||
|
if (actionHeaders != null) {
|
||||||
|
for (FileBlockHeader header : actionHeaders) {
|
||||||
|
Structure actionStructure = header.getStructure(blenderContext);
|
||||||
|
LOGGER.log(Level.INFO, "Found animation: {0}.", actionStructure.getName());
|
||||||
|
blenderContext.addAction(this.getTracks(actionStructure, blenderContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method applies animations to the given node. The names of the animations should be the same as actions names in the blender file.
|
||||||
|
* @param node
|
||||||
|
* the node to whom the animations will be applied
|
||||||
|
* @param animationMatchMethod
|
||||||
|
* the way animation should be matched with node
|
||||||
|
*/
|
||||||
|
public void applyAnimations(Node node, AnimationMatchMethod animationMatchMethod) {
|
||||||
|
List<BlenderAction> actions = this.getActions(node, animationMatchMethod);
|
||||||
|
if (actions.size() > 0) {
|
||||||
|
List<Animation> animations = new ArrayList<Animation>();
|
||||||
|
for (BlenderAction action : actions) {
|
||||||
|
SpatialTrack[] tracks = action.toTracks(node, blenderContext);
|
||||||
|
if (tracks != null && tracks.length > 0) {
|
||||||
|
Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime());
|
||||||
|
spatialAnimation.setTracks(tracks);
|
||||||
|
animations.add(spatialAnimation);
|
||||||
|
blenderContext.addAnimation((Long) node.getUserData(ObjectHelper.OMA_MARKER), spatialAnimation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animations.size() > 0) {
|
||||||
|
AnimControl control = new AnimControl();
|
||||||
|
HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size());
|
||||||
|
for (int i = 0; i < animations.size(); ++i) {
|
||||||
|
Animation animation = animations.get(i);
|
||||||
|
anims.put(animation.getName(), animation);
|
||||||
|
}
|
||||||
|
control.setAnimations(anims);
|
||||||
|
node.addControl(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method applies skeleton animations to the given node.
|
||||||
|
* @param node
|
||||||
|
* the node where the animations will be applied
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton of the node
|
||||||
|
* @param animationMatchMethod
|
||||||
|
* the way animation should be matched with skeleton
|
||||||
|
*/
|
||||||
|
public void applyAnimations(Node node, Skeleton skeleton, AnimationMatchMethod animationMatchMethod) {
|
||||||
|
node.addControl(new SkeletonControl(skeleton));
|
||||||
|
blenderContext.setNodeForSkeleton(skeleton, node);
|
||||||
|
List<BlenderAction> actions = this.getActions(skeleton, animationMatchMethod);
|
||||||
|
|
||||||
|
if (actions.size() > 0) {
|
||||||
|
List<Animation> animations = new ArrayList<Animation>();
|
||||||
|
for (BlenderAction action : actions) {
|
||||||
|
BoneTrack[] tracks = action.toTracks(skeleton, blenderContext);
|
||||||
|
if (tracks != null && tracks.length > 0) {
|
||||||
|
Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime());
|
||||||
|
boneAnimation.setTracks(tracks);
|
||||||
|
animations.add(boneAnimation);
|
||||||
|
Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
|
||||||
|
blenderContext.addAnimation(animatedNodeOMA, boneAnimation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (animations.size() > 0) {
|
||||||
|
AnimControl control = new AnimControl(skeleton);
|
||||||
|
HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size());
|
||||||
|
for (int i = 0; i < animations.size(); ++i) {
|
||||||
|
Animation animation = animations.get(i);
|
||||||
|
anims.put(animation.getName(), animation);
|
||||||
|
}
|
||||||
|
control.setAnimations(anims);
|
||||||
|
node.addControl(control);
|
||||||
|
|
||||||
|
// make sure that SkeletonControl is added AFTER the AnimControl
|
||||||
|
SkeletonControl skeletonControl = node.getControl(SkeletonControl.class);
|
||||||
|
if (skeletonControl != null) {
|
||||||
|
node.removeControl(SkeletonControl.class);
|
||||||
|
node.addControl(skeletonControl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates an ipo object used for interpolation calculations.
|
||||||
|
*
|
||||||
|
* @param ipoStructure
|
||||||
|
* the structure with ipo definition
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return the ipo object
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* this exception is thrown when the blender file is somehow
|
||||||
|
* corrupted
|
||||||
|
*/
|
||||||
|
public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
|
||||||
|
|
||||||
|
// preparing bezier curves
|
||||||
|
Ipo result = null;
|
||||||
|
List<Structure> curves = curvebase.evaluateListBase();// IpoCurve
|
||||||
|
if (curves.size() > 0) {
|
||||||
|
BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
|
||||||
|
int frame = 0;
|
||||||
|
for (Structure curve : curves) {
|
||||||
|
Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
|
||||||
|
List<Structure> bezTriples = pBezTriple.fetchData();
|
||||||
|
int type = ((Number) curve.getFieldValue("adrcode")).intValue();
|
||||||
|
bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
|
||||||
|
}
|
||||||
|
curves.clear();
|
||||||
|
result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
|
||||||
|
Long ipoOma = ipoStructure.getOldMemoryAddress();
|
||||||
|
blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.STRUCTURE, ipoStructure);
|
||||||
|
blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.FEATURE, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates an ipo with only a single value. No track type is
|
||||||
|
* specified so do not use it for calculating tracks.
|
||||||
|
*
|
||||||
|
* @param constValue
|
||||||
|
* the value of this ipo
|
||||||
|
* @return constant ipo
|
||||||
|
*/
|
||||||
|
public Ipo fromValue(float constValue) {
|
||||||
|
return new ConstIpo(constValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method retuns the bone tracks for animation.
|
||||||
|
*
|
||||||
|
* @param actionStructure
|
||||||
|
* the structure containing the tracks
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return a list of tracks for the specified animation
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when there are problems with the blend
|
||||||
|
* file
|
||||||
|
*/
|
||||||
|
private BlenderAction getTracks(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
if (blenderVersion < 250) {
|
||||||
|
return this.getTracks249(actionStructure, blenderContext);
|
||||||
|
} else {
|
||||||
|
return this.getTracks250(actionStructure, blenderContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method retuns the bone tracks for animation for blender version 2.50
|
||||||
|
* and higher.
|
||||||
|
*
|
||||||
|
* @param actionStructure
|
||||||
|
* the structure containing the tracks
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return a list of tracks for the specified animation
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when there are problems with the blend
|
||||||
|
* file
|
||||||
|
*/
|
||||||
|
private BlenderAction getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
LOGGER.log(Level.FINE, "Getting tracks!");
|
||||||
|
Structure groups = (Structure) actionStructure.getFieldValue("groups");
|
||||||
|
List<Structure> actionGroups = groups.evaluateListBase();// bActionGroup
|
||||||
|
BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps());
|
||||||
|
int lastFrame = 1;
|
||||||
|
for (Structure actionGroup : actionGroups) {
|
||||||
|
String name = actionGroup.getFieldValue("name").toString();
|
||||||
|
List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase();
|
||||||
|
BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
|
||||||
|
int channelCounter = 0;
|
||||||
|
for (Structure c : channels) {
|
||||||
|
int type = this.getCurveType(c, blenderContext);
|
||||||
|
Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
|
||||||
|
List<Structure> bezTriples = pBezTriple.fetchData();
|
||||||
|
bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
|
||||||
|
lastFrame = Math.max(lastFrame, ipo.getLastFrame());
|
||||||
|
blenderAction.featuresTracks.put(name, ipo);
|
||||||
|
}
|
||||||
|
blenderAction.stopFrame = lastFrame;
|
||||||
|
return blenderAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method retuns the bone tracks for animation for blender version 2.49
|
||||||
|
* (and probably several lower versions too).
|
||||||
|
*
|
||||||
|
* @param actionStructure
|
||||||
|
* the structure containing the tracks
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return a list of tracks for the specified animation
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when there are problems with the blend
|
||||||
|
* file
|
||||||
|
*/
|
||||||
|
private BlenderAction getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
LOGGER.log(Level.FINE, "Getting tracks!");
|
||||||
|
Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
|
||||||
|
List<Structure> actionChannels = chanbase.evaluateListBase();// bActionChannel
|
||||||
|
BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps());
|
||||||
|
int lastFrame = 1;
|
||||||
|
for (Structure bActionChannel : actionChannels) {
|
||||||
|
String animatedFeatureName = bActionChannel.getFieldValue("name").toString();
|
||||||
|
Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
|
||||||
|
if (!p.isNull()) {
|
||||||
|
Structure ipoStructure = p.fetchData().get(0);
|
||||||
|
Ipo ipo = this.fromIpoStructure(ipoStructure, blenderContext);
|
||||||
|
if (ipo != null) {// this can happen when ipo with no curves appear in blender file
|
||||||
|
lastFrame = Math.max(lastFrame, ipo.getLastFrame());
|
||||||
|
blenderAction.featuresTracks.put(animatedFeatureName, ipo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blenderAction.stopFrame = lastFrame;
|
||||||
|
return blenderAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the type of the ipo curve.
|
||||||
|
*
|
||||||
|
* @param structure
|
||||||
|
* the structure must contain the 'rna_path' field and
|
||||||
|
* 'array_index' field (the type is not important here)
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return the type of the curve
|
||||||
|
*/
|
||||||
|
public int getCurveType(Structure structure, BlenderContext blenderContext) {
|
||||||
|
// reading rna path first
|
||||||
|
BlenderInputStream bis = blenderContext.getInputStream();
|
||||||
|
int currentPosition = bis.getPosition();
|
||||||
|
Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
|
||||||
|
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
|
||||||
|
bis.setPosition(dataFileBlock.getBlockPosition());
|
||||||
|
String rnaPath = bis.readString();
|
||||||
|
bis.setPosition(currentPosition);
|
||||||
|
int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
|
||||||
|
|
||||||
|
// determining the curve type
|
||||||
|
if (rnaPath.endsWith("location")) {
|
||||||
|
return Ipo.AC_LOC_X + arrayIndex;
|
||||||
|
}
|
||||||
|
if (rnaPath.endsWith("rotation_quaternion")) {
|
||||||
|
return Ipo.AC_QUAT_W + arrayIndex;
|
||||||
|
}
|
||||||
|
if (rnaPath.endsWith("scale")) {
|
||||||
|
return Ipo.AC_SIZE_X + arrayIndex;
|
||||||
|
}
|
||||||
|
if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
|
||||||
|
return Ipo.OB_ROT_X + arrayIndex;
|
||||||
|
}
|
||||||
|
LOGGER.log(Level.WARNING, "Unknown curve rna path: {0}", rnaPath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method returns the actions for the given skeleton. The actions represent armature animation in blender.
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton we fetch the actions for
|
||||||
|
* @param animationMatchMethod
|
||||||
|
* the method of animation matching
|
||||||
|
* @return a list of animations for the specified skeleton
|
||||||
|
*/
|
||||||
|
private List<BlenderAction> getActions(Skeleton skeleton, AnimationMatchMethod animationMatchMethod) {
|
||||||
|
List<BlenderAction> result = new ArrayList<BlenderAction>();
|
||||||
|
|
||||||
|
// first get a set of bone names
|
||||||
|
Set<String> boneNames = new HashSet<String>();
|
||||||
|
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
|
||||||
|
String boneName = skeleton.getBone(i).getName();
|
||||||
|
if (boneName != null && boneName.length() > 0) {
|
||||||
|
boneNames.add(skeleton.getBone(i).getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finding matches
|
||||||
|
Set<String> matchingNames = new HashSet<String>();
|
||||||
|
for (Entry<String, BlenderAction> actionEntry : blenderContext.getActions().entrySet()) {
|
||||||
|
// compute how many action tracks match the skeleton bones' names
|
||||||
|
for (String boneName : boneNames) {
|
||||||
|
if (actionEntry.getValue().hasTrackName(boneName)) {
|
||||||
|
matchingNames.add(boneName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlenderAction action = null;
|
||||||
|
if (animationMatchMethod == AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH && matchingNames.size() > 0) {
|
||||||
|
action = actionEntry.getValue();
|
||||||
|
} else if (matchingNames.size() == actionEntry.getValue().getTracksCount()) {
|
||||||
|
action = actionEntry.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action != null) {
|
||||||
|
// remove the tracks that do not match the bone names if the matching method is different from ALL_NAMES_MATCH
|
||||||
|
if (animationMatchMethod != AnimationMatchMethod.ALL_NAMES_MATCH) {
|
||||||
|
action = action.clone();
|
||||||
|
action.removeTracksThatAreNotInTheCollection(matchingNames);
|
||||||
|
}
|
||||||
|
result.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
matchingNames.clear();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method returns the actions for the given node. The actions represent object animation in blender.
|
||||||
|
* @param node
|
||||||
|
* the node we fetch the actions for
|
||||||
|
* @param animationMatchMethod
|
||||||
|
* the method of animation matching
|
||||||
|
* @return a list of animations for the specified node
|
||||||
|
*/
|
||||||
|
private List<BlenderAction> getActions(Node node, AnimationMatchMethod animationMatchMethod) {
|
||||||
|
List<BlenderAction> result = new ArrayList<BlenderAction>();
|
||||||
|
|
||||||
|
for (Entry<String, BlenderAction> actionEntry : blenderContext.getActions().entrySet()) {
|
||||||
|
if (actionEntry.getValue().hasTrackName(node.getName())) {
|
||||||
|
result.add(actionEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.animations;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.jme3.animation.BoneTrack;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract representation of animation. The data stored here is mainly a
|
||||||
|
* raw action data loaded from blender. It can later be transformed into
|
||||||
|
* bone or spatial animation and applied to the specified node.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class BlenderAction implements Cloneable {
|
||||||
|
/** The action name. */
|
||||||
|
/* package */final String name;
|
||||||
|
/** Animation speed - frames per second. */
|
||||||
|
/* package */int fps;
|
||||||
|
/**
|
||||||
|
* The last frame of the animation (the last ipo curve node position is
|
||||||
|
* used as a last frame).
|
||||||
|
*/
|
||||||
|
/* package */int stopFrame;
|
||||||
|
/**
|
||||||
|
* Tracks of the features. In case of bone animation the keys are the
|
||||||
|
* names of the bones. In case of spatial animation - the node's name is
|
||||||
|
* used. A single ipo contains all tracks for location, rotation and
|
||||||
|
* scales.
|
||||||
|
*/
|
||||||
|
/* package */Map<String, Ipo> featuresTracks = new HashMap<String, Ipo>();
|
||||||
|
|
||||||
|
public BlenderAction(String name, int fps) {
|
||||||
|
this.name = name;
|
||||||
|
this.fps = fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTracksThatAreNotInTheCollection(Collection<String> trackNames) {
|
||||||
|
Map<String, Ipo> newTracks = new HashMap<String, Ipo>();
|
||||||
|
for (String trackName : trackNames) {
|
||||||
|
if (featuresTracks.containsKey(trackName)) {
|
||||||
|
newTracks.put(trackName, featuresTracks.get(trackName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
featuresTracks = newTracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlenderAction clone() {
|
||||||
|
BlenderAction result = new BlenderAction(name, fps);
|
||||||
|
result.stopFrame = stopFrame;
|
||||||
|
result.featuresTracks = new HashMap<String, Ipo>(featuresTracks);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the action into JME spatial animation tracks.
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* the node that will be animated
|
||||||
|
* @return the spatial tracks for the node
|
||||||
|
*/
|
||||||
|
public SpatialTrack[] toTracks(Node node, BlenderContext blenderContext) {
|
||||||
|
List<SpatialTrack> tracks = new ArrayList<SpatialTrack>(featuresTracks.size());
|
||||||
|
for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
|
||||||
|
tracks.add((SpatialTrack) entry.getValue().calculateTrack(0, null, node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale(), 1, stopFrame, fps, true));
|
||||||
|
}
|
||||||
|
return tracks.toArray(new SpatialTrack[tracks.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the action into JME bone animation tracks.
|
||||||
|
*
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton that will be animated
|
||||||
|
* @return the bone tracks for the node
|
||||||
|
*/
|
||||||
|
public BoneTrack[] toTracks(Skeleton skeleton, BlenderContext blenderContext) {
|
||||||
|
List<BoneTrack> tracks = new ArrayList<BoneTrack>(featuresTracks.size());
|
||||||
|
for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
|
||||||
|
int boneIndex = skeleton.getBoneIndex(entry.getKey());
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(boneIndex));
|
||||||
|
tracks.add((BoneTrack) entry.getValue().calculateTrack(boneIndex, boneContext, boneContext.getBone().getBindPosition(), boneContext.getBone().getBindRotation(), boneContext.getBone().getBindScale(), 1, stopFrame, fps, false));
|
||||||
|
}
|
||||||
|
return tracks.toArray(new BoneTrack[tracks.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the name of the action
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the time of animations (in seconds)
|
||||||
|
*/
|
||||||
|
public float getAnimationTime() {
|
||||||
|
return (stopFrame - 1) / (float) fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the current action has a track of a given name.
|
||||||
|
* CAUTION! The names are case sensitive.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the track
|
||||||
|
* @return <B>true</b> if the track of a given name exists for the
|
||||||
|
* action and <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public boolean hasTrackName(String name) {
|
||||||
|
return featuresTracks.containsKey(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the amount of tracks in current action
|
||||||
|
*/
|
||||||
|
public int getTracksCount() {
|
||||||
|
return featuresTracks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BlenderTrack [name = " + name + "; tracks = [" + featuresTracks.keySet() + "]]";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,400 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.animations;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds the basic data that describes a bone.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class BoneContext {
|
||||||
|
// the flags of the bone
|
||||||
|
public static final int SELECTED = 0x000001;
|
||||||
|
public static final int CONNECTED_TO_PARENT = 0x000010;
|
||||||
|
public static final int DEFORM = 0x001000;
|
||||||
|
public static final int NO_LOCAL_LOCATION = 0x400000;
|
||||||
|
public static final int NO_INHERIT_SCALE = 0x008000;
|
||||||
|
public static final int NO_INHERIT_ROTATION = 0x000200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bones' matrices have, unlike objects', the coordinate system identical to JME's (Y axis is UP, X to the right and Z toward us).
|
||||||
|
* So in order to have them loaded properly we need to transform their armature matrix (which blender sees as rotated) to make sure we get identical results.
|
||||||
|
*/
|
||||||
|
public static final Matrix4f BONE_ARMATURE_TRANSFORMATION_MATRIX = new Matrix4f(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
|
||||||
|
|
||||||
|
private static final int IKFLAG_LOCK_X = 0x01;
|
||||||
|
private static final int IKFLAG_LOCK_Y = 0x02;
|
||||||
|
private static final int IKFLAG_LOCK_Z = 0x04;
|
||||||
|
private static final int IKFLAG_LIMIT_X = 0x08;
|
||||||
|
private static final int IKFLAG_LIMIT_Y = 0x10;
|
||||||
|
private static final int IKFLAG_LIMIT_Z = 0x20;
|
||||||
|
|
||||||
|
private BlenderContext blenderContext;
|
||||||
|
/** The OMA of the bone's armature object. */
|
||||||
|
private Long armatureObjectOMA;
|
||||||
|
/** The OMA of the model that owns the bone's skeleton. */
|
||||||
|
private Long skeletonOwnerOma;
|
||||||
|
/** The structure of the bone. */
|
||||||
|
private Structure boneStructure;
|
||||||
|
/** Bone's name. */
|
||||||
|
private String boneName;
|
||||||
|
/** The bone's flag. */
|
||||||
|
private int flag;
|
||||||
|
/** The bone's matrix in world space. */
|
||||||
|
private Matrix4f globalBoneMatrix;
|
||||||
|
/** The bone's matrix in the model space. */
|
||||||
|
private Matrix4f boneMatrixInModelSpace;
|
||||||
|
/** The parent context. */
|
||||||
|
private BoneContext parent;
|
||||||
|
/** The children of this context. */
|
||||||
|
private List<BoneContext> children = new ArrayList<BoneContext>();
|
||||||
|
/** Created bone (available after calling 'buildBone' method). */
|
||||||
|
private Bone bone;
|
||||||
|
/** The length of the bone. */
|
||||||
|
private float length;
|
||||||
|
/** The bone's deform envelope. */
|
||||||
|
private BoneEnvelope boneEnvelope;
|
||||||
|
|
||||||
|
// The below data is used only for IK constraint computations.
|
||||||
|
|
||||||
|
/** The bone's stretch value. */
|
||||||
|
private float ikStretch;
|
||||||
|
/** Bone's rotation minimum values. */
|
||||||
|
private Vector3f limitMin;
|
||||||
|
/** Bone's rotation maximum values. */
|
||||||
|
private Vector3f limitMax;
|
||||||
|
/** The bone's stiffness values (how much it rotates during IK computations. */
|
||||||
|
private Vector3f stiffness;
|
||||||
|
/** Values that indicate if any axis' rotation should be limited by some angle. */
|
||||||
|
private boolean[] limits;
|
||||||
|
/** Values that indicate if any axis' rotation should be disabled during IK computations. */
|
||||||
|
private boolean[] locks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Creates the basic set of bone's data.
|
||||||
|
*
|
||||||
|
* @param armatureObjectOMA
|
||||||
|
* the OMA of the bone's armature object
|
||||||
|
* @param boneStructure
|
||||||
|
* the bone's structure
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when problem with blender data reading
|
||||||
|
* occurs
|
||||||
|
*/
|
||||||
|
public BoneContext(Long armatureObjectOMA, Structure boneStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
this(boneStructure, armatureObjectOMA, null, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Creates the basic set of bone's data.
|
||||||
|
*
|
||||||
|
* @param boneStructure
|
||||||
|
* the bone's structure
|
||||||
|
* @param armatureObjectOMA
|
||||||
|
* the OMA of the bone's armature object
|
||||||
|
* @param parent
|
||||||
|
* bone's parent (null if the bone is the root bone)
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when problem with blender data reading
|
||||||
|
* occurs
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
this.parent = parent;
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
|
this.boneStructure = boneStructure;
|
||||||
|
this.armatureObjectOMA = armatureObjectOMA;
|
||||||
|
boneName = boneStructure.getFieldValue("name").toString();
|
||||||
|
flag = ((Number) boneStructure.getFieldValue("flag")).intValue();
|
||||||
|
length = ((Number) boneStructure.getFieldValue("length")).floatValue();
|
||||||
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
|
|
||||||
|
// first get the bone matrix in its armature space
|
||||||
|
globalBoneMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
|
||||||
|
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||||
|
// then make sure it is rotated in a proper way to fit the jme bone transformation conventions
|
||||||
|
globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure armatureStructure = blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext);
|
||||||
|
Spatial armature = (Spatial) objectHelper.toObject(armatureStructure, blenderContext);
|
||||||
|
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
|
Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform(), new Matrix4f());
|
||||||
|
|
||||||
|
// and now compute the final bone matrix in world space
|
||||||
|
globalBoneMatrix = armatureWorldMatrix.mult(globalBoneMatrix);
|
||||||
|
|
||||||
|
// load the bone deformation envelope if necessary
|
||||||
|
if ((flag & DEFORM) == 0) {// if the flag is NOT set then the DEFORM is in use
|
||||||
|
boneEnvelope = new BoneEnvelope(boneStructure, armatureWorldMatrix, blenderContext.getBlenderKey().isFixUpAxis());
|
||||||
|
}
|
||||||
|
|
||||||
|
// load bone's pose channel data
|
||||||
|
Pointer pPose = (Pointer) armatureStructure.getFieldValue("pose");
|
||||||
|
if (pPose != null && pPose.isNotNull()) {
|
||||||
|
List<Structure> poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
|
||||||
|
for (Structure poseChannel : poseChannels) {
|
||||||
|
Long boneOMA = ((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress();
|
||||||
|
if (boneOMA.equals(this.boneStructure.getOldMemoryAddress())) {
|
||||||
|
ikStretch = ((Number) poseChannel.getFieldValue("ikstretch")).floatValue();
|
||||||
|
DynamicArray<Number> limitMin = (DynamicArray<Number>) poseChannel.getFieldValue("limitmin");
|
||||||
|
this.limitMin = new Vector3f(limitMin.get(0).floatValue(), limitMin.get(1).floatValue(), limitMin.get(2).floatValue());
|
||||||
|
|
||||||
|
DynamicArray<Number> limitMax = (DynamicArray<Number>) poseChannel.getFieldValue("limitmax");
|
||||||
|
this.limitMax = new Vector3f(limitMax.get(0).floatValue(), limitMax.get(1).floatValue(), limitMax.get(2).floatValue());
|
||||||
|
|
||||||
|
DynamicArray<Number> stiffness = (DynamicArray<Number>) poseChannel.getFieldValue("stiffness");
|
||||||
|
this.stiffness = new Vector3f(stiffness.get(0).floatValue(), stiffness.get(1).floatValue(), stiffness.get(2).floatValue());
|
||||||
|
|
||||||
|
int ikFlag = ((Number) poseChannel.getFieldValue("ikflag")).intValue();
|
||||||
|
locks = new boolean[] { (ikFlag & IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LOCK_Z) != 0 };
|
||||||
|
// limits are enabled when locks are disabled, so we ween to take that into account here
|
||||||
|
limits = new boolean[] { (ikFlag & IKFLAG_LIMIT_X & ~IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LIMIT_Y & ~IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LIMIT_Z & ~IKFLAG_LOCK_Z) != 0 };
|
||||||
|
break;// we have found what we need, no need to search further
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the children
|
||||||
|
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase();
|
||||||
|
for (Structure child : childbase) {
|
||||||
|
children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method builds the bone. It recursively builds the bone's children.
|
||||||
|
*
|
||||||
|
* @param bones
|
||||||
|
* a list of bones where the newly created bone will be added
|
||||||
|
* @param skeletonOwnerOma
|
||||||
|
* the spatial of the object that will own the skeleton
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return newly created bone
|
||||||
|
*/
|
||||||
|
public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
|
||||||
|
this.skeletonOwnerOma = skeletonOwnerOma;
|
||||||
|
Long boneOMA = boneStructure.getOldMemoryAddress();
|
||||||
|
bone = new Bone(boneName);
|
||||||
|
bones.add(bone);
|
||||||
|
blenderContext.addLoadedFeatures(boneOMA, LoadedDataType.STRUCTURE, boneStructure);
|
||||||
|
blenderContext.addLoadedFeatures(boneOMA, LoadedDataType.FEATURE, bone);
|
||||||
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
|
|
||||||
|
Structure skeletonOwnerObjectStructure = (Structure) blenderContext.getLoadedFeature(skeletonOwnerOma, LoadedDataType.STRUCTURE);
|
||||||
|
// I could load 'imat' here, but apparently in some older blenders there were bugs or unfinished functionalities that stored ZERO matrix in imat field
|
||||||
|
// loading 'obmat' and inverting it makes us avoid errors in such cases
|
||||||
|
Matrix4f invertedObjectOwnerGlobalMatrix = objectHelper.getMatrix(skeletonOwnerObjectStructure, "obmat", blenderContext.getBlenderKey().isFixUpAxis()).invertLocal();
|
||||||
|
if (objectHelper.isParent(skeletonOwnerOma, armatureObjectOMA)) {
|
||||||
|
boneMatrixInModelSpace = globalBoneMatrix.mult(invertedObjectOwnerGlobalMatrix);
|
||||||
|
} else {
|
||||||
|
boneMatrixInModelSpace = invertedObjectOwnerGlobalMatrix.mult(globalBoneMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4f boneLocalMatrix = parent == null ? boneMatrixInModelSpace : parent.boneMatrixInModelSpace.invert().multLocal(boneMatrixInModelSpace);
|
||||||
|
|
||||||
|
Vector3f poseLocation = parent == null || !this.is(CONNECTED_TO_PARENT) ? boneLocalMatrix.toTranslationVector() : new Vector3f(0, parent.length, 0);
|
||||||
|
Quaternion rotation = boneLocalMatrix.toRotationQuat().normalizeLocal();
|
||||||
|
Vector3f scale = boneLocalMatrix.toScaleVector();
|
||||||
|
|
||||||
|
bone.setBindTransforms(poseLocation, rotation, scale);
|
||||||
|
for (BoneContext child : children) {
|
||||||
|
bone.addChild(child.buildBone(bones, skeletonOwnerOma, blenderContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return built bone (available after calling 'buildBone' method)
|
||||||
|
*/
|
||||||
|
public Bone getBone() {
|
||||||
|
return bone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the old memory address of the bone
|
||||||
|
*/
|
||||||
|
public Long getBoneOma() {
|
||||||
|
return boneStructure.getOldMemoryAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method returns the length of the bone.
|
||||||
|
* If you want to use it for bone debugger take model space scale into account and do
|
||||||
|
* something like this:
|
||||||
|
* <b>boneContext.getLength() * boneContext.getBone().getModelSpaceScale().y</b>.
|
||||||
|
* Otherwise the bones might not look as they should in the bone debugger.
|
||||||
|
* @return the length of the bone
|
||||||
|
*/
|
||||||
|
public float getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return OMA of the bone's armature object
|
||||||
|
*/
|
||||||
|
public Long getArmatureObjectOMA() {
|
||||||
|
return armatureObjectOMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OMA of the model that owns the bone's skeleton
|
||||||
|
*/
|
||||||
|
public Long getSkeletonOwnerOma() {
|
||||||
|
return skeletonOwnerOma;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the skeleton the bone of this context belongs to
|
||||||
|
*/
|
||||||
|
public Skeleton getSkeleton() {
|
||||||
|
return blenderContext.getSkeleton(armatureObjectOMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the initial bone's matrix in model space
|
||||||
|
*/
|
||||||
|
public Matrix4f getBoneMatrixInModelSpace() {
|
||||||
|
return boneMatrixInModelSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the vertex assigning envelope of the bone
|
||||||
|
*/
|
||||||
|
public BoneEnvelope getBoneEnvelope() {
|
||||||
|
return boneEnvelope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bone's stretch factor
|
||||||
|
*/
|
||||||
|
public float getIkStretch() {
|
||||||
|
return ikStretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return indicates if the X rotation should be limited
|
||||||
|
*/
|
||||||
|
public boolean isLimitX() {
|
||||||
|
return limits != null ? limits[0] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return indicates if the Y rotation should be limited
|
||||||
|
*/
|
||||||
|
public boolean isLimitY() {
|
||||||
|
return limits != null ? limits[1] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return indicates if the Z rotation should be limited
|
||||||
|
*/
|
||||||
|
public boolean isLimitZ() {
|
||||||
|
return limits != null ? limits[2] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return indicates if the X rotation should be disabled
|
||||||
|
*/
|
||||||
|
public boolean isLockX() {
|
||||||
|
return locks != null ? locks[0] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return indicates if the Y rotation should be disabled
|
||||||
|
*/
|
||||||
|
public boolean isLockY() {
|
||||||
|
return locks != null ? locks[1] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return indicates if the Z rotation should be disabled
|
||||||
|
*/
|
||||||
|
public boolean isLockZ() {
|
||||||
|
return locks != null ? locks[2] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the minimum values in rotation limitation (if limitation is enabled for specific axis).
|
||||||
|
*/
|
||||||
|
public Vector3f getLimitMin() {
|
||||||
|
return limitMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum values in rotation limitation (if limitation is enabled for specific axis).
|
||||||
|
*/
|
||||||
|
public Vector3f getLimitMax() {
|
||||||
|
return limitMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the stiffness of the bone
|
||||||
|
*/
|
||||||
|
public Vector3f getStiffness() {
|
||||||
|
return stiffness;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the bone is of specified property defined by its flag.
|
||||||
|
* @param flagMask
|
||||||
|
* the mask of the flag (constants defined in this class)
|
||||||
|
* @return <b>true</b> if the bone IS of specified proeprty and <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public boolean is(int flagMask) {
|
||||||
|
return (flag & flagMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the root bone context of this bone context
|
||||||
|
*/
|
||||||
|
public BoneContext getRoot() {
|
||||||
|
BoneContext result = this;
|
||||||
|
while (result.parent != null) {
|
||||||
|
result = result.parent;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a number of bones from this bone to its root
|
||||||
|
*/
|
||||||
|
public int getDistanceFromRoot() {
|
||||||
|
int result = 0;
|
||||||
|
BoneContext boneContext = this;
|
||||||
|
while (boneContext.parent != null) {
|
||||||
|
boneContext = boneContext.parent;
|
||||||
|
++result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BoneContext: " + boneName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.animations;
|
||||||
|
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of bone envelope. Used when assigning bones to the mesh by envelopes.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski
|
||||||
|
*/
|
||||||
|
public class BoneEnvelope {
|
||||||
|
/** A defined distance that will be included in the envelope space. */
|
||||||
|
private float distance;
|
||||||
|
/** The bone's weight. */
|
||||||
|
private float weight;
|
||||||
|
/** The radius of the bone's head. */
|
||||||
|
private float boneHeadRadius;
|
||||||
|
/** The radius of the bone's tail. */
|
||||||
|
private float boneTailRadius;
|
||||||
|
/** Head position in rest pose in world space. */
|
||||||
|
private Vector3f head;
|
||||||
|
/** Tail position in rest pose in world space. */
|
||||||
|
private Vector3f tail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constructor of bone envelope. It reads all the needed data. Take notice that the positions of head and tail
|
||||||
|
* are computed in the world space and that the points' positions given for computations should be in world space as well.
|
||||||
|
*
|
||||||
|
* @param boneStructure
|
||||||
|
* the blender bone structure
|
||||||
|
* @param armatureWorldMatrix
|
||||||
|
* the world matrix of the armature object
|
||||||
|
* @param fixUpAxis
|
||||||
|
* a variable that tells if we use the Y-is up axis orientation
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BoneEnvelope(Structure boneStructure, Matrix4f armatureWorldMatrix, boolean fixUpAxis) {
|
||||||
|
distance = ((Number) boneStructure.getFieldValue("dist")).floatValue();
|
||||||
|
weight = ((Number) boneStructure.getFieldValue("weight")).floatValue();
|
||||||
|
boneHeadRadius = ((Number) boneStructure.getFieldValue("rad_head")).floatValue();
|
||||||
|
boneTailRadius = ((Number) boneStructure.getFieldValue("rad_tail")).floatValue();
|
||||||
|
|
||||||
|
DynamicArray<Number> headArray = (DynamicArray<Number>) boneStructure.getFieldValue("arm_head");
|
||||||
|
head = new Vector3f(headArray.get(0).floatValue(), headArray.get(1).floatValue(), headArray.get(2).floatValue());
|
||||||
|
if (fixUpAxis) {
|
||||||
|
float z = head.z;
|
||||||
|
head.z = -head.y;
|
||||||
|
head.y = z;
|
||||||
|
}
|
||||||
|
armatureWorldMatrix.mult(head, head);// move the head point to global space
|
||||||
|
|
||||||
|
DynamicArray<Number> tailArray = (DynamicArray<Number>) boneStructure.getFieldValue("arm_tail");
|
||||||
|
tail = new Vector3f(tailArray.get(0).floatValue(), tailArray.get(1).floatValue(), tailArray.get(2).floatValue());
|
||||||
|
if (fixUpAxis) {
|
||||||
|
float z = tail.z;
|
||||||
|
tail.z = -tail.y;
|
||||||
|
tail.y = z;
|
||||||
|
}
|
||||||
|
armatureWorldMatrix.mult(tail, tail);// move the tail point to global space
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method verifies if the given point is inside the envelope.
|
||||||
|
* @param point
|
||||||
|
* the point in 3D space (MUST be in a world coordinate space)
|
||||||
|
* @return <b>true</b> if the point is inside the envelope and <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public boolean isInEnvelope(Vector3f point) {
|
||||||
|
Vector3f v = tail.subtract(head);
|
||||||
|
float boneLength = v.length();
|
||||||
|
v.normalizeLocal();
|
||||||
|
|
||||||
|
// computing a plane that contains 'point' and v is its normal vector
|
||||||
|
// the plane's equation is: Ax + By + Cz + D = 0, where v = [A, B, C]
|
||||||
|
float D = -v.dot(point);
|
||||||
|
|
||||||
|
// computing a point where a line that contains head and tail crosses the plane
|
||||||
|
float temp = -(v.dot(head) + D) / v.dot(v);
|
||||||
|
Vector3f p = head.add(v.x * temp, v.y * temp, v.z * temp);
|
||||||
|
|
||||||
|
// determining if the point p is on the same or other side of head than the tail point
|
||||||
|
Vector3f headToPointOnLineVector = p.subtract(head);
|
||||||
|
float headToPointLength = headToPointOnLineVector.length();
|
||||||
|
float cosinus = headToPointOnLineVector.dot(v) / headToPointLength;// the length of v is already = 1; cosinus should be either 1, 0 or -1
|
||||||
|
if (cosinus < 0 && headToPointLength > boneHeadRadius || headToPointLength > boneLength + boneTailRadius) {
|
||||||
|
return false;// the point is outside the anvelope
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check if the point is inside and envelope
|
||||||
|
float pointDistanceFromLine = point.subtract(p).length(), maximumDistance = 0;
|
||||||
|
if (cosinus < 0) {
|
||||||
|
// checking if the distance from p to point is inside the half sphere defined by head envelope
|
||||||
|
// compute the distance from the line to the half sphere border
|
||||||
|
maximumDistance = boneHeadRadius;
|
||||||
|
} else if (headToPointLength < boneLength) {
|
||||||
|
// compute the maximum available distance
|
||||||
|
if (boneTailRadius > boneHeadRadius) {
|
||||||
|
// compute the distance from head to p
|
||||||
|
float headToPDistance = p.subtract(head).length();
|
||||||
|
// from tangens function we have
|
||||||
|
float x = headToPDistance * ((boneTailRadius - boneHeadRadius) / boneLength);
|
||||||
|
maximumDistance = x + boneHeadRadius;
|
||||||
|
} else if (boneTailRadius < boneHeadRadius) {
|
||||||
|
// compute the distance from head to p
|
||||||
|
float tailToPDistance = p.subtract(tail).length();
|
||||||
|
// from tangens function we have
|
||||||
|
float x = tailToPDistance * ((boneHeadRadius - boneTailRadius) / boneLength);
|
||||||
|
maximumDistance = x + boneTailRadius;
|
||||||
|
} else {
|
||||||
|
maximumDistance = boneTailRadius;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// checking if the distance from p to point is inside the half sphere defined by tail envelope
|
||||||
|
maximumDistance = boneTailRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointDistanceFromLine <= maximumDistance + distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the weight of the bone
|
||||||
|
*/
|
||||||
|
public float getWeight() {
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BoneEnvelope [d=" + distance + ", w=" + weight + ", hr=" + boneHeadRadius + ", tr=" + boneTailRadius + ", (" + head + ") -> (" + tail + ")]";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,317 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.animations;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.animation.BoneTrack;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.animation.Track;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.plugins.blender.curves.BezierCurve;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to calculate bezier curves value for the given frames. The
|
||||||
|
* Ipo (interpolation object) consists of several b-spline curves (connected 3rd
|
||||||
|
* degree bezier curves) of a different type.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski
|
||||||
|
*/
|
||||||
|
public class Ipo {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName());
|
||||||
|
|
||||||
|
public static final int AC_LOC_X = 1;
|
||||||
|
public static final int AC_LOC_Y = 2;
|
||||||
|
public static final int AC_LOC_Z = 3;
|
||||||
|
public static final int OB_ROT_X = 7;
|
||||||
|
public static final int OB_ROT_Y = 8;
|
||||||
|
public static final int OB_ROT_Z = 9;
|
||||||
|
public static final int AC_SIZE_X = 13;
|
||||||
|
public static final int AC_SIZE_Y = 14;
|
||||||
|
public static final int AC_SIZE_Z = 15;
|
||||||
|
public static final int AC_QUAT_W = 25;
|
||||||
|
public static final int AC_QUAT_X = 26;
|
||||||
|
public static final int AC_QUAT_Y = 27;
|
||||||
|
public static final int AC_QUAT_Z = 28;
|
||||||
|
|
||||||
|
/** A list of bezier curves for this interpolation object. */
|
||||||
|
private BezierCurve[] bezierCurves;
|
||||||
|
/** Each ipo contains one bone track. */
|
||||||
|
private Track calculatedTrack;
|
||||||
|
/** This variable indicates if the Y asxis is the UP axis or not. */
|
||||||
|
protected boolean fixUpAxis;
|
||||||
|
/**
|
||||||
|
* Depending on the blender version rotations are stored in degrees or
|
||||||
|
* radians so we need to know the version that is used.
|
||||||
|
*/
|
||||||
|
protected final int blenderVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Stores the bezier curves.
|
||||||
|
*
|
||||||
|
* @param bezierCurves
|
||||||
|
* a table of bezier curves
|
||||||
|
* @param fixUpAxis
|
||||||
|
* indicates if the Y is the up axis or not
|
||||||
|
* @param blenderVersion
|
||||||
|
* the blender version that is currently used
|
||||||
|
*/
|
||||||
|
public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
|
||||||
|
this.bezierCurves = bezierCurves;
|
||||||
|
this.fixUpAxis = fixUpAxis;
|
||||||
|
this.blenderVersion = blenderVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method calculates the ipo value for the first curve.
|
||||||
|
*
|
||||||
|
* @param frame
|
||||||
|
* the frame for which the value is calculated
|
||||||
|
* @return calculated ipo value
|
||||||
|
*/
|
||||||
|
public double calculateValue(int frame) {
|
||||||
|
return this.calculateValue(frame, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method calculates the ipo value for the curve of the specified
|
||||||
|
* index. Make sure you do not exceed the curves amount. Alway chech the
|
||||||
|
* amount of curves before calling this method.
|
||||||
|
*
|
||||||
|
* @param frame
|
||||||
|
* the frame for which the value is calculated
|
||||||
|
* @param curveIndex
|
||||||
|
* the index of the curve
|
||||||
|
* @return calculated ipo value
|
||||||
|
*/
|
||||||
|
public double calculateValue(int frame, int curveIndex) {
|
||||||
|
return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the frame where last bezier triple center point of
|
||||||
|
* the specified bezier curve is located.
|
||||||
|
*
|
||||||
|
* @return the frame number of the last defined bezier triple point for the
|
||||||
|
* specified ipo
|
||||||
|
*/
|
||||||
|
public int getLastFrame() {
|
||||||
|
int result = 1;
|
||||||
|
for (int i = 0; i < bezierCurves.length; ++i) {
|
||||||
|
int tempResult = bezierCurves[i].getLastFrame();
|
||||||
|
if (tempResult > result) {
|
||||||
|
result = tempResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method calculates the value of the curves as a bone track between
|
||||||
|
* the specified frames.
|
||||||
|
*
|
||||||
|
* @param targetIndex
|
||||||
|
* the index of the target for which the method calculates the
|
||||||
|
* tracks IMPORTANT! Aet to -1 (or any negative number) if you
|
||||||
|
* want to load spatial animation.
|
||||||
|
* @param localTranslation
|
||||||
|
* the local translation of the object/bone that will be animated by
|
||||||
|
* the track
|
||||||
|
* @param localRotation
|
||||||
|
* the local rotation of the object/bone that will be animated by
|
||||||
|
* the track
|
||||||
|
* @param localScale
|
||||||
|
* the local scale of the object/bone that will be animated by
|
||||||
|
* the track
|
||||||
|
* @param startFrame
|
||||||
|
* the first frame of tracks (inclusive)
|
||||||
|
* @param stopFrame
|
||||||
|
* the last frame of the tracks (inclusive)
|
||||||
|
* @param fps
|
||||||
|
* frame rate (frames per second)
|
||||||
|
* @param spatialTrack
|
||||||
|
* this flag indicates if the track belongs to a spatial or to a
|
||||||
|
* bone; the difference is important because it appears that bones
|
||||||
|
* in blender have the same type of coordinate system (Y as UP)
|
||||||
|
* as jme while other features have different one (Z is UP)
|
||||||
|
* @return bone track for the specified bone
|
||||||
|
*/
|
||||||
|
public Track calculateTrack(int targetIndex, BoneContext boneContext, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
|
||||||
|
if (calculatedTrack == null) {
|
||||||
|
// preparing data for track
|
||||||
|
int framesAmount = stopFrame - startFrame;
|
||||||
|
float timeBetweenFrames = 1.0f / fps;
|
||||||
|
|
||||||
|
float[] times = new float[framesAmount + 1];
|
||||||
|
Vector3f[] translations = new Vector3f[framesAmount + 1];
|
||||||
|
float[] translation = new float[3];
|
||||||
|
Quaternion[] rotations = new Quaternion[framesAmount + 1];
|
||||||
|
float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), };
|
||||||
|
float[] eulerRotation = localRotation.toAngles(null);
|
||||||
|
Vector3f[] scales = new Vector3f[framesAmount + 1];
|
||||||
|
float[] scale = new float[] { localScale.x, localScale.y, localScale.z };
|
||||||
|
float degreeToRadiansFactor = 1;
|
||||||
|
if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees
|
||||||
|
degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
|
||||||
|
}
|
||||||
|
int yIndex = 1, zIndex = 2;
|
||||||
|
boolean swapAxes = spatialTrack && fixUpAxis;
|
||||||
|
if (swapAxes) {
|
||||||
|
yIndex = 2;
|
||||||
|
zIndex = 1;
|
||||||
|
}
|
||||||
|
boolean eulerRotationUsed = false, queternionRotationUsed = false;
|
||||||
|
|
||||||
|
// calculating track data
|
||||||
|
for (int frame = startFrame; frame <= stopFrame; ++frame) {
|
||||||
|
boolean translationSet = false;
|
||||||
|
translation[0] = translation[1] = translation[2] = 0;
|
||||||
|
int index = frame - startFrame;
|
||||||
|
times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
|
||||||
|
for (int j = 0; j < bezierCurves.length; ++j) {
|
||||||
|
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
|
||||||
|
switch (bezierCurves[j].getType()) {
|
||||||
|
// LOCATION
|
||||||
|
case AC_LOC_X:
|
||||||
|
translation[0] = (float) value;
|
||||||
|
translationSet = true;
|
||||||
|
break;
|
||||||
|
case AC_LOC_Y:
|
||||||
|
if (swapAxes && value != 0) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
translation[yIndex] = (float) value;
|
||||||
|
translationSet = true;
|
||||||
|
break;
|
||||||
|
case AC_LOC_Z:
|
||||||
|
translation[zIndex] = (float) value;
|
||||||
|
translationSet = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// EULER ROTATION
|
||||||
|
case OB_ROT_X:
|
||||||
|
eulerRotationUsed = true;
|
||||||
|
eulerRotation[0] = (float) value * degreeToRadiansFactor;
|
||||||
|
break;
|
||||||
|
case OB_ROT_Y:
|
||||||
|
eulerRotationUsed = true;
|
||||||
|
if (swapAxes && value != 0) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
eulerRotation[yIndex] = (float) value * degreeToRadiansFactor;
|
||||||
|
break;
|
||||||
|
case OB_ROT_Z:
|
||||||
|
eulerRotationUsed = true;
|
||||||
|
eulerRotation[zIndex] = (float) value * degreeToRadiansFactor;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// SIZE
|
||||||
|
case AC_SIZE_X:
|
||||||
|
scale[0] = (float) value;
|
||||||
|
break;
|
||||||
|
case AC_SIZE_Y:
|
||||||
|
scale[yIndex] = (float) value;
|
||||||
|
break;
|
||||||
|
case AC_SIZE_Z:
|
||||||
|
scale[zIndex] = (float) value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// QUATERNION ROTATION (used with bone animation)
|
||||||
|
case AC_QUAT_W:
|
||||||
|
queternionRotationUsed = true;
|
||||||
|
quaternionRotation[3] = (float) value;
|
||||||
|
break;
|
||||||
|
case AC_QUAT_X:
|
||||||
|
queternionRotationUsed = true;
|
||||||
|
quaternionRotation[0] = (float) value;
|
||||||
|
break;
|
||||||
|
case AC_QUAT_Y:
|
||||||
|
queternionRotationUsed = true;
|
||||||
|
if (swapAxes && value != 0) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
quaternionRotation[yIndex] = (float) value;
|
||||||
|
break;
|
||||||
|
case AC_QUAT_Z:
|
||||||
|
quaternionRotation[zIndex] = (float) value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(translationSet) {
|
||||||
|
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
|
||||||
|
} else {
|
||||||
|
translations[index] = new Vector3f();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(boneContext != null) {
|
||||||
|
if(boneContext.getBone().getParent() == null && boneContext.is(BoneContext.NO_LOCAL_LOCATION)) {
|
||||||
|
float temp = translations[index].z;
|
||||||
|
translations[index].z = -translations[index].y;
|
||||||
|
translations[index].y = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queternionRotationUsed) {
|
||||||
|
rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
|
||||||
|
} else {
|
||||||
|
rotations[index] = new Quaternion().fromAngles(eulerRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
|
||||||
|
}
|
||||||
|
if (spatialTrack) {
|
||||||
|
calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
|
||||||
|
} else {
|
||||||
|
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queternionRotationUsed && eulerRotationUsed) {
|
||||||
|
LOGGER.warning("Animation uses both euler and quaternion tracks for rotations. Quaternion rotation is applied. Make sure that this is what you wanted!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculatedTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ipo constant curve. This is a curve with only one value and no specified
|
||||||
|
* type. This type of ipo cannot be used to calculate tracks. It should only
|
||||||
|
* be used to calculate single value for a given frame.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */static class ConstIpo extends Ipo {
|
||||||
|
|
||||||
|
/** The constant value of this ipo. */
|
||||||
|
private float constValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Stores the constant value of this ipo.
|
||||||
|
*
|
||||||
|
* @param constValue
|
||||||
|
* the constant value of this ipo
|
||||||
|
*/
|
||||||
|
public ConstIpo(float constValue) {
|
||||||
|
super(null, false, 0);// the version is not important here
|
||||||
|
this.constValue = constValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double calculateValue(int frame) {
|
||||||
|
return constValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double calculateValue(int frame, int curveIndex) {
|
||||||
|
return constValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoneTrack calculateTrack(int boneIndex, BoneContext boneContext, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) {
|
||||||
|
throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.cameras;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that is used to load cameras into the scene.
|
||||||
|
* @author Marcin Roguski
|
||||||
|
*/
|
||||||
|
public class CameraHelper extends AbstractBlenderHelper {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
|
||||||
|
protected static final int DEFAULT_CAM_WIDTH = 640;
|
||||||
|
protected static final int DEFAULT_CAM_HEIGHT = 480;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
|
||||||
|
* different blender versions.
|
||||||
|
* @param blenderVersion
|
||||||
|
* the version read from the blend file
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public CameraHelper(String blenderVersion, BlenderContext blenderContext) {
|
||||||
|
super(blenderVersion, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts the given structure to jme camera.
|
||||||
|
*
|
||||||
|
* @param structure
|
||||||
|
* camera structure
|
||||||
|
* @return jme camera object
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when there are problems with the
|
||||||
|
* blender file
|
||||||
|
*/
|
||||||
|
public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
if (blenderVersion >= 250) {
|
||||||
|
return this.toCamera250(structure, blenderContext.getSceneStructure());
|
||||||
|
} else {
|
||||||
|
return this.toCamera249(structure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts the given structure to jme camera. Should be used form blender 2.5+.
|
||||||
|
*
|
||||||
|
* @param structure
|
||||||
|
* camera structure
|
||||||
|
* @param sceneStructure
|
||||||
|
* scene structure
|
||||||
|
* @return jme camera object
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when there are problems with the
|
||||||
|
* blender file
|
||||||
|
*/
|
||||||
|
private Camera toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException {
|
||||||
|
int width = DEFAULT_CAM_WIDTH;
|
||||||
|
int height = DEFAULT_CAM_HEIGHT;
|
||||||
|
if (sceneStructure != null) {
|
||||||
|
Structure renderData = (Structure) sceneStructure.getFieldValue("r");
|
||||||
|
width = ((Number) renderData.getFieldValue("xsch")).shortValue();
|
||||||
|
height = ((Number) renderData.getFieldValue("ysch")).shortValue();
|
||||||
|
}
|
||||||
|
Camera camera = new Camera(width, height);
|
||||||
|
int type = ((Number) structure.getFieldValue("type")).intValue();
|
||||||
|
if (type != 0 && type != 1) {
|
||||||
|
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
|
||||||
|
type = 0;
|
||||||
|
}
|
||||||
|
// type==0 - perspective; type==1 - orthographic; perspective is used as default
|
||||||
|
camera.setParallelProjection(type == 1);
|
||||||
|
float aspect = width / (float) height;
|
||||||
|
float fovY; // Vertical field of view in degrees
|
||||||
|
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
|
||||||
|
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
|
||||||
|
if (type == 0) {
|
||||||
|
// Convert lens MM to vertical degrees in fovY, see Blender rna_Camera_angle_get()
|
||||||
|
// Default sensor size prior to 2.60 was 32.
|
||||||
|
float sensor = 32.0f;
|
||||||
|
boolean sensorVertical = false;
|
||||||
|
Number sensorFit = (Number) structure.getFieldValue("sensor_fit");
|
||||||
|
if (sensorFit != null) {
|
||||||
|
// If sensor_fit is vert (2), then sensor_y is used
|
||||||
|
sensorVertical = sensorFit.byteValue() == 2;
|
||||||
|
String sensorName = "sensor_x";
|
||||||
|
if (sensorVertical) {
|
||||||
|
sensorName = "sensor_y";
|
||||||
|
}
|
||||||
|
sensor = ((Number) structure.getFieldValue(sensorName)).floatValue();
|
||||||
|
}
|
||||||
|
float focalLength = ((Number) structure.getFieldValue("lens")).floatValue();
|
||||||
|
float fov = 2.0f * FastMath.atan(sensor / 2.0f / focalLength);
|
||||||
|
if (sensorVertical) {
|
||||||
|
fovY = fov * FastMath.RAD_TO_DEG;
|
||||||
|
} else {
|
||||||
|
// Convert fov from horizontal to vertical
|
||||||
|
fovY = 2.0f * FastMath.atan(FastMath.tan(fov / 2.0f) / aspect) * FastMath.RAD_TO_DEG;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This probably is not correct.
|
||||||
|
fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
|
||||||
|
}
|
||||||
|
camera.setFrustumPerspective(fovY, aspect, clipsta, clipend);
|
||||||
|
camera.setName(structure.getName());
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts the given structure to jme camera. Should be used form blender 2.49.
|
||||||
|
*
|
||||||
|
* @param structure
|
||||||
|
* camera structure
|
||||||
|
* @return jme camera object
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* an exception is thrown when there are problems with the
|
||||||
|
* blender file
|
||||||
|
*/
|
||||||
|
private Camera toCamera249(Structure structure) throws BlenderFileException {
|
||||||
|
Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
|
||||||
|
int type = ((Number) structure.getFieldValue("type")).intValue();
|
||||||
|
if (type != 0 && type != 1) {
|
||||||
|
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
|
||||||
|
type = 0;
|
||||||
|
}
|
||||||
|
// type==0 - perspective; type==1 - orthographic; perspective is used as default
|
||||||
|
camera.setParallelProjection(type == 1);
|
||||||
|
float aspect = 0;
|
||||||
|
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
|
||||||
|
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
|
||||||
|
if (type == 0) {
|
||||||
|
aspect = ((Number) structure.getFieldValue("lens")).floatValue();
|
||||||
|
} else {
|
||||||
|
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
|
||||||
|
}
|
||||||
|
camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend);
|
||||||
|
camera.setName(structure.getName());
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraint applied on the bone.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */class BoneConstraint extends Constraint {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bone constraint constructor.
|
||||||
|
*
|
||||||
|
* @param constraintStructure
|
||||||
|
* the constraint's structure
|
||||||
|
* @param ownerOMA
|
||||||
|
* the OMA of the bone that owns the constraint
|
||||||
|
* @param influenceIpo
|
||||||
|
* the influence interpolation curve
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* exception thrown when problems with blender file occur
|
||||||
|
*/
|
||||||
|
public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
if (targetOMA != null) {
|
||||||
|
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE);
|
||||||
|
if (nodeTarget == null) {
|
||||||
|
LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// the second part of the if expression verifies if the found node
|
||||||
|
// (if any) is an armature node
|
||||||
|
if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
|
||||||
|
if (subtargetName.trim().isEmpty()) {
|
||||||
|
LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// if the target is not an object node then it is an Armature,
|
||||||
|
// so make sure the bone is in the current skeleton
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
|
||||||
|
LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(int frame) {
|
||||||
|
super.apply(frame);
|
||||||
|
blenderContext.getBoneContext(ownerOMA).getBone().updateModelTransforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTargetOMA() {
|
||||||
|
if(targetOMA != null && subtargetName != null && !subtargetName.trim().isEmpty()) {
|
||||||
|
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE);
|
||||||
|
if(nodeTarget != null) {
|
||||||
|
if(blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneByName(targetOMA, subtargetName);
|
||||||
|
return boneContext != null ? boneContext.getBoneOma() : 0L;
|
||||||
|
}
|
||||||
|
return targetOMA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinition;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinitionFactory;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation of a constraint.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public abstract class Constraint {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(Constraint.class.getName());
|
||||||
|
|
||||||
|
/** The name of this constraint. */
|
||||||
|
protected final String name;
|
||||||
|
/** Indicates if the constraint is already baked or not. */
|
||||||
|
protected boolean baked;
|
||||||
|
|
||||||
|
protected Space ownerSpace;
|
||||||
|
protected final ConstraintDefinition constraintDefinition;
|
||||||
|
protected Long ownerOMA;
|
||||||
|
|
||||||
|
protected Long targetOMA;
|
||||||
|
protected Space targetSpace;
|
||||||
|
protected String subtargetName;
|
||||||
|
|
||||||
|
/** The ipo object defining influence. */
|
||||||
|
protected final Ipo ipo;
|
||||||
|
/** The blender context. */
|
||||||
|
protected final BlenderContext blenderContext;
|
||||||
|
protected final ConstraintHelper constraintHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor creates the constraint instance.
|
||||||
|
*
|
||||||
|
* @param constraintStructure
|
||||||
|
* the constraint's structure (bConstraint clss in blender 2.49).
|
||||||
|
* @param ownerOMA
|
||||||
|
* the old memory address of the constraint owner
|
||||||
|
* @param ownerType
|
||||||
|
* the type of the constraint owner
|
||||||
|
* @param influenceIpo
|
||||||
|
* the ipo curve of the influence factor
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* this exception is thrown when the blender file is somehow
|
||||||
|
* corrupted
|
||||||
|
*/
|
||||||
|
public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
|
name = constraintStructure.getFieldValue("name").toString();
|
||||||
|
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
|
||||||
|
if (pData.isNotNull()) {
|
||||||
|
Structure data = pData.fetchData().get(0);
|
||||||
|
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, name, ownerOMA, blenderContext);
|
||||||
|
Pointer pTar = (Pointer) data.getFieldValue("tar");
|
||||||
|
if (pTar != null && pTar.isNotNull()) {
|
||||||
|
targetOMA = pTar.getOldMemoryAddress();
|
||||||
|
targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
|
||||||
|
Object subtargetValue = data.getFieldValue("subtarget");
|
||||||
|
if (subtargetValue != null) {// not all constraint data have the
|
||||||
|
// subtarget field
|
||||||
|
subtargetName = subtargetValue.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Null constraint has no data, so create it here
|
||||||
|
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, name, null, blenderContext);
|
||||||
|
}
|
||||||
|
ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
|
||||||
|
ipo = influenceIpo;
|
||||||
|
this.ownerOMA = ownerOMA;
|
||||||
|
constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
|
LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <b>true</b> if the constraint is implemented and <b>false</b>
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
public boolean isImplemented() {
|
||||||
|
return constraintDefinition == null ? true : constraintDefinition.isImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the name of the constraint type, similar to the constraint name
|
||||||
|
* used in Blender
|
||||||
|
*/
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return constraintDefinition.getConstraintTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OMAs of the features whose transform had been altered beside the constraint owner
|
||||||
|
*/
|
||||||
|
public Set<Long> getAlteredOmas() {
|
||||||
|
return constraintDefinition.getAlteredOmas();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs validation before baking. Checks factors that can prevent
|
||||||
|
* constraint from baking that could not be checked during constraint
|
||||||
|
* loading.
|
||||||
|
*/
|
||||||
|
public abstract boolean validate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OMA of the target or 0 if no target is specified for the constraint
|
||||||
|
*/
|
||||||
|
public abstract Long getTargetOMA();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
|
||||||
|
* @param frame
|
||||||
|
* the frame of the animation
|
||||||
|
*/
|
||||||
|
public void apply(int frame) {
|
||||||
|
if (LOGGER.isLoggable(Level.FINEST)) {
|
||||||
|
LOGGER.log(Level.FINEST, "Applying constraint: {0} for frame {1}", new Object[] { name, frame });
|
||||||
|
}
|
||||||
|
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
|
||||||
|
constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float) ipo.calculateValue(frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return determines if the definition of the constraint will change the bone in any way; in most cases
|
||||||
|
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
|
||||||
|
* computing to improve the computation speed and lower the computations complexity
|
||||||
|
*/
|
||||||
|
public boolean isTrackToBeChanged() {
|
||||||
|
return constraintDefinition == null ? false : constraintDefinition.isTrackToBeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (name == null ? 0 : name.hashCode());
|
||||||
|
result = prime * result + (ownerOMA == null ? 0 : ownerOMA.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Constraint other = (Constraint) obj;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!name.equals(other.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ownerOMA == null) {
|
||||||
|
if (other.ownerOMA != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!ownerOMA.equals(other.ownerOMA)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Constraint(name = " + name + ", def = " + constraintDefinition + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,476 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.AnimationHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class should be used for constraint calculations.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class ConstraintHelper extends AbstractBlenderHelper {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper constructor.
|
||||||
|
*
|
||||||
|
* @param blenderVersion
|
||||||
|
* the version read from the blend file
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public ConstraintHelper(String blenderVersion, BlenderContext blenderContext) {
|
||||||
|
super(blenderVersion, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method reads constraints for for the given structure. The
|
||||||
|
* constraints are loaded only once for object/bone.
|
||||||
|
*
|
||||||
|
* @param objectStructure
|
||||||
|
* the structure we read constraint's for
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @throws BlenderFileException
|
||||||
|
*/
|
||||||
|
public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
LOGGER.fine("Loading constraints.");
|
||||||
|
// reading influence ipos for the constraints
|
||||||
|
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
|
||||||
|
Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
|
||||||
|
Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
|
||||||
|
if (pActions.isNotNull()) {
|
||||||
|
List<Structure> actions = pActions.fetchData();
|
||||||
|
for (Structure action : actions) {
|
||||||
|
Structure chanbase = (Structure) action.getFieldValue("chanbase");
|
||||||
|
List<Structure> actionChannels = chanbase.evaluateListBase();
|
||||||
|
for (Structure actionChannel : actionChannels) {
|
||||||
|
Map<String, Ipo> ipos = new HashMap<String, Ipo>();
|
||||||
|
Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
|
||||||
|
List<Structure> constraintChannels = constChannels.evaluateListBase();
|
||||||
|
for (Structure constraintChannel : constraintChannels) {
|
||||||
|
Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
|
||||||
|
if (pIpo.isNotNull()) {
|
||||||
|
String constraintName = constraintChannel.getFieldValue("name").toString();
|
||||||
|
Ipo ipo = animationHelper.fromIpoStructure(pIpo.fetchData().get(0), blenderContext);
|
||||||
|
ipos.put(constraintName, ipo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String actionName = actionChannel.getFieldValue("name").toString();
|
||||||
|
constraintsIpos.put(actionName, ipos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loading constraints connected with the object's bones
|
||||||
|
Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
|
||||||
|
if (pPose.isNotNull()) {
|
||||||
|
List<Structure> poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
|
||||||
|
for (Structure poseChannel : poseChannels) {
|
||||||
|
List<Constraint> constraintsList = new ArrayList<Constraint>();
|
||||||
|
Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
|
||||||
|
|
||||||
|
// the name is read directly from structure because bone might
|
||||||
|
// not yet be loaded
|
||||||
|
String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
|
||||||
|
List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase();
|
||||||
|
for (Structure constraint : constraints) {
|
||||||
|
String constraintName = constraint.getFieldValue("name").toString();
|
||||||
|
Map<String, Ipo> ipoMap = constraintsIpos.get(name);
|
||||||
|
Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName);
|
||||||
|
if (ipo == null) {
|
||||||
|
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
|
||||||
|
ipo = animationHelper.fromValue(enforce);
|
||||||
|
}
|
||||||
|
constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
|
||||||
|
}
|
||||||
|
blenderContext.addConstraints(boneOMA, constraintsList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loading constraints connected with the object itself
|
||||||
|
List<Structure> constraints = ((Structure) objectStructure.getFieldValue("constraints")).evaluateListBase();
|
||||||
|
if (constraints != null && constraints.size() > 0) {
|
||||||
|
Pointer pData = (Pointer) objectStructure.getFieldValue("data");
|
||||||
|
String dataType = pData.isNotNull() ? pData.fetchData().get(0).getType() : null;
|
||||||
|
List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
|
||||||
|
|
||||||
|
for (Structure constraint : constraints) {
|
||||||
|
String constraintName = constraint.getFieldValue("name").toString();
|
||||||
|
String objectName = objectStructure.getName();
|
||||||
|
|
||||||
|
Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
|
||||||
|
Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null;
|
||||||
|
if (ipo == null) {
|
||||||
|
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
|
||||||
|
ipo = animationHelper.fromValue(enforce);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintsList.add(this.createConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
|
||||||
|
}
|
||||||
|
blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates a proper constraint object depending on the object's
|
||||||
|
* data type. Supported data types: <li>Mesh <li>Armature <li>Camera <li>
|
||||||
|
* Lamp Bone constraints are created in a different place.
|
||||||
|
*
|
||||||
|
* @param dataType
|
||||||
|
* the type of the object's data
|
||||||
|
* @param constraintStructure
|
||||||
|
* the constraint structure
|
||||||
|
* @param ownerOMA
|
||||||
|
* the owner OMA
|
||||||
|
* @param influenceIpo
|
||||||
|
* the influence interpolation curve
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return constraint object for the required type
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* thrown when problems with blender file occured
|
||||||
|
*/
|
||||||
|
private Constraint createConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
|
||||||
|
return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
|
} else if ("Armature".equalsIgnoreCase(dataType)) {
|
||||||
|
return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method bakes all available and valid constraints.
|
||||||
|
*
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public void bakeConstraints(BlenderContext blenderContext) {
|
||||||
|
Set<Long> owners = new HashSet<Long>();
|
||||||
|
for (Constraint constraint : blenderContext.getAllConstraints()) {
|
||||||
|
if(constraint instanceof BoneConstraint) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA);
|
||||||
|
owners.add(boneContext.getArmatureObjectOMA());
|
||||||
|
} else {
|
||||||
|
Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedDataType.FEATURE);
|
||||||
|
while (spatial.getParent() != null) {
|
||||||
|
spatial = spatial.getParent();
|
||||||
|
}
|
||||||
|
owners.add((Long)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, spatial));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SimulationNode> simulationRootNodes = new ArrayList<SimulationNode>(owners.size());
|
||||||
|
for(Long ownerOMA : owners) {
|
||||||
|
simulationRootNodes.add(new SimulationNode(ownerOMA, blenderContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SimulationNode node : simulationRootNodes) {
|
||||||
|
node.simulate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method retreives the transform from a feature in a given space.
|
||||||
|
*
|
||||||
|
* @param oma
|
||||||
|
* the OMA of the feature (spatial or armature node)
|
||||||
|
* @param subtargetName
|
||||||
|
* the feature's subtarget (bone in a case of armature's node)
|
||||||
|
* @param space
|
||||||
|
* the space the transform is evaluated to
|
||||||
|
* @return thensform of a feature in a given space
|
||||||
|
*/
|
||||||
|
public Transform getTransform(Long oma, String subtargetName, Space space) {
|
||||||
|
Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE);
|
||||||
|
boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null;
|
||||||
|
if (isArmature) {
|
||||||
|
blenderContext.getSkeleton(oma).updateWorldVectors();
|
||||||
|
BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName);
|
||||||
|
Bone bone = targetBoneContext.getBone();
|
||||||
|
|
||||||
|
if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) {
|
||||||
|
space = Space.CONSTRAINT_SPACE_POSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
TempVars tempVars = TempVars.get();// use readable names of the matrices so that the code is more clear
|
||||||
|
Transform result;
|
||||||
|
switch (space) {
|
||||||
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
|
Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedDataType.FEATURE);
|
||||||
|
Matrix4f boneModelMatrix = this.toMatrix(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale(), tempVars.tempMat4);
|
||||||
|
Matrix4f modelWorldMatrix = this.toMatrix(model.getWorldTransform(), tempVars.tempMat42);
|
||||||
|
Matrix4f boneMatrixInWorldSpace = modelWorldMatrix.multLocal(boneModelMatrix);
|
||||||
|
result = new Transform(boneMatrixInWorldSpace.toTranslationVector(), boneMatrixInWorldSpace.toRotationQuat(), boneMatrixInWorldSpace.toScaleVector());
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!";
|
||||||
|
result = new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_POSE: {
|
||||||
|
Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4);
|
||||||
|
Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal();
|
||||||
|
Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix);
|
||||||
|
result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CONSTRAINT_SPACE_PARLOCAL: {
|
||||||
|
Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4);
|
||||||
|
Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal();
|
||||||
|
Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix);
|
||||||
|
result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector());
|
||||||
|
Bone parent = bone.getParent();
|
||||||
|
if(parent != null) {
|
||||||
|
BoneContext parentContext = blenderContext.getBoneContext(parent);
|
||||||
|
Vector3f head = parent.getModelSpacePosition();
|
||||||
|
Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(parentContext.getLength())));
|
||||||
|
result.getTranslation().subtractLocal(tail);
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown space type: " + space);
|
||||||
|
}
|
||||||
|
tempVars.release();
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
switch (space) {
|
||||||
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
return feature.getLocalTransform();
|
||||||
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
|
return feature.getWorldTransform();
|
||||||
|
case CONSTRAINT_SPACE_PARLOCAL:
|
||||||
|
case CONSTRAINT_SPACE_POSE:
|
||||||
|
throw new IllegalStateException("Nodes can have only Local and World spaces applied!");
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown space type: " + space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies transform to a feature (bone or spatial). Computations transform
|
||||||
|
* the given transformation from the given space to the feature's local
|
||||||
|
* space.
|
||||||
|
*
|
||||||
|
* @param oma
|
||||||
|
* the OMA of the feature we apply transformation to
|
||||||
|
* @param subtargetName
|
||||||
|
* the name of the feature's subtarget (bone in case of armature)
|
||||||
|
* @param space
|
||||||
|
* the space in which the given transform is to be applied
|
||||||
|
* @param transform
|
||||||
|
* the transform we apply
|
||||||
|
*/
|
||||||
|
public void applyTransform(Long oma, String subtargetName, Space space, Transform transform) {
|
||||||
|
Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE);
|
||||||
|
boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null;
|
||||||
|
if (isArmature) {
|
||||||
|
Skeleton skeleton = blenderContext.getSkeleton(oma);
|
||||||
|
BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName);
|
||||||
|
Bone bone = targetBoneContext.getBone();
|
||||||
|
|
||||||
|
if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) {
|
||||||
|
space = Space.CONSTRAINT_SPACE_POSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
TempVars tempVars = TempVars.get();
|
||||||
|
switch (space) {
|
||||||
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!";
|
||||||
|
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_WORLD: {
|
||||||
|
Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform, tempVars.tempMat4);
|
||||||
|
Matrix4f modelWorldMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42);
|
||||||
|
Matrix4f boneMatrixInModelSpace = modelWorldMatrix.invertLocal().multLocal(boneMatrixInWorldSpace);
|
||||||
|
Bone parent = bone.getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4);
|
||||||
|
boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace);
|
||||||
|
}
|
||||||
|
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CONSTRAINT_SPACE_POSE: {
|
||||||
|
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4);
|
||||||
|
Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42));
|
||||||
|
Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal();
|
||||||
|
Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace);
|
||||||
|
Bone parent = bone.getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4);
|
||||||
|
boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace);
|
||||||
|
}
|
||||||
|
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CONSTRAINT_SPACE_PARLOCAL:
|
||||||
|
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4);
|
||||||
|
Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42));
|
||||||
|
Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal();
|
||||||
|
Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace);
|
||||||
|
Bone parent = bone.getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
//first add the initial parent matrix to the bone's model matrix
|
||||||
|
BoneContext parentContext = blenderContext.getBoneContext(parent);
|
||||||
|
|
||||||
|
Matrix4f initialParentMatrixInModelSpace = parentContext.getBoneMatrixInModelSpace();
|
||||||
|
Matrix4f currentParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4);
|
||||||
|
//the bone will now move with its parent in model space
|
||||||
|
|
||||||
|
//now we need to subtract the difference between current parent's model matrix and its initial model matrix
|
||||||
|
boneMatrixInModelSpace = initialParentMatrixInModelSpace.mult(boneMatrixInModelSpace);
|
||||||
|
|
||||||
|
Matrix4f diffMatrix = initialParentMatrixInModelSpace.mult(currentParentMatrixInModelSpace.invert());
|
||||||
|
boneMatrixInModelSpace.multLocal(diffMatrix);
|
||||||
|
//now the bone will have its position in model space with initial parent's model matrix added
|
||||||
|
}
|
||||||
|
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tempVars.release();
|
||||||
|
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
|
||||||
|
}
|
||||||
|
tempVars.release();
|
||||||
|
skeleton.updateWorldVectors();
|
||||||
|
} else {
|
||||||
|
switch (space) {
|
||||||
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
feature.getLocalTransform().set(transform);
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
|
if (feature.getParent() == null) {
|
||||||
|
feature.setLocalTransform(transform);
|
||||||
|
} else {
|
||||||
|
Transform parentWorldTransform = feature.getParent().getWorldTransform();
|
||||||
|
|
||||||
|
TempVars tempVars = TempVars.get();
|
||||||
|
Matrix4f parentInverseMatrix = this.toMatrix(parentWorldTransform, tempVars.tempMat4).invertLocal();
|
||||||
|
Matrix4f m = this.toMatrix(transform, tempVars.tempMat42);
|
||||||
|
m = m.multLocal(parentInverseMatrix);
|
||||||
|
tempVars.release();
|
||||||
|
|
||||||
|
transform.setTranslation(m.toTranslationVector());
|
||||||
|
transform.setRotation(m.toRotationQuat());
|
||||||
|
transform.setScale(m.toScaleVector());
|
||||||
|
|
||||||
|
feature.setLocalTransform(transform);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Invalid space type for spatial object: " + space.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts given transform to the matrix.
|
||||||
|
*
|
||||||
|
* @param transform
|
||||||
|
* the transform to be converted
|
||||||
|
* @param store
|
||||||
|
* the matrix where the result will be stored
|
||||||
|
* @return the store matrix
|
||||||
|
*/
|
||||||
|
public Matrix4f toMatrix(Transform transform, Matrix4f store) {
|
||||||
|
if (transform != null) {
|
||||||
|
return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale(), store);
|
||||||
|
}
|
||||||
|
store.loadIdentity();
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts given transformation parameters into the matrix.
|
||||||
|
*
|
||||||
|
* @param position
|
||||||
|
* the position of the feature
|
||||||
|
* @param rotation
|
||||||
|
* the rotation of the feature
|
||||||
|
* @param scale
|
||||||
|
* the scale of the feature
|
||||||
|
* @param store
|
||||||
|
* the matrix where the result will be stored
|
||||||
|
* @return the store matrix
|
||||||
|
*/
|
||||||
|
private Matrix4f toMatrix(Vector3f position, Quaternion rotation, Vector3f scale, Matrix4f store) {
|
||||||
|
store.loadIdentity();
|
||||||
|
store.setTranslation(position);
|
||||||
|
store.setRotationQuaternion(rotation);
|
||||||
|
store.setScale(scale);
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The space of target or owner transformation.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public static enum Space {
|
||||||
|
/** A transformation of the bone or spatial in the world space. */
|
||||||
|
CONSTRAINT_SPACE_WORLD,
|
||||||
|
/**
|
||||||
|
* For spatial it is the transformation in its parent space or in WORLD space if it has no parent.
|
||||||
|
* For bone it is a transformation in its bone parent space or in armature space if it has no parent.
|
||||||
|
*/
|
||||||
|
CONSTRAINT_SPACE_LOCAL,
|
||||||
|
/**
|
||||||
|
* This space IS NOT applicable for spatials.
|
||||||
|
* For bone it is a transformation in the blender's armature object space.
|
||||||
|
*/
|
||||||
|
CONSTRAINT_SPACE_POSE,
|
||||||
|
|
||||||
|
CONSTRAINT_SPACE_PARLOCAL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the enum instance when given the appropriate
|
||||||
|
* value from the blend file.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* the blender's value of the space modifier
|
||||||
|
* @return the scape enum instance
|
||||||
|
*/
|
||||||
|
public static Space valueOf(byte c) {
|
||||||
|
switch (c) {
|
||||||
|
case 0:
|
||||||
|
return CONSTRAINT_SPACE_WORLD;
|
||||||
|
case 1:
|
||||||
|
return CONSTRAINT_SPACE_LOCAL;
|
||||||
|
case 2:
|
||||||
|
return CONSTRAINT_SPACE_POSE;
|
||||||
|
case 3:
|
||||||
|
return CONSTRAINT_SPACE_PARLOCAL;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Value: " + c + " cannot be converted to Space enum instance!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,397 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Stack;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimChannel;
|
||||||
|
import com.jme3.animation.AnimControl;
|
||||||
|
import com.jme3.animation.Animation;
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.animation.BoneTrack;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.animation.Track;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node that represents either spatial or bone in constraint simulation. The
|
||||||
|
* node is applied its translation, rotation and scale for each frame of its
|
||||||
|
* animation. Then the constraints are applied that will eventually alter it.
|
||||||
|
* After that the feature's transformation is stored in VirtualTrack which is
|
||||||
|
* converted to new bone or spatial track at the very end.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class SimulationNode {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
|
||||||
|
|
||||||
|
private Long featureOMA;
|
||||||
|
/** The blender context. */
|
||||||
|
private BlenderContext blenderContext;
|
||||||
|
/** The name of the node (for debugging purposes). */
|
||||||
|
private String name;
|
||||||
|
/** A list of children for the node (either bones or child spatials). */
|
||||||
|
private List<SimulationNode> children = new ArrayList<SimulationNode>();
|
||||||
|
/** A list of node's animations. */
|
||||||
|
private List<Animation> animations;
|
||||||
|
|
||||||
|
/** The nodes spatial (if null then the boneContext should be set). */
|
||||||
|
private Spatial spatial;
|
||||||
|
/** The skeleton of the bone (not null if the node simulated the bone). */
|
||||||
|
private Skeleton skeleton;
|
||||||
|
/** Animation controller for the node's feature. */
|
||||||
|
private AnimControl animControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The star transform of a spatial. Needed to properly reset the spatial to
|
||||||
|
* its start position.
|
||||||
|
*/
|
||||||
|
private Transform spatialStartTransform;
|
||||||
|
/** Star transformations for bones. Needed to properly reset the bones. */
|
||||||
|
private Map<Bone, Transform> boneStartTransforms;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the nodes tree for the given feature. The feature (bone or
|
||||||
|
* spatial) is found by its OMA. The feature must be a root bone or a root
|
||||||
|
* spatial.
|
||||||
|
*
|
||||||
|
* @param featureOMA
|
||||||
|
* the OMA of either bone or spatial
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public SimulationNode(Long featureOMA, BlenderContext blenderContext) {
|
||||||
|
this(featureOMA, blenderContext, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the node for the feature.
|
||||||
|
*
|
||||||
|
* @param featureOMA
|
||||||
|
* the OMA of either bone or spatial
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @param rootNode
|
||||||
|
* indicates if the feature is a root bone or root spatial or not
|
||||||
|
*/
|
||||||
|
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
|
||||||
|
this.featureOMA = featureOMA;
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
|
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedDataType.FEATURE);
|
||||||
|
if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) {
|
||||||
|
skeleton = blenderContext.getSkeleton(featureOMA);
|
||||||
|
|
||||||
|
Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
|
||||||
|
animControl = nodeWithAnimationControl.getControl(AnimControl.class);
|
||||||
|
|
||||||
|
boneStartTransforms = new HashMap<Bone, Transform>();
|
||||||
|
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
|
||||||
|
Bone bone = skeleton.getBone(i);
|
||||||
|
boneStartTransforms.put(bone, new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (rootNode && spatial.getParent() != null) {
|
||||||
|
throw new IllegalStateException("Given spatial must be a root node!");
|
||||||
|
}
|
||||||
|
this.spatial = spatial;
|
||||||
|
spatialStartTransform = spatial.getLocalTransform().clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
name = '>' + spatial.getName() + '<';
|
||||||
|
|
||||||
|
// add children nodes
|
||||||
|
if (skeleton != null) {
|
||||||
|
Node node = blenderContext.getControlledNode(skeleton);
|
||||||
|
Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
|
||||||
|
animations = blenderContext.getAnimations(animatedNodeOMA);
|
||||||
|
} else {
|
||||||
|
animations = blenderContext.getAnimations(featureOMA);
|
||||||
|
for (Spatial child : spatial.getChildren()) {
|
||||||
|
if (child instanceof Node) {
|
||||||
|
children.add(new SimulationNode((Long) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, child), blenderContext, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the node's feature to its starting transformation.
|
||||||
|
*/
|
||||||
|
private void reset() {
|
||||||
|
if (spatial != null) {
|
||||||
|
spatial.setLocalTransform(spatialStartTransform);
|
||||||
|
for (SimulationNode child : children) {
|
||||||
|
child.reset();
|
||||||
|
}
|
||||||
|
} else if (skeleton != null) {
|
||||||
|
for (Entry<Bone, Transform> entry : boneStartTransforms.entrySet()) {
|
||||||
|
Transform t = entry.getValue();
|
||||||
|
entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
|
||||||
|
entry.getKey().updateModelTransforms();
|
||||||
|
}
|
||||||
|
skeleton.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates the spatial node.
|
||||||
|
*/
|
||||||
|
private void simulateSpatial() {
|
||||||
|
List<Constraint> constraints = blenderContext.getConstraints(featureOMA);
|
||||||
|
if (constraints != null && constraints.size() > 0) {
|
||||||
|
LOGGER.fine("Simulating spatial.");
|
||||||
|
boolean applyStaticConstraints = true;
|
||||||
|
if (animations != null) {
|
||||||
|
for (Animation animation : animations) {
|
||||||
|
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
||||||
|
int maxFrame = (int) animationTimeBoundaries[0];
|
||||||
|
float maxTime = animationTimeBoundaries[1];
|
||||||
|
|
||||||
|
VirtualTrack vTrack = new VirtualTrack(spatial.getName(), maxFrame, maxTime);
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
for (int frame = 0; frame < maxFrame; ++frame) {
|
||||||
|
spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]);
|
||||||
|
spatial.setLocalRotation(((SpatialTrack) track).getRotations()[frame]);
|
||||||
|
spatial.setLocalScale(((SpatialTrack) track).getScales()[frame]);
|
||||||
|
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
constraint.apply(frame);
|
||||||
|
vTrack.setTransform(frame, spatial.getLocalTransform());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Track newTrack = vTrack.getAsSpatialTrack();
|
||||||
|
if (newTrack != null) {
|
||||||
|
animation.removeTrack(track);
|
||||||
|
animation.addTrack(newTrack);
|
||||||
|
}
|
||||||
|
applyStaticConstraints = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are no animations then just constraint the static
|
||||||
|
// object's transformation
|
||||||
|
if (applyStaticConstraints) {
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
constraint.apply(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SimulationNode child : children) {
|
||||||
|
child.simulate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates the bone node.
|
||||||
|
*/
|
||||||
|
private void simulateSkeleton() {
|
||||||
|
LOGGER.fine("Simulating skeleton.");
|
||||||
|
Set<Long> alteredOmas = new HashSet<Long>();
|
||||||
|
|
||||||
|
if (animations != null) {
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
AnimChannel animChannel = animControl.createChannel();
|
||||||
|
|
||||||
|
for (Animation animation : animations) {
|
||||||
|
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
||||||
|
int maxFrame = (int) animationTimeBoundaries[0];
|
||||||
|
float maxTime = animationTimeBoundaries[1];
|
||||||
|
|
||||||
|
Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
|
||||||
|
for (int frame = 0; frame < maxFrame; ++frame) {
|
||||||
|
// this MUST be done here, otherwise setting next frame of animation will
|
||||||
|
// lead to possible errors
|
||||||
|
this.reset();
|
||||||
|
|
||||||
|
// first set proper time for all bones in all the tracks ...
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
float time = ((BoneTrack) track).getTimes()[frame];
|
||||||
|
track.setTime(time, 1, animControl, animChannel, vars);
|
||||||
|
skeleton.updateWorldVectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and then apply constraints from the root bone to the last child ...
|
||||||
|
Set<Long> applied = new HashSet<Long>();
|
||||||
|
for (Bone rootBone : skeleton.getRoots()) {
|
||||||
|
// ignore the 0-indexed bone
|
||||||
|
if (skeleton.getBoneIndex(rootBone) > 0) {
|
||||||
|
this.applyConstraints(rootBone, alteredOmas, applied, frame, new Stack<Bone>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... add virtual tracks if necessary, for bones that were altered but had no tracks before ...
|
||||||
|
for (Long boneOMA : alteredOmas) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
|
||||||
|
int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
|
||||||
|
if (!tracks.containsKey(boneIndex)) {
|
||||||
|
tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alteredOmas.clear();
|
||||||
|
|
||||||
|
// ... and fill in another frame in the result track
|
||||||
|
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
||||||
|
Bone bone = skeleton.getBone(trackEntry.getKey());
|
||||||
|
Transform startTransform = boneStartTransforms.get(bone);
|
||||||
|
|
||||||
|
// track contains differences between the frame position and bind positions of bones/spatials
|
||||||
|
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
|
||||||
|
Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
|
||||||
|
Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
|
||||||
|
|
||||||
|
trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
||||||
|
Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
|
||||||
|
if (newTrack != null) {
|
||||||
|
boolean trackReplaced = false;
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
|
||||||
|
animation.removeTrack(track);
|
||||||
|
animation.addTrack(newTrack);
|
||||||
|
trackReplaced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!trackReplaced) {
|
||||||
|
animation.addTrack(newTrack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
animControl.clearChannels();
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies constraints to the given bone and its children.
|
||||||
|
* The goal is to apply constraint from root bone to the last child.
|
||||||
|
* @param bone
|
||||||
|
* the bone whose constraints will be applied
|
||||||
|
* @param alteredOmas
|
||||||
|
* the set of OMAS of the altered bones (is populated if necessary)
|
||||||
|
* @param frame
|
||||||
|
* the current frame of the animation
|
||||||
|
* @param bonesStack
|
||||||
|
* the stack of bones used to avoid infinite loops while applying constraints
|
||||||
|
*/
|
||||||
|
private void applyConstraints(Bone bone, Set<Long> alteredOmas, Set<Long> applied, int frame, Stack<Bone> bonesStack) {
|
||||||
|
if (!bonesStack.contains(bone)) {
|
||||||
|
bonesStack.push(bone);
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
||||||
|
if (!applied.contains(boneContext.getBoneOma())) {
|
||||||
|
List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
||||||
|
if (constraints != null && constraints.size() > 0) {
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) {
|
||||||
|
// first apply constraints of the target bone
|
||||||
|
BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA());
|
||||||
|
this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame, bonesStack);
|
||||||
|
}
|
||||||
|
constraint.apply(frame);
|
||||||
|
if (constraint.getAlteredOmas() != null) {
|
||||||
|
alteredOmas.addAll(constraint.getAlteredOmas());
|
||||||
|
}
|
||||||
|
alteredOmas.add(boneContext.getBoneOma());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applied.add(boneContext.getBoneOma());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Bone> children = bone.getChildren();
|
||||||
|
if (children != null && children.size() > 0) {
|
||||||
|
for (Bone child : bone.getChildren()) {
|
||||||
|
this.applyConstraints(child, alteredOmas, applied, frame, bonesStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bonesStack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates the node.
|
||||||
|
*/
|
||||||
|
public void simulate() {
|
||||||
|
this.reset();
|
||||||
|
if (spatial != null) {
|
||||||
|
this.simulateSpatial();
|
||||||
|
} else {
|
||||||
|
this.simulateSkeleton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the maximum frame and time for the animation. Different tracks
|
||||||
|
* can have different lengths so here the maximum one is being found.
|
||||||
|
*
|
||||||
|
* @param animation
|
||||||
|
* the animation
|
||||||
|
* @return maximum frame and time of the animation
|
||||||
|
*/
|
||||||
|
private float[] computeAnimationTimeBoundaries(Animation animation) {
|
||||||
|
int maxFrame = Integer.MIN_VALUE;
|
||||||
|
float maxTime = -Float.MAX_VALUE;
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
if (track instanceof BoneTrack) {
|
||||||
|
maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length);
|
||||||
|
maxTime = Math.max(maxTime, ((BoneTrack) track).getTimes()[((BoneTrack) track).getTimes().length - 1]);
|
||||||
|
} else if (track instanceof SpatialTrack) {
|
||||||
|
maxFrame = Math.max(maxFrame, ((SpatialTrack) track).getTranslations().length);
|
||||||
|
maxTime = Math.max(maxTime, ((SpatialTrack) track).getTimes()[((SpatialTrack) track).getTimes().length - 1]);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Unsupported track type for simuation: " + track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new float[] { maxFrame, maxTime };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds constraints for the node's features.
|
||||||
|
*
|
||||||
|
* @param ownerOMA
|
||||||
|
* the feature's OMA
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return a list of feature's constraints or empty list if none were found
|
||||||
|
*/
|
||||||
|
private List<Constraint> findConstraints(Long ownerOMA, BlenderContext blenderContext) {
|
||||||
|
List<Constraint> result = new ArrayList<Constraint>();
|
||||||
|
List<Constraint> constraints = blenderContext.getConstraints(ownerOMA);
|
||||||
|
if (constraints != null) {
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) {
|
||||||
|
result.add(constraint);
|
||||||
|
}
|
||||||
|
// TODO: add proper warnings to some map or set so that they are not logged on every frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.size() > 0 ? result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraint applied on the skeleton. This constraint is here only to make the
|
||||||
|
* application not crash when loads constraints applied to armature. But
|
||||||
|
* skeleton movement is not supported by jme so the constraint will never be
|
||||||
|
* applied.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */class SkeletonConstraint extends Constraint {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(SkeletonConstraint.class.getName());
|
||||||
|
|
||||||
|
public SkeletonConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
LOGGER.warning("Constraints for skeleton are not supported.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(int frame) {
|
||||||
|
LOGGER.warning("Applying constraints to skeleton is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTargetOMA() {
|
||||||
|
LOGGER.warning("Constraints for skeleton are not supported.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraint applied on the spatial objects. This includes: nodes, cameras
|
||||||
|
* nodes and light nodes.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */class SpatialConstraint extends Constraint {
|
||||||
|
public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
if (targetOMA != null) {
|
||||||
|
return blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE) != null;
|
||||||
|
}
|
||||||
|
return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTargetOMA() {
|
||||||
|
return targetOMA;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.jme3.animation.BoneTrack;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A virtual track that stores computed frames after constraints are applied.
|
||||||
|
* Not all the frames need to be inserted. If there are lacks then the class
|
||||||
|
* will fill the gaps.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */class VirtualTrack {
|
||||||
|
/** The name of the track (for debugging purposes). */
|
||||||
|
private String name;
|
||||||
|
/** The last frame for the track. */
|
||||||
|
public int maxFrame;
|
||||||
|
/** The max time for the track. */
|
||||||
|
public float maxTime;
|
||||||
|
/** Translations of the track. */
|
||||||
|
public ArrayList<Vector3f> translations;
|
||||||
|
/** Rotations of the track. */
|
||||||
|
public ArrayList<Quaternion> rotations;
|
||||||
|
/** Scales of the track. */
|
||||||
|
public ArrayList<Vector3f> scales;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the object storing the maximum frame and time.
|
||||||
|
*
|
||||||
|
* @param maxFrame
|
||||||
|
* the last frame for the track
|
||||||
|
* @param maxTime
|
||||||
|
* the max time for the track
|
||||||
|
*/
|
||||||
|
public VirtualTrack(String name, int maxFrame, float maxTime) {
|
||||||
|
this.name = name;
|
||||||
|
this.maxFrame = maxFrame;
|
||||||
|
this.maxTime = maxTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the transform for the given frame.
|
||||||
|
*
|
||||||
|
* @param frameIndex
|
||||||
|
* the frame for which the transform will be set
|
||||||
|
* @param transform
|
||||||
|
* the transformation to be set
|
||||||
|
*/
|
||||||
|
public void setTransform(int frameIndex, Transform transform) {
|
||||||
|
if (translations == null) {
|
||||||
|
translations = this.createList(Vector3f.ZERO, frameIndex);
|
||||||
|
}
|
||||||
|
this.append(translations, Vector3f.ZERO, frameIndex - translations.size());
|
||||||
|
translations.add(transform.getTranslation().clone());
|
||||||
|
|
||||||
|
if (rotations == null) {
|
||||||
|
rotations = this.createList(Quaternion.IDENTITY, frameIndex);
|
||||||
|
}
|
||||||
|
this.append(rotations, Quaternion.IDENTITY, frameIndex - rotations.size());
|
||||||
|
rotations.add(transform.getRotation().clone());
|
||||||
|
|
||||||
|
if (scales == null) {
|
||||||
|
scales = this.createList(Vector3f.UNIT_XYZ, frameIndex);
|
||||||
|
}
|
||||||
|
this.append(scales, Vector3f.UNIT_XYZ, frameIndex - scales.size());
|
||||||
|
scales.add(transform.getScale().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the track as a bone track.
|
||||||
|
*
|
||||||
|
* @param targetBoneIndex
|
||||||
|
* the bone index
|
||||||
|
* @return the bone track
|
||||||
|
*/
|
||||||
|
public BoneTrack getAsBoneTrack(int targetBoneIndex) {
|
||||||
|
if (translations == null && rotations == null && scales == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new BoneTrack(targetBoneIndex, this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the track as a spatial track.
|
||||||
|
*
|
||||||
|
* @return the spatial track
|
||||||
|
*/
|
||||||
|
public SpatialTrack getAsSpatialTrack() {
|
||||||
|
if (translations == null && rotations == null && scales == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SpatialTrack(this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method creates times for the track based on the given maximum values.
|
||||||
|
*
|
||||||
|
* @return the times for the track
|
||||||
|
*/
|
||||||
|
private float[] createTimes() {
|
||||||
|
float[] times = new float[maxFrame];
|
||||||
|
float dT = maxTime / maxFrame;
|
||||||
|
float t = 0;
|
||||||
|
for (int i = 0; i < maxFrame; ++i) {
|
||||||
|
times[i] = t;
|
||||||
|
t += dT;
|
||||||
|
}
|
||||||
|
return times;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method that creates a list of a given size filled with given
|
||||||
|
* elements.
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* the element to be put into the list
|
||||||
|
* @param count
|
||||||
|
* the list size
|
||||||
|
* @return the list
|
||||||
|
*/
|
||||||
|
private <T> ArrayList<T> createList(T element, int count) {
|
||||||
|
ArrayList<T> result = new ArrayList<T>(count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the element to the given list.
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* the list where the element will be appended
|
||||||
|
* @param element
|
||||||
|
* the element to be appended
|
||||||
|
* @param count
|
||||||
|
* how many times the element will be appended
|
||||||
|
*/
|
||||||
|
private <T> void append(ArrayList<T> list, T element, int count) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
list.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder(2048);
|
||||||
|
result.append("TRACK: ").append(name).append('\n');
|
||||||
|
if (translations != null && translations.size() > 0) {
|
||||||
|
result.append("TRANSLATIONS: ").append(translations.toString()).append('\n');
|
||||||
|
}
|
||||||
|
if (rotations != null && rotations.size() > 0) {
|
||||||
|
result.append("ROTATIONS: ").append(rotations.toString()).append('\n');
|
||||||
|
}
|
||||||
|
if (scales != null && scales.size() > 0) {
|
||||||
|
result.append("SCALES: ").append(scales.toString()).append('\n');
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class for all constraint definitions.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public abstract class ConstraintDefinition {
|
||||||
|
protected ConstraintHelper constraintHelper;
|
||||||
|
/** Constraints flag. Used to load user's options applied to the constraint. */
|
||||||
|
protected int flag;
|
||||||
|
/** The constraint's owner. Loaded during runtime. */
|
||||||
|
private Object owner;
|
||||||
|
/** The blender context. */
|
||||||
|
protected BlenderContext blenderContext;
|
||||||
|
/** The constraint's owner OMA. */
|
||||||
|
protected Long ownerOMA;
|
||||||
|
/** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
|
||||||
|
protected Set<Long> alteredOmas;
|
||||||
|
/** The variable that determines if the constraint will alter the track in any way. */
|
||||||
|
protected boolean trackToBeChanged = true;
|
||||||
|
/** The name of the constraint. */
|
||||||
|
protected String constraintName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a constraint definition based on the constraint definition
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* @param constraintData
|
||||||
|
* the constraint definition structure
|
||||||
|
* @param ownerOMA
|
||||||
|
* the constraint's owner OMA
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public ConstraintDefinition(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
|
if (constraintData != null) {// Null constraint has no data
|
||||||
|
Number flag = (Number) constraintData.getFieldValue("flag");
|
||||||
|
if (flag != null) {
|
||||||
|
this.flag = flag.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
|
constraintHelper = (ConstraintHelper) (blenderContext == null ? null : blenderContext.getHelper(ConstraintHelper.class));
|
||||||
|
this.ownerOMA = ownerOMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConstraintName(String constraintName) {
|
||||||
|
this.constraintName = constraintName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return determines if the definition of the constraint will change the bone in any way; in most cases
|
||||||
|
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
|
||||||
|
* computing to improve the computation speed and lower the computations complexity
|
||||||
|
*/
|
||||||
|
public boolean isTrackToBeChanged() {
|
||||||
|
return trackToBeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return determines if this constraint definition requires a defined target or not
|
||||||
|
*/
|
||||||
|
public abstract boolean isTargetRequired();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is here because we have no guarantee that the owner is loaded
|
||||||
|
* when constraint is being created. So use it to get the owner when it is
|
||||||
|
* needed for computations.
|
||||||
|
*
|
||||||
|
* @return the owner of the constraint or null if none is set
|
||||||
|
*/
|
||||||
|
protected Object getOwner() {
|
||||||
|
if (ownerOMA != null && owner == null) {
|
||||||
|
owner = blenderContext.getLoadedFeature(ownerOMA, LoadedDataType.FEATURE);
|
||||||
|
if (owner == null) {
|
||||||
|
throw new IllegalStateException("Cannot load constraint's owner for constraint type: " + this.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method gets the owner's transformation. The owner can be either bone or spatial.
|
||||||
|
* @param ownerSpace
|
||||||
|
* the space in which the computed transformation is given
|
||||||
|
* @return the constraint owner's transformation
|
||||||
|
*/
|
||||||
|
protected Transform getOwnerTransform(Space ownerSpace) {
|
||||||
|
if (this.getOwner() instanceof Bone) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
return constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
}
|
||||||
|
return constraintHelper.getTransform(ownerOMA, null, ownerSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method applies the given transformation to the owner.
|
||||||
|
* @param ownerTransform
|
||||||
|
* the transformation to apply to the owner
|
||||||
|
* @param ownerSpace
|
||||||
|
* the space that defines which owner's transformation (ie. global, local, etc. will be set)
|
||||||
|
*/
|
||||||
|
protected void applyOwnerTransform(Transform ownerTransform, Space ownerSpace) {
|
||||||
|
if (this.getOwner() instanceof Bone) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
|
} else {
|
||||||
|
constraintHelper.applyTransform(ownerOMA, null, ownerSpace, ownerTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <b>true</b> if the definition is implemented and <b>false</b>
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
public boolean isImplemented() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of all OMAs of the features that the constraint had altered beside its owner
|
||||||
|
*/
|
||||||
|
public Set<Long> getAlteredOmas() {
|
||||||
|
return alteredOmas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the type name of the constraint
|
||||||
|
*/
|
||||||
|
public abstract String getConstraintTypeName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bakes the constraint for the current feature (bone or spatial) position.
|
||||||
|
*
|
||||||
|
* @param ownerSpace
|
||||||
|
* the space where owner transform will be evaluated in
|
||||||
|
* @param targetSpace
|
||||||
|
* the space where target transform will be evaluated in
|
||||||
|
* @param targetTransform
|
||||||
|
* the target transform used by some of the constraints
|
||||||
|
* @param influence
|
||||||
|
* the influence of the constraint (from range <0; 1>)
|
||||||
|
*/
|
||||||
|
public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getConstraintTypeName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents 'Dist limit' constraint type in blender.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */class ConstraintDefinitionDistLimit extends ConstraintDefinition {
|
||||||
|
private static final int LIMITDIST_INSIDE = 0;
|
||||||
|
private static final int LIMITDIST_OUTSIDE = 1;
|
||||||
|
private static final int LIMITDIST_ONSURFACE = 2;
|
||||||
|
|
||||||
|
protected int mode;
|
||||||
|
protected float dist;
|
||||||
|
|
||||||
|
public ConstraintDefinitionDistLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
|
mode = ((Number) constraintData.getFieldValue("mode")).intValue();
|
||||||
|
dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
|
||||||
|
// distance limit does not work on bones who are connected to their parent
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (influence == 0 || targetTransform == null) {
|
||||||
|
return;// no need to do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
|
||||||
|
|
||||||
|
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
|
||||||
|
float currentDistance = v.length();
|
||||||
|
switch (mode) {
|
||||||
|
case LIMITDIST_INSIDE:
|
||||||
|
if (currentDistance >= dist) {
|
||||||
|
v.normalizeLocal();
|
||||||
|
v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
|
||||||
|
ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIMITDIST_ONSURFACE:
|
||||||
|
if (currentDistance > dist) {
|
||||||
|
v.normalizeLocal();
|
||||||
|
v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
|
||||||
|
ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
|
||||||
|
} else if (currentDistance < dist) {
|
||||||
|
v.normalizeLocal().multLocal(dist * influence);
|
||||||
|
ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIMITDIST_OUTSIDE:
|
||||||
|
if (currentDistance <= dist) {
|
||||||
|
v = targetTransform.getTranslation().subtract(ownerTransform.getTranslation()).normalizeLocal().multLocal(dist * influence);
|
||||||
|
ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.applyOwnerTransform(ownerTransform, ownerSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTargetRequired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Limit distance";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
public class ConstraintDefinitionFactory {
|
||||||
|
private static final Map<String, Class<? extends ConstraintDefinition>> CONSTRAINT_CLASSES = new HashMap<String, Class<? extends ConstraintDefinition>>();
|
||||||
|
static {
|
||||||
|
CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bLocLimitConstraint", ConstraintDefinitionLocLimit.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bNullConstraint", ConstraintDefinitionNull.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bRotateLikeConstraint", ConstraintDefinitionRotLike.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionIK.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bTransLikeConstraint", ConstraintDefinitionTransLike.class);// since blender 2.51
|
||||||
|
CONSTRAINT_CLASSES.put("bSameVolumeConstraint", ConstraintDefinitionMaintainVolume.class);// since blender 2.53
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<String, String> UNSUPPORTED_CONSTRAINTS = new HashMap<String, String>();
|
||||||
|
static {
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bActionConstraint", "Action");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bRigidBodyJointConstraint", "Rigid body joint");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bShrinkWrapConstraint", "Shrinkwrap");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bStretchToConstraint", "Stretch to");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bTransformConstraint", "Transform");
|
||||||
|
// Blender 2.50+
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bSplineIKConstraint", "Spline inverse kinematics");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bDampTrackConstraint", "Damp track");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bPivotConstraint", "Pivot");
|
||||||
|
// Blender 2.56+
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bTrackToConstraint", "Track to");
|
||||||
|
// Blender 2.62+
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bCameraSolverConstraint", "Camera solver");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bObjectSolverConstraint", "Object solver");
|
||||||
|
UNSUPPORTED_CONSTRAINTS.put("bFollowTrackConstraint", "Follow track");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates the constraint instance.
|
||||||
|
*
|
||||||
|
* @param constraintStructure
|
||||||
|
* the constraint's structure (bConstraint clss in blender 2.49).
|
||||||
|
* If the value is null the NullConstraint is created.
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* this exception is thrown when the blender file is somehow
|
||||||
|
* corrupted
|
||||||
|
*/
|
||||||
|
public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, String constraintName, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
|
if (constraintStructure == null) {
|
||||||
|
return new ConstraintDefinitionNull(null, ownerOMA, blenderContext);
|
||||||
|
}
|
||||||
|
String constraintClassName = constraintStructure.getType();
|
||||||
|
Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
|
||||||
|
if (constraintDefinitionClass != null) {
|
||||||
|
try {
|
||||||
|
ConstraintDefinition def = (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext);
|
||||||
|
def.setConstraintName(constraintName);
|
||||||
|
return def;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String unsupportedConstraintClassName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName);
|
||||||
|
if (unsupportedConstraintClassName != null) {
|
||||||
|
return new UnsupportedConstraintDefinition(unsupportedConstraintClassName);
|
||||||
|
} else {
|
||||||
|
throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.ejml.simple.SimpleMatrix;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.math.DQuaternion;
|
||||||
|
import com.jme3.scene.plugins.blender.math.DTransform;
|
||||||
|
import com.jme3.scene.plugins.blender.math.Matrix;
|
||||||
|
import com.jme3.scene.plugins.blender.math.Vector3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A definiotion of a Inverse Kinematics constraint. This implementation uses Jacobian pseudoinverse algorithm.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class ConstraintDefinitionIK extends ConstraintDefinition {
|
||||||
|
private static final float MIN_DISTANCE = 0.001f;
|
||||||
|
private static final float MIN_ANGLE_CHANGE = 0.001f;
|
||||||
|
private static final int FLAG_USE_TAIL = 0x01;
|
||||||
|
private static final int FLAG_POSITION = 0x20;
|
||||||
|
|
||||||
|
private BonesChain bones;
|
||||||
|
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
|
||||||
|
private int bonesAffected;
|
||||||
|
/** Indicates if the tail of the bone should be used or not. */
|
||||||
|
private boolean useTail;
|
||||||
|
/** The amount of iterations of the algorithm. */
|
||||||
|
private int iterations;
|
||||||
|
/** The count of bones' chain. */
|
||||||
|
private int bonesCount = -1;
|
||||||
|
|
||||||
|
public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
|
bonesAffected = ((Number) constraintData.getFieldValue("rootbone")).intValue();
|
||||||
|
iterations = ((Number) constraintData.getFieldValue("iterations")).intValue();
|
||||||
|
useTail = (flag & FLAG_USE_TAIL) != 0;
|
||||||
|
|
||||||
|
if ((flag & FLAG_POSITION) == 0) {
|
||||||
|
trackToBeChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackToBeChanged) {
|
||||||
|
alteredOmas = new HashSet<Long>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Below are the variables that only need to be allocated once for IK constraint instance.
|
||||||
|
*/
|
||||||
|
/** Temporal quaternion. */
|
||||||
|
private DQuaternion tempDQuaternion = new DQuaternion();
|
||||||
|
/** Temporal matrix column. */
|
||||||
|
private Vector3d col = new Vector3d();
|
||||||
|
/** Effector's position change. */
|
||||||
|
private Matrix deltaP = new Matrix(3, 1);
|
||||||
|
/** The current target position. */
|
||||||
|
private Vector3d target = new Vector3d();
|
||||||
|
/** Rotation vectors for each joint (allocated when we know the size of a bones' chain. */
|
||||||
|
private Vector3d[] rotationVectors;
|
||||||
|
/** The Jacobian matrix. Allocated when the bones' chain size is known. */
|
||||||
|
private Matrix J;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
|
if (influence == 0 || !trackToBeChanged || targetTransform == null || bonesCount == 0) {
|
||||||
|
return;// no need to do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bones == null) {
|
||||||
|
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, alteredOmas, blenderContext);
|
||||||
|
}
|
||||||
|
if (bones.size() == 0) {
|
||||||
|
bonesCount = 0;
|
||||||
|
return;// no need to do anything
|
||||||
|
}
|
||||||
|
double distanceFromTarget = Double.MAX_VALUE;
|
||||||
|
target.set(targetTransform.getTranslation().x, targetTransform.getTranslation().y, targetTransform.getTranslation().z);
|
||||||
|
|
||||||
|
if (bonesCount < 0) {
|
||||||
|
bonesCount = bones.size();
|
||||||
|
rotationVectors = new Vector3d[bonesCount];
|
||||||
|
for (int i = 0; i < bonesCount; ++i) {
|
||||||
|
rotationVectors[i] = new Vector3d();
|
||||||
|
}
|
||||||
|
J = new Matrix(3, bonesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoneContext topBone = bones.get(0);
|
||||||
|
for (int i = 0; i < iterations; ++i) {
|
||||||
|
DTransform topBoneTransform = bones.getWorldTransform(topBone);
|
||||||
|
Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
|
||||||
|
distanceFromTarget = e.distance(target);
|
||||||
|
if (distanceFromTarget <= MIN_DISTANCE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
deltaP.setColumn(0, 0, target.x - e.x, target.y - e.y, target.z - e.z);
|
||||||
|
int column = 0;
|
||||||
|
for (BoneContext boneContext : bones) {
|
||||||
|
DTransform boneWorldTransform = bones.getWorldTransform(boneContext);
|
||||||
|
Vector3d j = boneWorldTransform.getTranslation(); // current join position
|
||||||
|
Vector3d vectorFromJointToEffector = e.subtract(j);
|
||||||
|
vectorFromJointToEffector.cross(target.subtract(j), rotationVectors[column]).normalizeLocal();
|
||||||
|
rotationVectors[column].cross(vectorFromJointToEffector, col);
|
||||||
|
J.setColumn(col, column++);
|
||||||
|
}
|
||||||
|
Matrix J_1 = J.pseudoinverse();
|
||||||
|
|
||||||
|
SimpleMatrix deltaThetas = J_1.mult(deltaP);
|
||||||
|
if (deltaThetas.elementMaxAbs() < MIN_ANGLE_CHANGE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < deltaThetas.numRows(); ++j) {
|
||||||
|
double angle = deltaThetas.get(j, 0);
|
||||||
|
Vector3d rotationVector = rotationVectors[j];
|
||||||
|
|
||||||
|
tempDQuaternion.fromAngleAxis(angle, rotationVector);
|
||||||
|
BoneContext boneContext = bones.get(j);
|
||||||
|
Bone bone = boneContext.getBone();
|
||||||
|
if (bone.equals(this.getOwner())) {
|
||||||
|
if (boneContext.isLockX()) {
|
||||||
|
tempDQuaternion.set(0, tempDQuaternion.getY(), tempDQuaternion.getZ(), tempDQuaternion.getW());
|
||||||
|
}
|
||||||
|
if (boneContext.isLockY()) {
|
||||||
|
tempDQuaternion.set(tempDQuaternion.getX(), 0, tempDQuaternion.getZ(), tempDQuaternion.getW());
|
||||||
|
}
|
||||||
|
if (boneContext.isLockZ()) {
|
||||||
|
tempDQuaternion.set(tempDQuaternion.getX(), tempDQuaternion.getY(), 0, tempDQuaternion.getW());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DTransform boneTransform = bones.getWorldTransform(boneContext);
|
||||||
|
boneTransform.getRotation().set(tempDQuaternion.mult(boneTransform.getRotation()));
|
||||||
|
bones.setWorldTransform(boneContext, boneTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applying the results
|
||||||
|
for (int i = bonesCount - 1; i >= 0; --i) {
|
||||||
|
BoneContext boneContext = bones.get(i);
|
||||||
|
DTransform transform = bones.getWorldTransform(boneContext);
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform());
|
||||||
|
}
|
||||||
|
bones = null;// need to reload them again
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Inverse kinematics";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTargetRequired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loaded bones' chain. This class allows to operate on transform matrices that use double precision in computations.
|
||||||
|
* Only the final result is being transformed to single precision numbers.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
private static class BonesChain extends ArrayList<BoneContext> {
|
||||||
|
private static final long serialVersionUID = -1850524345643600718L;
|
||||||
|
|
||||||
|
private List<Matrix> localBonesMatrices = new ArrayList<Matrix>();
|
||||||
|
|
||||||
|
public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection<Long> alteredOmas, BlenderContext blenderContext) {
|
||||||
|
if (bone != null) {
|
||||||
|
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
|
if (!useTail) {
|
||||||
|
bone = bone.getParent();
|
||||||
|
}
|
||||||
|
while (bone != null && (bonesAffected <= 0 || this.size() < bonesAffected)) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
||||||
|
this.add(boneContext);
|
||||||
|
alteredOmas.add(boneContext.getBoneOma());
|
||||||
|
|
||||||
|
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
|
||||||
|
localBonesMatrices.add(new DTransform(transform).toMatrix());
|
||||||
|
|
||||||
|
bone = bone.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(localBonesMatrices.size() > 0) {
|
||||||
|
// making the matrices describe the local transformation
|
||||||
|
Matrix parentWorldMatrix = localBonesMatrices.get(localBonesMatrices.size() - 1);
|
||||||
|
for(int i=localBonesMatrices.size() - 2;i>=0;--i) {
|
||||||
|
SimpleMatrix m = parentWorldMatrix.invert().mult(localBonesMatrices.get(i));
|
||||||
|
parentWorldMatrix = localBonesMatrices.get(i);
|
||||||
|
localBonesMatrices.set(i, new Matrix(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DTransform getWorldTransform(BoneContext bone) {
|
||||||
|
int index = this.indexOf(bone);
|
||||||
|
return this.getWorldMatrix(index).toTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorldTransform(BoneContext bone, DTransform transform) {
|
||||||
|
int index = this.indexOf(bone);
|
||||||
|
Matrix boneMatrix = transform.toMatrix();
|
||||||
|
|
||||||
|
if (index < this.size() - 1) {
|
||||||
|
// computing the current bone local transform
|
||||||
|
Matrix parentWorldMatrix = this.getWorldMatrix(index + 1);
|
||||||
|
SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix);
|
||||||
|
boneMatrix = new Matrix(m);
|
||||||
|
}
|
||||||
|
localBonesMatrices.set(index, boneMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix getWorldMatrix(int index) {
|
||||||
|
if (index == this.size() - 1) {
|
||||||
|
return new Matrix(localBonesMatrices.get(this.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleMatrix result = this.getWorldMatrix(index + 1);
|
||||||
|
result = result.mult(localBonesMatrices.get(index));
|
||||||
|
return new Matrix(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user