diff --git a/.gitignore b/.gitignore
index 6858b0c8e..aeb771d9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
/jme3-desktop/build/
/jme3-android-native/build/
/jme3-android/build/
+/jme3-android-examples/build/
/jme3-blender/build/
/jme3-effects/build/
/jme3-bullet/build/
diff --git a/.travis.yml b/.travis.yml
index 25aedbbe0..71c3b576f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,41 @@
language: java
-# jdk:
-# - oraclejdk8
+sudo: false
+env:
+ - GRADLE_USER_HOME=gradle-cache
+
+cache:
+ directories:
+ - gradle-cache
+ - netbeans
branches:
only:
- master
-before_install:
+notifications:
+ slack:
+ on_success: change
+ on_failure: always
+ rooms:
+ secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s="
+
+install:
+ - ./gradlew assemble
+
+script:
+ - ./gradlew check
+ - ./gradlew createZipDistribution
+
+deploy:
+ provider: releases
+ api_key:
+ secure: "KbFiMt0a8FxUKvCJUYwikLYaqqGMn1p6k4OsXnGqwptQZEUIayabNLHeaD2kTNT3e6AY1ETwQLff/lB2LttmIo4g5NWW63g1K3A/HwgnhJwETengiProZ/Udl+ugPeDL/+ar43HUhFq4knBnzFKnEcHAThTPVqH/RMDvZf1UUYI="
+ file: build/distributions/jME3.1.0_snapshot-github_2015-06-20.zip
+ skip_cleanup: true
+ on:
+ tags: true
+
+# before_install:
# required libs for android build tools
# sudo apt-get update
# sudo apt-get install -qq p7zip-full
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ebecab28b..82ffeb4e8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,13 +16,6 @@ When you're ready to submit your code, just make a [pull request](https://help.g
- When committing, always be sure to run an update before you commit. If there is a conflict between the latest revision and your patch after the update, then it is your responsibility to track down the update that caused the conflict and determine the issue (and fix it). In the case where the breaking commit has no thread linked (and one cannot be found in the forum), then the contributor should contact an administrator and wait for feedback before committing.
- If your code is committed and it introduces new functionality, please edit the wiki accordingly. We can easily roll back to previous revisions, so just do your best; point us to it and we’ll see if it sticks!
-**Note to Eclipse users:** The Eclipse [git client does not support https](http://hub.jmonkeyengine.org/forum/topic/problem-cloning-the-new-git-repository/#post-265594). The current workaround is to use the command line to clone the repository.
-To import the local repository as a project follow these steps:
-
-1. Add a line 'apply plugin: eclipse' to your common.gradle file in the main project directory.
-2. Navigate to the project directory in command line and execute command 'gradle eclipse'. This will load all the dependancies for eclipse.
-3. In Eclipse, add the repository as an existing Java Project.
-
p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/2013/04/03/issue-etiquette.html) when it comes to GitHub etiquette. If at any point we fail to uphold this standard, let us know.
#### Core Contributors
diff --git a/README.md b/README.md
index 69d2207bd..29161ffa4 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
-jMonkeyEngine
+jMonkeyEngine
=============
-jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open source, cross platform and cutting edge. And it is all beautifully documented. The 3.0 branch is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll be frequently submitting stable 3.0.x updates until the major 3.1 version arrives in Q4 2014.
+[](https://travis-ci.org/jMonkeyEngine/jmonkeyengine)
+
+jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open source, cross platform and cutting edge. And it is all beautifully documented. The 3.0 branch is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll be frequently submitting stable 3.0.x updates until the major 3.1 version arrives.
The engine is used by several commercial game studios and computer-science courses. Here's a taste:
diff --git a/build.gradle b/build.gradle
index adb31217b..8e6790230 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,14 +1,23 @@
import org.gradle.api.artifacts.*
-apply plugin: 'base' // To add "clean" task to the root project.
-//apply plugin: 'java-library-distribution'
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.1.0'
+ }
+}
+
+apply plugin: 'base'
// This is applied to all sub projects
subprojects {
- // Don't add to native builds
- // if(!project.name.endsWith('native')){
- apply from: rootProject.file('common.gradle')
- // }
+ if(!project.name.equals('jme3-android-examples')) {
+ apply from: rootProject.file('common.gradle')
+ } else {
+ apply from: rootProject.file('common-android-app.gradle')
+ }
}
task run(dependsOn: ':jme3-examples:run') {
@@ -166,11 +175,11 @@ ext {
// }
//}
-allprojects {
- tasks.withType(JavaExec) {
- enableAssertions = true // false by default
- }
- tasks.withType(Test) {
- enableAssertions = true // true by default
- }
-}
\ No newline at end of file
+//allprojects {
+// tasks.withType(JavaExec) {
+// enableAssertions = true // false by default
+// }
+// tasks.withType(Test) {
+// enableAssertions = true // true by default
+// }
+//}
\ No newline at end of file
diff --git a/common-android-app.gradle b/common-android-app.gradle
new file mode 100644
index 000000000..0ec48fbd7
--- /dev/null
+++ b/common-android-app.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'com.android.application'
+
+group = 'com.jme3'
+version = jmeVersion + '-' + jmeVersionTag
+
+sourceCompatibility = '1.6'
+
+repositories {
+ mavenCentral()
+ maven {
+ url "http://nifty-gui.sourceforge.net/nifty-maven-repo"
+ }
+}
\ No newline at end of file
diff --git a/common.gradle b/common.gradle
index 9d65f7338..6af4c664f 100644
--- a/common.gradle
+++ b/common.gradle
@@ -4,16 +4,17 @@
apply plugin: 'java'
apply plugin: 'maven'
+apply plugin: 'maven-publish'
-String mavenGroupId = 'com.jme3'
-String mavenVersion = jmeVersion + '-' + jmeVersionTag //'-SNAPSHOT'
+group = 'com.jme3'
+version = jmeVersion + '-' + jmeVersionTag
sourceCompatibility = '1.6'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
repositories {
mavenCentral()
- maven{
+ maven {
url "http://nifty-gui.sourceforge.net/nifty-maven-repo"
}
}
@@ -23,11 +24,6 @@ dependencies {
testCompile group: 'junit', name: 'junit', version: '4.10'
}
-String mavenArtifactId = name
-
-group = mavenGroupId
-version = mavenVersion
-
javadoc {
failOnError = false
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
@@ -60,11 +56,40 @@ artifacts {
}
}
-configure(install.repositories.mavenInstaller) {
- pom.project {
- groupId = mavenGroupId
- artifactId = mavenArtifactId
- version = mavenVersion
+publishing {
+ publications {
+ maven(MavenPublication) {
+ from components.java
+ artifact sourcesJar
+ artifact javadocJar
+
+ pom.withXml {
+ asNode().children().last() + {
+ resolveStrategy = Closure.DELEGATE_FIRST
+ name POM_NAME
+ description POM_DESCRIPTION
+ url POM_URL
+ scm {
+ url POM_SCM_URL
+ connection POM_SCM_CONNECTION
+ developerConnection POM_SCM_DEVELOPER_CONNECTION
+ }
+ licenses {
+ license {
+ name POM_LICENSE_NAME
+ url POM_LICENSE_URL
+ distribution POM_LICENSE_DISTRIBUTION
+ }
+ }
+ }
+ }
+ }
+ }
+
+ repositories {
+ maven {
+ url "${rootProject.buildDir}/repo" // change to point to your repo, e.g. http://my.org/repo
+ }
}
}
diff --git a/gradle.properties b/gradle.properties
index 00429d65a..a00139bdf 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,6 +11,7 @@ buildJavaDoc = true
# specify if SDK and Native libraries get built
buildSdkProject = true
buildNativeProjects = false
+buildAndroidExamples = false
# Path to android NDK for building native libraries
#ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7
@@ -23,3 +24,14 @@ bulletZipFile = bullet.zip
# Path for downloading NetBeans Base
netbeansUrl = http://download.netbeans.org/netbeans/8.0.2/final/zip/netbeans-8.0.2-201411181905-javase.zip
+
+# POM settings
+POM_NAME=jMonkeyEngine
+POM_DESCRIPTION=jMonkeyEngine is a 3D game engine for adventurous Java developers
+POM_URL=http://jmonkeyengine.org
+POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine
+POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git
+POM_SCM_DEVELOPER_CONNECTION=scm:git:git@github.com:jMonkeyEngine/jmonkeyengine.git
+POM_LICENSE_NAME=New BSD (3-clause) License
+POM_LICENSE_URL=http://opensource.org/licenses/BSD-3-Clause
+POM_LICENSE_DISTRIBUTION=repo
diff --git a/jme3-android-examples/build.gradle b/jme3-android-examples/build.gradle
new file mode 100644
index 000000000..f1ee38739
--- /dev/null
+++ b/jme3-android-examples/build.gradle
@@ -0,0 +1,40 @@
+dependencies {
+ compile project(':jme3-core')
+ compile project(':jme3-android')
+ compile project(':jme3-effects')
+ compile project(':jme3-bullet')
+ compile project(':jme3-bullet-native-android')
+ compile project(':jme3-networking')
+ compile project(':jme3-niftygui')
+ compile project(':jme3-plugins')
+ compile project(':jme3-terrain')
+ compile project(':jme3-testdata')
+}
+
+android {
+ compileSdkVersion 10
+ buildToolsVersion "22.0.1"
+
+ lintOptions {
+ // Fix nifty gui referencing "java.awt" package.
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ applicationId "com.jme3.android"
+ minSdkVersion 10 // Android 2.3 GINGERBREAD
+ targetSdkVersion 22 // Android 5.1 LOLLIPOP
+ versionCode 1
+ versionName "1.0" // TODO: from settings.gradle
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ debug {
+ applicationIdSuffix ".debug"
+ debuggable true
+ }
+ }
+}
\ No newline at end of file
diff --git a/jme3-android-examples/src/main/AndroidManifest.xml b/jme3-android-examples/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ee80b6b28
--- /dev/null
+++ b/jme3-android-examples/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jme3-android-examples/src/main/java/jme3test/android/TestChooserAndroid.java b/jme3-android-examples/src/main/java/jme3test/android/TestChooserAndroid.java
new file mode 100644
index 000000000..e704bc85a
--- /dev/null
+++ b/jme3-android-examples/src/main/java/jme3test/android/TestChooserAndroid.java
@@ -0,0 +1,12 @@
+package jme3test.android;
+
+import android.content.pm.ActivityInfo;
+import android.app.*;
+import android.os.Bundle;
+
+public class TestChooserAndroid extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+}
\ No newline at end of file
diff --git a/jme3-android-examples/src/main/res/values/strings.xml b/jme3-android-examples/src/main/res/values/strings.xml
new file mode 100644
index 000000000..b184fa936
--- /dev/null
+++ b/jme3-android-examples/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+ JMEAndroidTest
+ About
+ Quit
+
\ No newline at end of file
diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
index 1d2835bc3..b103e92df 100644
--- a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
+++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
@@ -1,586 +1,580 @@
-package com.jme3.app;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.NinePatchDrawable;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.*;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-import com.jme3.audio.AudioRenderer;
-import com.jme3.input.JoyInput;
-import com.jme3.input.TouchInput;
-import com.jme3.input.android.AndroidSensorJoyInput;
-import com.jme3.input.controls.TouchListener;
-import com.jme3.input.controls.TouchTrigger;
-import com.jme3.input.event.TouchEvent;
-import com.jme3.system.AppSettings;
-import com.jme3.system.SystemListener;
-import com.jme3.system.android.JmeAndroidSystem;
-import com.jme3.system.android.OGLESContext;
-import com.jme3.util.AndroidLogHandler;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-/**
- * AndroidHarness wraps a jme application object and runs it on
- * Android
- *
- * @author Kirill
- * @author larynx
- */
-public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener {
-
- protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
- /**
- * The application class to start
- */
- protected String appClass = "jme3test.android.Test";
- /**
- * The jme3 application object
- */
- protected Application app = null;
-
- /**
- * Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888.
- * (default = 24)
- */
- protected int eglBitsPerPixel = 24;
-
- /**
- * Sets the desired number of Alpha bits for the surfaceview. This affects
- * how the surfaceview is able to display Android views that are located
- * under the surfaceview jME uses to render the scenegraph.
- * 0 = Opaque surfaceview background (fastest)
- * 1->7 = Transparent surfaceview background
- * 8 or higher = Translucent surfaceview background
- * (default = 0)
- */
- protected int eglAlphaBits = 0;
-
- /**
- * The number of depth bits specifies the precision of the depth buffer.
- * (default = 16)
- */
- protected int eglDepthBits = 16;
-
- /**
- * Sets the number of samples to use for multisampling.
- * Leave 0 (default) to disable multisampling.
- * Set to 2 or 4 to enable multisampling.
- */
- protected int eglSamples = 0;
-
- /**
- * Set the number of stencil bits.
- * (default = 0)
- */
- protected int eglStencilBits = 0;
-
- /**
- * Set the desired frame rate. If frameRate > 0, the application
- * will be capped at the desired frame rate.
- * (default = -1, no frame rate cap)
- */
- protected int frameRate = -1;
-
- /**
- * Sets the type of Audio Renderer to be used.
- *
- * Android MediaPlayer / SoundPool can be used on all
- * supported Android platform versions (2.2+)
- * OpenAL Soft uses an OpenSL backend and is only supported on Android
- * versions 2.3+.
- *
- * Only use ANDROID_ static strings found in AppSettings
- *
- */
- protected String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT;
-
- /**
- * If true Android Sensors are used as simulated Joysticks. Users can use the
- * Android sensor feedback through the RawInputListener or by registering
- * JoyAxisTriggers.
- */
- protected boolean joystickEventsEnabled = false;
- /**
- * If true KeyEvents are generated from TouchEvents
- */
- protected boolean keyEventsEnabled = true;
- /**
- * If true MouseEvents are generated from TouchEvents
- */
- protected boolean mouseEventsEnabled = true;
- /**
- * Flip X axis
- */
- protected boolean mouseEventsInvertX = false;
- /**
- * Flip Y axis
- */
- protected boolean mouseEventsInvertY = false;
- /**
- * if true finish this activity when the jme app is stopped
- */
- protected boolean finishOnAppStop = true;
- /**
- * set to false if you don't want the harness to handle the exit hook
- */
- protected boolean handleExitHook = true;
- /**
- * Title of the exit dialog, default is "Do you want to exit?"
- */
- protected String exitDialogTitle = "Do you want to exit?";
- /**
- * Message of the exit dialog, default is "Use your home key to bring this
- * app into the background or exit to terminate it."
- */
- protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
- /**
- * Set the screen window mode. If screenFullSize is true, then the
- * notification bar and title bar are removed and the screen covers the
- * entire display. If screenFullSize is false, then the notification bar
- * remains visible if screenShowTitle is true while screenFullScreen is
- * false, then the title bar is also displayed under the notification bar.
- */
- protected boolean screenFullScreen = true;
- /**
- * if screenShowTitle is true while screenFullScreen is false, then the
- * title bar is also displayed under the notification bar
- */
- protected boolean screenShowTitle = true;
- /**
- * Splash Screen picture Resource ID. If a Splash Screen is desired, set
- * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
- * splashPicID = 0, then no splash screen will be displayed.
- */
- protected int splashPicID = 0;
-
- /**
- * No longer used - Use the android:screenOrientation declaration in
- * the AndroidManifest.xml file.
- */
- @Deprecated
- protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
-
- protected OGLESContext ctx;
- protected GLSurfaceView view = null;
- protected boolean isGLThreadPaused = true;
- protected ImageView splashImageView = null;
- protected FrameLayout frameLayout = null;
- final private String ESCAPE_EVENT = "TouchEscape";
- private boolean firstDrawFrame = true;
- private boolean inConfigChange = false;
-
- private class DataObject {
- protected Application app = null;
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- logger.log(Level.FINE, "onRetainNonConfigurationInstance");
- final DataObject data = new DataObject();
- data.app = this.app;
- inConfigChange = true;
- return data;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- initializeLogHandler();
-
- logger.fine("onCreate");
- super.onCreate(savedInstanceState);
-
- if (screenFullScreen) {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- } else {
- if (!screenShowTitle) {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- }
- }
-
- final DataObject data = (DataObject) getLastNonConfigurationInstance();
- if (data != null) {
- logger.log(Level.FINE, "Using Retained App");
- this.app = data.app;
- } else {
- // Discover the screen reolution
- //TODO try to find a better way to get a hand on the resolution
- WindowManager wind = this.getWindowManager();
- Display disp = wind.getDefaultDisplay();
- Log.d("AndroidHarness", "Resolution from Window, width:" + disp.getWidth() + ", height: " + disp.getHeight());
-
- // Create Settings
- logger.log(Level.FINE, "Creating settings");
- AppSettings settings = new AppSettings(true);
- settings.setEmulateMouse(mouseEventsEnabled);
- settings.setEmulateMouseFlipAxis(mouseEventsInvertX, mouseEventsInvertY);
- settings.setUseJoysticks(joystickEventsEnabled);
- settings.setEmulateKeyboard(keyEventsEnabled);
-
- settings.setBitsPerPixel(eglBitsPerPixel);
- settings.setAlphaBits(eglAlphaBits);
- settings.setDepthBits(eglDepthBits);
- settings.setSamples(eglSamples);
- settings.setStencilBits(eglStencilBits);
-
- settings.setResolution(disp.getWidth(), disp.getHeight());
- settings.setAudioRenderer(audioRendererType);
-
- settings.setFrameRate(frameRate);
-
- // Create application instance
- try {
- if (app == null) {
- @SuppressWarnings("unchecked")
- Class extends Application> clazz = (Class extends Application>) Class.forName(appClass);
- app = clazz.newInstance();
- }
-
- app.setSettings(settings);
- app.start();
- } catch (Exception ex) {
- handleError("Class " + appClass + " init failed", ex);
- setContentView(new TextView(this));
- }
- }
-
- ctx = (OGLESContext) app.getContext();
- view = ctx.createView(this);
- // store the glSurfaceView in JmeAndroidSystem for future use
- JmeAndroidSystem.setView(view);
- // AndroidHarness wraps the app as a SystemListener.
- ctx.setSystemListener(this);
- layoutDisplay();
- }
-
- @Override
- protected void onRestart() {
- logger.fine("onRestart");
- super.onRestart();
- if (app != null) {
- app.restart();
- }
- }
-
- @Override
- protected void onStart() {
- logger.fine("onStart");
- super.onStart();
- }
-
- @Override
- protected void onResume() {
- logger.fine("onResume");
- super.onResume();
-
- gainFocus();
- }
-
- @Override
- protected void onPause() {
- logger.fine("onPause");
- loseFocus();
-
- super.onPause();
- }
-
- @Override
- protected void onStop() {
- logger.fine("onStop");
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- logger.fine("onDestroy");
- final DataObject data = (DataObject) getLastNonConfigurationInstance();
- if (data != null || inConfigChange) {
- logger.fine("In Config Change, not stopping app.");
- } else {
- if (app != null) {
- app.stop(!isGLThreadPaused);
- }
- }
- setContentView(new TextView(this));
- ctx = null;
- app = null;
- view = null;
- JmeAndroidSystem.setView(null);
-
- super.onDestroy();
- }
-
- public Application getJmeApplication() {
- return app;
- }
-
- /**
- * Called when an error has occurred. By default, will show an error message
- * to the user and print the exception/error to the log.
- */
- @Override
- public void handleError(final String errorMsg, final Throwable t) {
- String stackTrace = "";
- String title = "Error";
-
- if (t != null) {
- // Convert exception to string
- StringWriter sw = new StringWriter(100);
- t.printStackTrace(new PrintWriter(sw));
- stackTrace = sw.toString();
- title = t.toString();
- }
-
- final String finalTitle = title;
- final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
- + "\n" + stackTrace;
-
- logger.log(Level.SEVERE, finalMsg);
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
- .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create();
- dialog.show();
- }
- });
- }
-
- /**
- * Called by the android alert dialog, terminate the activity and OpenGL
- * rendering
- *
- * @param dialog
- * @param whichButton
- */
- public void onClick(DialogInterface dialog, int whichButton) {
- if (whichButton != -2) {
- if (app != null) {
- app.stop(true);
- }
- app = null;
- this.finish();
- }
- }
-
- /**
- * Gets called by the InputManager on all touch/drag/scale events
- */
- @Override
- public void onTouch(String name, TouchEvent evt, float tpf) {
- if (name.equals(ESCAPE_EVENT)) {
- switch (evt.getType()) {
- case KEY_UP:
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
- .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create();
- dialog.show();
- }
- });
- break;
- default:
- break;
- }
- }
- }
-
- public void layoutDisplay() {
- logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID);
- if (view == null) {
- logger.log(Level.FINE, "view is null!");
- }
- if (splashPicID != 0) {
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT,
- Gravity.CENTER);
-
- frameLayout = new FrameLayout(this);
- splashImageView = new ImageView(this);
-
- Drawable drawable = this.getResources().getDrawable(splashPicID);
- if (drawable instanceof NinePatchDrawable) {
- splashImageView.setBackgroundDrawable(drawable);
- } else {
- splashImageView.setImageResource(splashPicID);
- }
-
- if (view.getParent() != null) {
- ((ViewGroup) view.getParent()).removeView(view);
- }
- frameLayout.addView(view);
-
- if (splashImageView.getParent() != null) {
- ((ViewGroup) splashImageView.getParent()).removeView(splashImageView);
- }
- frameLayout.addView(splashImageView, lp);
-
- setContentView(frameLayout);
- logger.log(Level.FINE, "Splash Screen Created");
- } else {
- logger.log(Level.FINE, "Splash Screen Skipped.");
- setContentView(view);
- }
- }
-
- public void removeSplashScreen() {
- logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID);
- if (splashPicID != 0) {
- if (frameLayout != null) {
- if (splashImageView != null) {
- this.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- splashImageView.setVisibility(View.INVISIBLE);
- frameLayout.removeView(splashImageView);
- }
- });
- } else {
- logger.log(Level.FINE, "splashImageView is null");
- }
- } else {
- logger.log(Level.FINE, "frameLayout is null");
- }
- }
- }
-
- /**
- * Removes the standard Android log handler due to an issue with not logging
- * entries lower than INFO level and adds a handler that produces
- * JME formatted log messages.
- */
- protected void initializeLogHandler() {
- Logger log = LogManager.getLogManager().getLogger("");
- for (Handler handler : log.getHandlers()) {
- if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) {
- Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName());
- }
- log.removeHandler(handler);
- }
- Handler handler = new AndroidLogHandler();
- log.addHandler(handler);
- handler.setLevel(Level.ALL);
- }
-
- public void initialize() {
- app.initialize();
- if (handleExitHook) {
- // remove existing mapping from SimpleApplication that stops the app
- // when the esc key is pressed (esc key = android back key) so that
- // AndroidHarness can produce the exit app dialog box.
- if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) {
- app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);
- }
-
- app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK));
- app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT});
- }
- }
-
- public void reshape(int width, int height) {
- app.reshape(width, height);
- }
-
- public void update() {
- app.update();
- // call to remove the splash screen, if present.
- // call after app.update() to make sure no gap between
- // splash screen going away and app display being shown.
- if (firstDrawFrame) {
- removeSplashScreen();
- firstDrawFrame = false;
- }
- }
-
- public void requestClose(boolean esc) {
- app.requestClose(esc);
- }
-
- public void destroy() {
- if (app != null) {
- app.destroy();
- }
- if (finishOnAppStop) {
- finish();
- }
- }
-
- public void gainFocus() {
- logger.fine("gainFocus");
- if (view != null) {
- view.onResume();
- }
-
- if (app != null) {
- //resume the audio
- AudioRenderer audioRenderer = app.getAudioRenderer();
- if (audioRenderer != null) {
- audioRenderer.resumeAll();
- }
- //resume the sensors (aka joysticks)
- if (app.getContext() != null) {
- JoyInput joyInput = app.getContext().getJoyInput();
- if (joyInput != null) {
- if (joyInput instanceof AndroidSensorJoyInput) {
- AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput;
- androidJoyInput.resumeSensors();
- }
- }
- }
- }
-
- isGLThreadPaused = false;
-
- if (app != null) {
- app.gainFocus();
- }
- }
-
- public void loseFocus() {
- logger.fine("loseFocus");
- if (app != null) {
- app.loseFocus();
- }
-
- if (view != null) {
- view.onPause();
- }
-
- if (app != null) {
- //pause the audio
- AudioRenderer audioRenderer = app.getAudioRenderer();
- if (audioRenderer != null) {
- audioRenderer.pauseAll();
- }
- //pause the sensors (aka joysticks)
- if (app.getContext() != null) {
- JoyInput joyInput = app.getContext().getJoyInput();
- if (joyInput != null) {
- if (joyInput instanceof AndroidSensorJoyInput) {
- AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput;
- androidJoyInput.pauseSensors();
- }
- }
- }
- }
- isGLThreadPaused = true;
- }
-}
+package com.jme3.app;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.pm.ActivityInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.*;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.input.JoyInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.android.AndroidSensorJoyInput;
+import com.jme3.input.controls.TouchListener;
+import com.jme3.input.controls.TouchTrigger;
+import com.jme3.input.event.TouchEvent;
+import com.jme3.system.AppSettings;
+import com.jme3.system.SystemListener;
+import com.jme3.system.android.JmeAndroidSystem;
+import com.jme3.system.android.OGLESContext;
+import com.jme3.util.AndroidLogHandler;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * AndroidHarness wraps a jme application object and runs it on
+ * Android
+ *
+ * @author Kirill
+ * @author larynx
+ */
+public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener {
+
+ protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
+ /**
+ * The application class to start
+ */
+ protected String appClass = "jme3test.android.Test";
+ /**
+ * The jme3 application object
+ */
+ protected Application app = null;
+
+ /**
+ * Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888.
+ * (default = 24)
+ */
+ protected int eglBitsPerPixel = 24;
+
+ /**
+ * Sets the desired number of Alpha bits for the surfaceview. This affects
+ * how the surfaceview is able to display Android views that are located
+ * under the surfaceview jME uses to render the scenegraph.
+ * 0 = Opaque surfaceview background (fastest)
+ * 1->7 = Transparent surfaceview background
+ * 8 or higher = Translucent surfaceview background
+ * (default = 0)
+ */
+ protected int eglAlphaBits = 0;
+
+ /**
+ * The number of depth bits specifies the precision of the depth buffer.
+ * (default = 16)
+ */
+ protected int eglDepthBits = 16;
+
+ /**
+ * Sets the number of samples to use for multisampling.
+ * Leave 0 (default) to disable multisampling.
+ * Set to 2 or 4 to enable multisampling.
+ */
+ protected int eglSamples = 0;
+
+ /**
+ * Set the number of stencil bits.
+ * (default = 0)
+ */
+ protected int eglStencilBits = 0;
+
+ /**
+ * Set the desired frame rate. If frameRate > 0, the application
+ * will be capped at the desired frame rate.
+ * (default = -1, no frame rate cap)
+ */
+ protected int frameRate = -1;
+
+ /**
+ * Sets the type of Audio Renderer to be used.
+ *
+ * Android MediaPlayer / SoundPool can be used on all
+ * supported Android platform versions (2.2+)
+ * OpenAL Soft uses an OpenSL backend and is only supported on Android
+ * versions 2.3+.
+ *
+ * Only use ANDROID_ static strings found in AppSettings
+ *
+ */
+ protected String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT;
+
+ /**
+ * If true Android Sensors are used as simulated Joysticks. Users can use the
+ * Android sensor feedback through the RawInputListener or by registering
+ * JoyAxisTriggers.
+ */
+ protected boolean joystickEventsEnabled = false;
+ /**
+ * If true KeyEvents are generated from TouchEvents
+ */
+ protected boolean keyEventsEnabled = true;
+ /**
+ * If true MouseEvents are generated from TouchEvents
+ */
+ protected boolean mouseEventsEnabled = true;
+ /**
+ * Flip X axis
+ */
+ protected boolean mouseEventsInvertX = false;
+ /**
+ * Flip Y axis
+ */
+ protected boolean mouseEventsInvertY = false;
+ /**
+ * if true finish this activity when the jme app is stopped
+ */
+ protected boolean finishOnAppStop = true;
+ /**
+ * set to false if you don't want the harness to handle the exit hook
+ */
+ protected boolean handleExitHook = true;
+ /**
+ * Title of the exit dialog, default is "Do you want to exit?"
+ */
+ protected String exitDialogTitle = "Do you want to exit?";
+ /**
+ * Message of the exit dialog, default is "Use your home key to bring this
+ * app into the background or exit to terminate it."
+ */
+ protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
+ /**
+ * Set the screen window mode. If screenFullSize is true, then the
+ * notification bar and title bar are removed and the screen covers the
+ * entire display. If screenFullSize is false, then the notification bar
+ * remains visible if screenShowTitle is true while screenFullScreen is
+ * false, then the title bar is also displayed under the notification bar.
+ */
+ protected boolean screenFullScreen = true;
+ /**
+ * if screenShowTitle is true while screenFullScreen is false, then the
+ * title bar is also displayed under the notification bar
+ */
+ protected boolean screenShowTitle = true;
+ /**
+ * Splash Screen picture Resource ID. If a Splash Screen is desired, set
+ * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
+ * splashPicID = 0, then no splash screen will be displayed.
+ */
+ protected int splashPicID = 0;
+
+
+ protected OGLESContext ctx;
+ protected GLSurfaceView view = null;
+ protected boolean isGLThreadPaused = true;
+ protected ImageView splashImageView = null;
+ protected FrameLayout frameLayout = null;
+ final private String ESCAPE_EVENT = "TouchEscape";
+ private boolean firstDrawFrame = true;
+ private boolean inConfigChange = false;
+
+ private class DataObject {
+ protected Application app = null;
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ logger.log(Level.FINE, "onRetainNonConfigurationInstance");
+ final DataObject data = new DataObject();
+ data.app = this.app;
+ inConfigChange = true;
+ return data;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ initializeLogHandler();
+
+ logger.fine("onCreate");
+ super.onCreate(savedInstanceState);
+
+ if (screenFullScreen) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ } else {
+ if (!screenShowTitle) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+ }
+
+ final DataObject data = (DataObject) getLastNonConfigurationInstance();
+ if (data != null) {
+ logger.log(Level.FINE, "Using Retained App");
+ this.app = data.app;
+ } else {
+ // Discover the screen reolution
+ //TODO try to find a better way to get a hand on the resolution
+ WindowManager wind = this.getWindowManager();
+ Display disp = wind.getDefaultDisplay();
+ Log.d("AndroidHarness", "Resolution from Window, width:" + disp.getWidth() + ", height: " + disp.getHeight());
+
+ // Create Settings
+ logger.log(Level.FINE, "Creating settings");
+ AppSettings settings = new AppSettings(true);
+ settings.setEmulateMouse(mouseEventsEnabled);
+ settings.setEmulateMouseFlipAxis(mouseEventsInvertX, mouseEventsInvertY);
+ settings.setUseJoysticks(joystickEventsEnabled);
+ settings.setEmulateKeyboard(keyEventsEnabled);
+
+ settings.setBitsPerPixel(eglBitsPerPixel);
+ settings.setAlphaBits(eglAlphaBits);
+ settings.setDepthBits(eglDepthBits);
+ settings.setSamples(eglSamples);
+ settings.setStencilBits(eglStencilBits);
+
+ settings.setResolution(disp.getWidth(), disp.getHeight());
+ settings.setAudioRenderer(audioRendererType);
+
+ settings.setFrameRate(frameRate);
+
+ // Create application instance
+ try {
+ if (app == null) {
+ @SuppressWarnings("unchecked")
+ Class extends Application> clazz = (Class extends Application>) Class.forName(appClass);
+ app = clazz.newInstance();
+ }
+
+ app.setSettings(settings);
+ app.start();
+ } catch (Exception ex) {
+ handleError("Class " + appClass + " init failed", ex);
+ setContentView(new TextView(this));
+ }
+ }
+
+ ctx = (OGLESContext) app.getContext();
+ view = ctx.createView(this);
+ // store the glSurfaceView in JmeAndroidSystem for future use
+ JmeAndroidSystem.setView(view);
+ // AndroidHarness wraps the app as a SystemListener.
+ ctx.setSystemListener(this);
+ layoutDisplay();
+ }
+
+ @Override
+ protected void onRestart() {
+ logger.fine("onRestart");
+ super.onRestart();
+ if (app != null) {
+ app.restart();
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ logger.fine("onStart");
+ super.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ logger.fine("onResume");
+ super.onResume();
+
+ gainFocus();
+ }
+
+ @Override
+ protected void onPause() {
+ logger.fine("onPause");
+ loseFocus();
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ logger.fine("onStop");
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ logger.fine("onDestroy");
+ final DataObject data = (DataObject) getLastNonConfigurationInstance();
+ if (data != null || inConfigChange) {
+ logger.fine("In Config Change, not stopping app.");
+ } else {
+ if (app != null) {
+ app.stop(!isGLThreadPaused);
+ }
+ }
+ setContentView(new TextView(this));
+ ctx = null;
+ app = null;
+ view = null;
+ JmeAndroidSystem.setView(null);
+
+ super.onDestroy();
+ }
+
+ public Application getJmeApplication() {
+ return app;
+ }
+
+ /**
+ * Called when an error has occurred. By default, will show an error message
+ * to the user and print the exception/error to the log.
+ */
+ @Override
+ public void handleError(final String errorMsg, final Throwable t) {
+ String stackTrace = "";
+ String title = "Error";
+
+ if (t != null) {
+ // Convert exception to string
+ StringWriter sw = new StringWriter(100);
+ t.printStackTrace(new PrintWriter(sw));
+ stackTrace = sw.toString();
+ title = t.toString();
+ }
+
+ final String finalTitle = title;
+ final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
+ + "\n" + stackTrace;
+
+ logger.log(Level.SEVERE, finalMsg);
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
+ .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create();
+ dialog.show();
+ }
+ });
+ }
+
+ /**
+ * Called by the android alert dialog, terminate the activity and OpenGL
+ * rendering
+ *
+ * @param dialog
+ * @param whichButton
+ */
+ public void onClick(DialogInterface dialog, int whichButton) {
+ if (whichButton != -2) {
+ if (app != null) {
+ app.stop(true);
+ }
+ app = null;
+ this.finish();
+ }
+ }
+
+ /**
+ * Gets called by the InputManager on all touch/drag/scale events
+ */
+ @Override
+ public void onTouch(String name, TouchEvent evt, float tpf) {
+ if (name.equals(ESCAPE_EVENT)) {
+ switch (evt.getType()) {
+ case KEY_UP:
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
+ .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create();
+ dialog.show();
+ }
+ });
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public void layoutDisplay() {
+ logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID);
+ if (view == null) {
+ logger.log(Level.FINE, "view is null!");
+ }
+ if (splashPicID != 0) {
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT,
+ Gravity.CENTER);
+
+ frameLayout = new FrameLayout(this);
+ splashImageView = new ImageView(this);
+
+ Drawable drawable = this.getResources().getDrawable(splashPicID);
+ if (drawable instanceof NinePatchDrawable) {
+ splashImageView.setBackgroundDrawable(drawable);
+ } else {
+ splashImageView.setImageResource(splashPicID);
+ }
+
+ if (view.getParent() != null) {
+ ((ViewGroup) view.getParent()).removeView(view);
+ }
+ frameLayout.addView(view);
+
+ if (splashImageView.getParent() != null) {
+ ((ViewGroup) splashImageView.getParent()).removeView(splashImageView);
+ }
+ frameLayout.addView(splashImageView, lp);
+
+ setContentView(frameLayout);
+ logger.log(Level.FINE, "Splash Screen Created");
+ } else {
+ logger.log(Level.FINE, "Splash Screen Skipped.");
+ setContentView(view);
+ }
+ }
+
+ public void removeSplashScreen() {
+ logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID);
+ if (splashPicID != 0) {
+ if (frameLayout != null) {
+ if (splashImageView != null) {
+ this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ splashImageView.setVisibility(View.INVISIBLE);
+ frameLayout.removeView(splashImageView);
+ }
+ });
+ } else {
+ logger.log(Level.FINE, "splashImageView is null");
+ }
+ } else {
+ logger.log(Level.FINE, "frameLayout is null");
+ }
+ }
+ }
+
+ /**
+ * Removes the standard Android log handler due to an issue with not logging
+ * entries lower than INFO level and adds a handler that produces
+ * JME formatted log messages.
+ */
+ protected void initializeLogHandler() {
+ Logger log = LogManager.getLogManager().getLogger("");
+ for (Handler handler : log.getHandlers()) {
+ if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) {
+ Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName());
+ }
+ log.removeHandler(handler);
+ }
+ Handler handler = new AndroidLogHandler();
+ log.addHandler(handler);
+ handler.setLevel(Level.ALL);
+ }
+
+ public void initialize() {
+ app.initialize();
+ if (handleExitHook) {
+ // remove existing mapping from SimpleApplication that stops the app
+ // when the esc key is pressed (esc key = android back key) so that
+ // AndroidHarness can produce the exit app dialog box.
+ if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) {
+ app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);
+ }
+
+ app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK));
+ app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT});
+ }
+ }
+
+ public void reshape(int width, int height) {
+ app.reshape(width, height);
+ }
+
+ public void update() {
+ app.update();
+ // call to remove the splash screen, if present.
+ // call after app.update() to make sure no gap between
+ // splash screen going away and app display being shown.
+ if (firstDrawFrame) {
+ removeSplashScreen();
+ firstDrawFrame = false;
+ }
+ }
+
+ public void requestClose(boolean esc) {
+ app.requestClose(esc);
+ }
+
+ public void destroy() {
+ if (app != null) {
+ app.destroy();
+ }
+ if (finishOnAppStop) {
+ finish();
+ }
+ }
+
+ public void gainFocus() {
+ logger.fine("gainFocus");
+ if (view != null) {
+ view.onResume();
+ }
+
+ if (app != null) {
+ //resume the audio
+ AudioRenderer audioRenderer = app.getAudioRenderer();
+ if (audioRenderer != null) {
+ audioRenderer.resumeAll();
+ }
+ //resume the sensors (aka joysticks)
+ if (app.getContext() != null) {
+ JoyInput joyInput = app.getContext().getJoyInput();
+ if (joyInput != null) {
+ if (joyInput instanceof AndroidSensorJoyInput) {
+ AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput;
+ androidJoyInput.resumeSensors();
+ }
+ }
+ }
+ }
+
+ isGLThreadPaused = false;
+
+ if (app != null) {
+ app.gainFocus();
+ }
+ }
+
+ public void loseFocus() {
+ logger.fine("loseFocus");
+ if (app != null) {
+ app.loseFocus();
+ }
+
+ if (view != null) {
+ view.onPause();
+ }
+
+ if (app != null) {
+ //pause the audio
+ AudioRenderer audioRenderer = app.getAudioRenderer();
+ if (audioRenderer != null) {
+ audioRenderer.pauseAll();
+ }
+ //pause the sensors (aka joysticks)
+ if (app.getContext() != null) {
+ JoyInput joyInput = app.getContext().getJoyInput();
+ if (joyInput != null) {
+ if (joyInput instanceof AndroidSensorJoyInput) {
+ AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput;
+ androidJoyInput.pauseSensors();
+ }
+ }
+ }
+ }
+ isGLThreadPaused = true;
+ }
+}
diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java
index 1b205d6dd..5673c8609 100644
--- a/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java
+++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java
@@ -195,19 +195,6 @@ public class AndroidHarnessFragment extends Fragment implements
*/
protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
- /**
- * Set the screen window mode. If screenFullSize is true, then the
- * notification bar and title bar are removed and the screen covers the
- * entire display. If screenFullSize is false, then the notification bar
- * remains visible if screenShowTitle is true while screenFullScreen is
- * false, then the title bar is also displayed under the notification bar.
- */
- protected boolean screenFullScreen = true;
- /**
- * if screenShowTitle is true while screenFullScreen is false, then the
- * title bar is also displayed under the notification bar
- */
- protected boolean screenShowTitle = true;
/**
* Splash Screen picture Resource ID. If a Splash Screen is desired, set
* splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
diff --git a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
index c915164b3..a388ad612 100644
--- a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
+++ b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
@@ -74,7 +74,7 @@ public class VideoRecorderAppState extends AbstractAppState {
public Thread newThread(Runnable r) {
Thread th = new Thread(r);
- th.setName("jME Video Processing Thread");
+ th.setName("jME3 Video Processor");
th.setDaemon(true);
return th;
}
diff --git a/jme3-android/src/main/java/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java b/jme3-android/src/main/java/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java
index 7ed04658e..394cc257b 100644
--- a/jme3-android/src/main/java/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java
+++ b/jme3-android/src/main/java/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java
@@ -525,4 +525,9 @@ public class AndroidMediaPlayerAudioRenderer implements AudioRenderer,
@Override
public void deleteFilter(Filter filter) {
}
+
+ @Override
+ public float getSourcePlaybackTime(AudioSource src) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidGestureHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidGestureProcessor.java
similarity index 51%
rename from jme3-android/src/main/java/com/jme3/input/android/AndroidGestureHandler.java
rename to jme3-android/src/main/java/com/jme3/input/android/AndroidGestureProcessor.java
index d233836a5..2c0367946 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidGestureHandler.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidGestureProcessor.java
@@ -35,314 +35,240 @@ package com.jme3.input.android;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
-import android.view.View;
-import com.jme3.input.event.InputEvent;
-import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* AndroidGestureHandler uses Gesture type listeners to create jME TouchEvents
- * for gestures. This class is designed to handle the gestures supported
+ * for gestures. This class is designed to handle the gestures supported
* on Android rev 9 (Android 2.3). Extend this class to add functionality
* added by Android after rev 9.
- *
+ *
* @author iwgeric
*/
-public class AndroidGestureHandler implements
- GestureDetector.OnGestureListener,
+public class AndroidGestureProcessor implements
+ GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener,
ScaleGestureDetector.OnScaleGestureListener {
- private static final Logger logger = Logger.getLogger(AndroidGestureHandler.class.getName());
- private AndroidInputHandler androidInput;
- private GestureDetector gestureDetector;
- private ScaleGestureDetector scaleDetector;
+ private static final Logger logger = Logger.getLogger(AndroidGestureProcessor.class.getName());
+
+ private AndroidTouchInput touchInput;
float gestureDownX = -1f;
float gestureDownY = -1f;
float scaleStartX = -1f;
float scaleStartY = -1f;
- public AndroidGestureHandler(AndroidInputHandler androidInput) {
- this.androidInput = androidInput;
- }
-
- public void initialize() {
- }
-
- public void destroy() {
- setView(null);
- }
-
- public void setView(View view) {
- if (view != null) {
- gestureDetector = new GestureDetector(view.getContext(), this);
- scaleDetector = new ScaleGestureDetector(view.getContext(), this);
- } else {
- gestureDetector = null;
- scaleDetector = null;
- }
- }
-
- public void detectGesture(MotionEvent event) {
- if (gestureDetector != null && scaleDetector != null) {
- gestureDetector.onTouchEvent(event);
- scaleDetector.onTouchEvent(event);
- }
- }
-
- private int getPointerIndex(MotionEvent event) {
- return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
-
- private int getPointerId(MotionEvent event) {
- return event.getPointerId(getPointerIndex(event));
+ public AndroidGestureProcessor(AndroidTouchInput touchInput) {
+ this.touchInput = touchInput;
}
-
- private void processEvent(TouchEvent event) {
- // Add the touch event
- androidInput.addEvent(event);
- if (androidInput.isSimulateMouse()) {
- InputEvent mouseEvent = generateMouseEvent(event);
- if (mouseEvent != null) {
- // Add the mouse event
- androidInput.addEvent(mouseEvent);
- }
- }
- }
-
- // TODO: Ring Buffer for mouse events?
- private InputEvent generateMouseEvent(TouchEvent event) {
- InputEvent inputEvent = null;
- int newX;
- int newY;
- int newDX;
- int newDY;
- if (androidInput.isMouseEventsInvertX()) {
- newX = (int) (androidInput.invertX(event.getX()));
- newDX = (int)event.getDeltaX() * -1;
- } else {
- newX = (int) event.getX();
- newDX = (int)event.getDeltaX();
- }
- int wheel = (int) (event.getScaleSpan()); // might need to scale to match mouse wheel
- int dWheel = (int) (event.getDeltaScaleSpan()); // might need to scale to match mouse wheel
-
- if (androidInput.isMouseEventsInvertY()) {
- newY = (int) (androidInput.invertY(event.getY()));
- newDY = (int)event.getDeltaY() * -1;
- } else {
- newY = (int) event.getY();
- newDY = (int)event.getDeltaY();
- }
-
- switch (event.getType()) {
- case SCALE_MOVE:
- inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, wheel, dWheel);
- inputEvent.setTime(event.getTime());
- break;
- }
-
- return inputEvent;
- }
-
/* Events from onGestureListener */
-
+
+ @Override
public boolean onDown(MotionEvent event) {
// start of all GestureListeners. Not really a gesture by itself
// so we don't create an event.
// However, reset the scaleInProgress here since this is the beginning
// of a series of gesture events.
-// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
- gestureDownX = androidInput.getJmeX(event.getX());
- gestureDownY = androidInput.invertY(androidInput.getJmeY(event.getY()));
+// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+ gestureDownX = touchInput.getJmeX(event.getX());
+ gestureDownY = touchInput.invertY(touchInput.getJmeY(event.getY()));
return true;
}
+ @Override
public boolean onSingleTapUp(MotionEvent event) {
// Up of single tap. May be followed by a double tap later.
// use onSingleTapConfirmed instead.
-// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
+// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
return true;
}
+ @Override
public void onShowPress(MotionEvent event) {
-// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
- float jmeX = androidInput.getJmeX(event.getX());
- float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+ float jmeX = touchInput.getJmeX(event.getX());
+ float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
- touchEvent.setPointerId(getPointerId(event));
+ touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
- processEvent(touchEvent);
+ touchInput.addEvent(touchEvent);
}
+ @Override
public void onLongPress(MotionEvent event) {
-// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
- float jmeX = androidInput.getJmeX(event.getX());
- float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+ float jmeX = touchInput.getJmeX(event.getX());
+ float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
- touchEvent.setPointerId(getPointerId(event));
+ touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
- processEvent(touchEvent);
+ touchInput.addEvent(touchEvent);
}
+ @Override
public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) {
// if not scaleInProgess, send scroll events. This is to avoid sending
// scroll events when one of the fingers is lifted just before the other one.
// Avoids sending the scroll for that brief period of time.
// Return true so that the next event doesn't accumulate the distX and distY values.
- // Apparantly, both distX and distY are negative.
+ // Apparantly, both distX and distY are negative.
// Negate distX to get the real value, but leave distY negative to compensate
// for the fact that jME has y=0 at bottom where Android has y=0 at top.
-// if (!scaleInProgress) {
- if (!scaleDetector.isInProgress()) {
-// logger.log(Level.INFO, "onScroll pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, dx: {7}, dy: {8}",
-// new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), distX, distY});
+ if (!touchInput.getScaleDetector().isInProgress()) {
+// logger.log(Level.INFO, "onScroll pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, dx: {7}, dy: {8}",
+// new Object[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), distX, distY});
- float jmeX = androidInput.getJmeX(endEvent.getX());
- float jmeY = androidInput.invertY(androidInput.getJmeY(endEvent.getY()));
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
- touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, androidInput.getJmeX(-distX), androidInput.getJmeY(distY));
- touchEvent.setPointerId(getPointerId(endEvent));
+ float jmeX = touchInput.getJmeX(endEvent.getX());
+ float jmeY = touchInput.invertY(touchInput.getJmeY(endEvent.getY()));
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
+ touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, touchInput.getJmeX(-distX), touchInput.getJmeY(distY));
+ touchEvent.setPointerId(touchInput.getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure());
- processEvent(touchEvent);
+ touchInput.addEvent(touchEvent);
}
return true;
}
+ @Override
public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float velocityX, float velocityY) {
// Fling happens only once at the end of the gesture (all fingers up).
// Fling returns the velocity of the finger movement in pixels/sec.
// Therefore, the dX and dY values are actually velocity instead of distance values
// Since this does not track the movement, use the start position and velocity values.
-
-// logger.log(Level.INFO, "onFling pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, velocityX: {7}, velocityY: {8}",
-// new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
- float jmeX = androidInput.getJmeX(startEvent.getX());
- float jmeY = androidInput.invertY(androidInput.getJmeY(startEvent.getY()));
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+// logger.log(Level.INFO, "onFling pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, velocityX: {7}, velocityY: {8}",
+// new Object[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
+
+ float jmeX = touchInput.getJmeX(startEvent.getX());
+ float jmeY = touchInput.invertY(touchInput.getJmeY(startEvent.getY()));
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
- touchEvent.setPointerId(getPointerId(endEvent));
+ touchEvent.setPointerId(touchInput.getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure());
- processEvent(touchEvent);
+ touchInput.addEvent(touchEvent);
return true;
}
/* Events from onDoubleTapListener */
-
+
+ @Override
public boolean onSingleTapConfirmed(MotionEvent event) {
// Up of single tap when no double tap followed.
-// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
- float jmeX = androidInput.getJmeX(event.getX());
- float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+ float jmeX = touchInput.getJmeX(event.getX());
+ float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
- touchEvent.setPointerId(getPointerId(event));
+ touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
- processEvent(touchEvent);
+ touchInput.addEvent(touchEvent);
return true;
}
+ @Override
public boolean onDoubleTap(MotionEvent event) {
//The down motion event of the first tap of the double-tap
- // We could use this event to fire off a double tap event, or use
+ // We could use this event to fire off a double tap event, or use
// DoubleTapEvent with a check for the UP action
-// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
- float jmeX = androidInput.getJmeX(event.getX());
- float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+ float jmeX = touchInput.getJmeX(event.getX());
+ float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
- touchEvent.setPointerId(getPointerId(event));
+ touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
- processEvent(touchEvent);
+ touchInput.addEvent(touchEvent);
return true;
}
+ @Override
public boolean onDoubleTapEvent(MotionEvent event) {
//Notified when an event within a double-tap gesture occurs, including the down, move(s), and up events.
// this means it will get called multiple times for a single double tap
-// logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
-// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-// if (getAction(event) == MotionEvent.ACTION_UP) {
-// TouchEvent touchEvent = touchEventPool.getNextFreeEvent();
-// touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), androidInput.invertY(event.getY()), 0, 0);
-// touchEvent.setPointerId(getPointerId(event));
-// touchEvent.setTime(event.getEventTime());
-// touchEvent.setPressure(event.getPressure());
-// processEvent(touchEvent);
-// }
+// logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
+// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+ if (touchInput.getAction(event) == MotionEvent.ACTION_UP) {
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
+ touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), touchInput.invertY(event.getY()), 0, 0);
+ touchEvent.setPointerId(touchInput.getPointerId(event));
+ touchEvent.setTime(event.getEventTime());
+ touchEvent.setPressure(event.getPressure());
+ touchInput.addEvent(touchEvent);
+ }
return true;
}
/* Events from ScaleGestureDetector */
-
+
+ @Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
// Scale uses a focusX and focusY instead of x and y. Focus is the middle
// of the fingers. Therefore, use the x and y values from the Down event
// so that the x and y values don't jump to the middle position.
// return true or all gestures for this beginning event will be discarded
- logger.log(Level.INFO, "onScaleBegin");
+// logger.log(Level.INFO, "onScaleBegin");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(0f);
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
- touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
- processEvent(touchEvent);
-
+ touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
+ touchInput.addEvent(touchEvent);
+
return true;
}
+ @Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
// return true or all gestures for this event will be accumulated
- logger.log(Level.INFO, "onScale");
+// logger.log(Level.INFO, "onScale");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
- touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
- processEvent(touchEvent);
+ touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
+ touchInput.addEvent(touchEvent);
return true;
}
+ @Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
- logger.log(Level.INFO, "onScaleEnd");
+// logger.log(Level.INFO, "onScaleEnd");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
- TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+ TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
- touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
- processEvent(touchEvent);
+ touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
+ touchInput.addEvent(touchEvent);
}
}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInput.java
deleted file mode 100644
index 02fef6b0f..000000000
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInput.java
+++ /dev/null
@@ -1,686 +0,0 @@
-package com.jme3.input.android;
-
-import android.view.*;
-import com.jme3.input.KeyInput;
-import com.jme3.input.RawInputListener;
-import com.jme3.input.TouchInput;
-import com.jme3.input.event.MouseButtonEvent;
-import com.jme3.input.event.MouseMotionEvent;
-import com.jme3.input.event.TouchEvent;
-import com.jme3.input.event.TouchEvent.Type;
-import com.jme3.math.Vector2f;
-import com.jme3.system.AppSettings;
-import com.jme3.util.RingBuffer;
-import java.util.HashMap;
-import java.util.logging.Logger;
-
-/**
- * AndroidInput is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
- * @author larynx
- *
- */
-public class AndroidInput implements
- TouchInput,
- View.OnTouchListener,
- View.OnKeyListener,
- GestureDetector.OnGestureListener,
- GestureDetector.OnDoubleTapListener,
- ScaleGestureDetector.OnScaleGestureListener {
-
- final private static int MAX_EVENTS = 1024;
- // Custom settings
- public boolean mouseEventsEnabled = true;
- public boolean mouseEventsInvertX = false;
- public boolean mouseEventsInvertY = false;
- public boolean keyboardEventsEnabled = false;
- public boolean dontSendHistory = false;
- // Used to transfer events from android thread to GLThread
- final private RingBuffer eventQueue = new RingBuffer(MAX_EVENTS);
- final private RingBuffer eventPoolUnConsumed = new RingBuffer(MAX_EVENTS);
- final private RingBuffer eventPool = new RingBuffer(MAX_EVENTS);
- final private HashMap lastPositions = new HashMap();
- // Internal
- private View view;
- private ScaleGestureDetector scaledetector;
- private boolean scaleInProgress = false;
- private GestureDetector detector;
- private int lastX;
- private int lastY;
- private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
- private boolean isInitialized = false;
- private RawInputListener listener = null;
- private static final int[] ANDROID_TO_JME = {
- 0x0, // unknown
- 0x0, // key code soft left
- 0x0, // key code soft right
- KeyInput.KEY_HOME,
- KeyInput.KEY_ESCAPE, // key back
- 0x0, // key call
- 0x0, // key endcall
- KeyInput.KEY_0,
- KeyInput.KEY_1,
- KeyInput.KEY_2,
- KeyInput.KEY_3,
- KeyInput.KEY_4,
- KeyInput.KEY_5,
- KeyInput.KEY_6,
- KeyInput.KEY_7,
- KeyInput.KEY_8,
- KeyInput.KEY_9,
- KeyInput.KEY_MULTIPLY,
- 0x0, // key pound
- KeyInput.KEY_UP,
- KeyInput.KEY_DOWN,
- KeyInput.KEY_LEFT,
- KeyInput.KEY_RIGHT,
- KeyInput.KEY_RETURN, // dpad center
- 0x0, // volume up
- 0x0, // volume down
- KeyInput.KEY_POWER, // power (?)
- 0x0, // camera
- 0x0, // clear
- KeyInput.KEY_A,
- KeyInput.KEY_B,
- KeyInput.KEY_C,
- KeyInput.KEY_D,
- KeyInput.KEY_E,
- KeyInput.KEY_F,
- KeyInput.KEY_G,
- KeyInput.KEY_H,
- KeyInput.KEY_I,
- KeyInput.KEY_J,
- KeyInput.KEY_K,
- KeyInput.KEY_L,
- KeyInput.KEY_M,
- KeyInput.KEY_N,
- KeyInput.KEY_O,
- KeyInput.KEY_P,
- KeyInput.KEY_Q,
- KeyInput.KEY_R,
- KeyInput.KEY_S,
- KeyInput.KEY_T,
- KeyInput.KEY_U,
- KeyInput.KEY_V,
- KeyInput.KEY_W,
- KeyInput.KEY_X,
- KeyInput.KEY_Y,
- KeyInput.KEY_Z,
- KeyInput.KEY_COMMA,
- KeyInput.KEY_PERIOD,
- KeyInput.KEY_LMENU,
- KeyInput.KEY_RMENU,
- KeyInput.KEY_LSHIFT,
- KeyInput.KEY_RSHIFT,
- // 0x0, // fn
- // 0x0, // cap (?)
-
- KeyInput.KEY_TAB,
- KeyInput.KEY_SPACE,
- 0x0, // sym (?) symbol
- 0x0, // explorer
- 0x0, // envelope
- KeyInput.KEY_RETURN, // newline/enter
- KeyInput.KEY_DELETE,
- KeyInput.KEY_GRAVE,
- KeyInput.KEY_MINUS,
- KeyInput.KEY_EQUALS,
- KeyInput.KEY_LBRACKET,
- KeyInput.KEY_RBRACKET,
- KeyInput.KEY_BACKSLASH,
- KeyInput.KEY_SEMICOLON,
- KeyInput.KEY_APOSTROPHE,
- KeyInput.KEY_SLASH,
- KeyInput.KEY_AT, // at (@)
- KeyInput.KEY_NUMLOCK, //0x0, // num
- 0x0, //headset hook
- 0x0, //focus
- KeyInput.KEY_ADD,
- KeyInput.KEY_LMETA, //menu
- 0x0,//notification
- 0x0,//search
- 0x0,//media play/pause
- 0x0,//media stop
- 0x0,//media next
- 0x0,//media previous
- 0x0,//media rewind
- 0x0,//media fastforward
- 0x0,//mute
- };
-
- public AndroidInput() {
- }
-
- public void setView(View view) {
- this.view = view;
- if (view != null) {
- detector = new GestureDetector(null, this, null, false);
- scaledetector = new ScaleGestureDetector(view.getContext(), this);
- view.setOnTouchListener(this);
- view.setOnKeyListener(this);
- }
- }
-
- private TouchEvent getNextFreeTouchEvent() {
- return getNextFreeTouchEvent(false);
- }
-
- /**
- * Fetches a touch event from the reuse pool
- * @param wait if true waits for a reusable event to get available/released
- * by an other thread, if false returns a new one if needed.
- *
- * @return a usable TouchEvent
- */
- private TouchEvent getNextFreeTouchEvent(boolean wait) {
- TouchEvent evt = null;
- synchronized (eventPoolUnConsumed) {
- int size = eventPoolUnConsumed.size();
- while (size > 0) {
- evt = eventPoolUnConsumed.pop();
- if (!evt.isConsumed()) {
- eventPoolUnConsumed.push(evt);
- evt = null;
- } else {
- break;
- }
- size--;
- }
- }
-
- if (evt == null) {
- if (eventPool.isEmpty() && wait) {
- logger.warning("eventPool buffer underrun");
- boolean isEmpty;
- do {
- synchronized (eventPool) {
- isEmpty = eventPool.isEmpty();
- }
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- } while (isEmpty);
- synchronized (eventPool) {
- evt = eventPool.pop();
- }
- } else if (eventPool.isEmpty()) {
- evt = new TouchEvent();
- logger.warning("eventPool buffer underrun");
- } else {
- synchronized (eventPool) {
- evt = eventPool.pop();
- }
- }
- }
- return evt;
- }
-
- /**
- * onTouch gets called from android thread on touchpad events
- */
- public boolean onTouch(View view, MotionEvent event) {
- if (view != this.view) {
- return false;
- }
- boolean bWasHandled = false;
- TouchEvent touch;
- // System.out.println("native : " + event.getAction());
- int action = event.getAction() & MotionEvent.ACTION_MASK;
- int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- int pointerId = event.getPointerId(pointerIndex);
- Vector2f lastPos = lastPositions.get(pointerId);
-
- // final int historySize = event.getHistorySize();
- //final int pointerCount = event.getPointerCount();
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_DOWN:
- touch = getNextFreeTouchEvent();
- touch.set(Type.DOWN, event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex), 0, 0);
- touch.setPointerId(pointerId);
- touch.setTime(event.getEventTime());
- touch.setPressure(event.getPressure(pointerIndex));
- processEvent(touch);
-
- lastPos = new Vector2f(event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex));
- lastPositions.put(pointerId, lastPos);
-
- bWasHandled = true;
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- touch = getNextFreeTouchEvent();
- touch.set(Type.UP, event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex), 0, 0);
- touch.setPointerId(pointerId);
- touch.setTime(event.getEventTime());
- touch.setPressure(event.getPressure(pointerIndex));
- processEvent(touch);
- lastPositions.remove(pointerId);
-
- bWasHandled = true;
- break;
- case MotionEvent.ACTION_MOVE:
- // Convert all pointers into events
- for (int p = 0; p < event.getPointerCount(); p++) {
- lastPos = lastPositions.get(event.getPointerId(p));
- if (lastPos == null) {
- lastPos = new Vector2f(event.getX(p), view.getHeight() - event.getY(p));
- lastPositions.put(event.getPointerId(p), lastPos);
- }
-
- float dX = event.getX(p) - lastPos.x;
- float dY = view.getHeight() - event.getY(p) - lastPos.y;
- if (dX != 0 || dY != 0) {
- touch = getNextFreeTouchEvent();
- touch.set(Type.MOVE, event.getX(p), view.getHeight() - event.getY(p), dX, dY);
- touch.setPointerId(event.getPointerId(p));
- touch.setTime(event.getEventTime());
- touch.setPressure(event.getPressure(p));
- touch.setScaleSpanInProgress(scaleInProgress);
- processEvent(touch);
- lastPos.set(event.getX(p), view.getHeight() - event.getY(p));
- }
- }
- bWasHandled = true;
- break;
- case MotionEvent.ACTION_OUTSIDE:
- break;
-
- }
-
- // Try to detect gestures
- this.detector.onTouchEvent(event);
- this.scaledetector.onTouchEvent(event);
-
- return bWasHandled;
- }
-
- /**
- * onKey gets called from android thread on key events
- */
- public boolean onKey(View view, int keyCode, KeyEvent event) {
- if (view != this.view) {
- return false;
- }
-
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- TouchEvent evt;
- evt = getNextFreeTouchEvent();
- evt.set(TouchEvent.Type.KEY_DOWN);
- evt.setKeyCode(keyCode);
- evt.setCharacters(event.getCharacters());
- evt.setTime(event.getEventTime());
-
- // Send the event
- processEvent(evt);
-
- // Handle all keys ourself except Volume Up/Down
- if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
- return false;
- } else {
- return true;
- }
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- TouchEvent evt;
- evt = getNextFreeTouchEvent();
- evt.set(TouchEvent.Type.KEY_UP);
- evt.setKeyCode(keyCode);
- evt.setCharacters(event.getCharacters());
- evt.setTime(event.getEventTime());
-
- // Send the event
- processEvent(evt);
-
- // Handle all keys ourself except Volume Up/Down
- if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
- return false;
- } else {
- return true;
- }
- } else {
- return false;
- }
- }
-
- public void loadSettings(AppSettings settings) {
- mouseEventsEnabled = settings.isEmulateMouse();
- mouseEventsInvertX = settings.isEmulateMouseFlipX();
- mouseEventsInvertY = settings.isEmulateMouseFlipY();
- }
-
- // -----------------------------------------
- // JME3 Input interface
- @Override
- public void initialize() {
- TouchEvent item;
- for (int i = 0; i < MAX_EVENTS; i++) {
- item = new TouchEvent();
- eventPool.push(item);
- }
- isInitialized = true;
- }
-
- @Override
- public void destroy() {
- isInitialized = false;
-
- // Clean up queues
- while (!eventPool.isEmpty()) {
- eventPool.pop();
- }
- while (!eventQueue.isEmpty()) {
- eventQueue.pop();
- }
-
-
- this.view = null;
- }
-
- @Override
- public boolean isInitialized() {
- return isInitialized;
- }
-
- @Override
- public void setInputListener(RawInputListener listener) {
- this.listener = listener;
- }
-
- @Override
- public long getInputTimeNanos() {
- return System.nanoTime();
- }
- // -----------------------------------------
-
- private void processEvent(TouchEvent event) {
- synchronized (eventQueue) {
- //Discarding events when the ring buffer is full to avoid buffer overflow.
- if(eventQueue.size()< MAX_EVENTS){
- eventQueue.push(event);
- }
-
- }
- }
-
- // --------------- INSIDE GLThread ---------------
- @Override
- public void update() {
- generateEvents();
- }
-
- private void generateEvents() {
- if (listener != null) {
- TouchEvent event;
- MouseButtonEvent btn;
- MouseMotionEvent mot;
- int newX;
- int newY;
-
- while (!eventQueue.isEmpty()) {
- synchronized (eventQueue) {
- event = eventQueue.pop();
- }
- if (event != null) {
- listener.onTouchEvent(event);
-
- if (mouseEventsEnabled) {
- if (mouseEventsInvertX) {
- newX = view.getWidth() - (int) event.getX();
- } else {
- newX = (int) event.getX();
- }
-
- if (mouseEventsInvertY) {
- newY = view.getHeight() - (int) event.getY();
- } else {
- newY = (int) event.getY();
- }
-
- switch (event.getType()) {
- case DOWN:
- // Handle mouse down event
- btn = new MouseButtonEvent(0, true, newX, newY);
- btn.setTime(event.getTime());
- listener.onMouseButtonEvent(btn);
- // Store current pos
- lastX = -1;
- lastY = -1;
- break;
-
- case UP:
- // Handle mouse up event
- btn = new MouseButtonEvent(0, false, newX, newY);
- btn.setTime(event.getTime());
- listener.onMouseButtonEvent(btn);
- // Store current pos
- lastX = -1;
- lastY = -1;
- break;
-
- case SCALE_MOVE:
- if (lastX != -1 && lastY != -1) {
- newX = lastX;
- newY = lastY;
- }
- int wheel = (int) (event.getScaleSpan() / 4f); // scale to match mouse wheel
- int dwheel = (int) (event.getDeltaScaleSpan() / 4f); // scale to match mouse wheel
- mot = new MouseMotionEvent(newX, newX, 0, 0, wheel, dwheel);
- mot.setTime(event.getTime());
- listener.onMouseMotionEvent(mot);
- lastX = newX;
- lastY = newY;
-
- break;
-
- case MOVE:
- if (event.isScaleSpanInProgress()) {
- break;
- }
-
- int dx;
- int dy;
- if (lastX != -1) {
- dx = newX - lastX;
- dy = newY - lastY;
- } else {
- dx = 0;
- dy = 0;
- }
-
- mot = new MouseMotionEvent(newX, newY, dx, dy, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan());
- mot.setTime(event.getTime());
- listener.onMouseMotionEvent(mot);
- lastX = newX;
- lastY = newY;
-
- break;
- }
- }
- }
-
- if (event.isConsumed() == false) {
- synchronized (eventPoolUnConsumed) {
- eventPoolUnConsumed.push(event);
- }
-
- } else {
- synchronized (eventPool) {
- eventPool.push(event);
- }
- }
- }
-
- }
- }
- // --------------- ENDOF INSIDE GLThread ---------------
-
- // --------------- Gesture detected callback events ---------------
- public boolean onDown(MotionEvent event) {
- return false;
- }
-
- public void onLongPress(MotionEvent event) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.LONGPRESSED, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(event.getEventTime());
- processEvent(touch);
- }
-
- public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.FLING, event.getX(), view.getHeight() - event.getY(), vx, vy);
- touch.setPointerId(0);
- touch.setTime(event.getEventTime());
- processEvent(touch);
-
- return true;
- }
-
- public boolean onSingleTapConfirmed(MotionEvent event) {
- //Nothing to do here the tap has already been detected.
- return false;
- }
-
- public boolean onDoubleTap(MotionEvent event) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.DOUBLETAP, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(event.getEventTime());
- processEvent(touch);
- return true;
- }
-
- public boolean onDoubleTapEvent(MotionEvent event) {
- return false;
- }
-
- public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
- scaleInProgress = true;
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(scaleGestureDetector.getEventTime());
- touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
- touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
- touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
- touch.setScaleSpanInProgress(scaleInProgress);
- processEvent(touch);
- // System.out.println("scaleBegin");
-
- return true;
- }
-
- public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), view.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(scaleGestureDetector.getEventTime());
- touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
- touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
- touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
- touch.setScaleSpanInProgress(scaleInProgress);
- processEvent(touch);
- // System.out.println("scale");
-
- return false;
- }
-
- public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
- scaleInProgress = false;
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), view.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(scaleGestureDetector.getEventTime());
- touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
- touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
- touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
- touch.setScaleSpanInProgress(scaleInProgress);
- processEvent(touch);
- }
-
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.SCROLL, e1.getX(), view.getHeight() - e1.getY(), distanceX, distanceY * (-1));
- touch.setPointerId(0);
- touch.setTime(e1.getEventTime());
- processEvent(touch);
- //System.out.println("scroll " + e1.getPointerCount());
- return false;
- }
-
- public void onShowPress(MotionEvent event) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.SHOWPRESS, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(event.getEventTime());
- processEvent(touch);
- }
-
- public boolean onSingleTapUp(MotionEvent event) {
- TouchEvent touch = getNextFreeTouchEvent();
- touch.set(Type.TAP, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
- touch.setPointerId(0);
- touch.setTime(event.getEventTime());
- processEvent(touch);
- return true;
- }
-
- @Override
- public void setSimulateKeyboard(boolean simulate) {
- keyboardEventsEnabled = simulate;
- }
-
- @Override
- public void setOmitHistoricEvents(boolean dontSendHistory) {
- this.dontSendHistory = dontSendHistory;
- }
-
- /**
- * @deprecated Use {@link #getSimulateMouse()};
- */
- @Deprecated
- public boolean isMouseEventsEnabled() {
- return mouseEventsEnabled;
- }
-
- @Deprecated
- public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
- this.mouseEventsEnabled = mouseEventsEnabled;
- }
-
- public boolean isMouseEventsInvertY() {
- return mouseEventsInvertY;
- }
-
- public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
- this.mouseEventsInvertY = mouseEventsInvertY;
- }
-
- public boolean isMouseEventsInvertX() {
- return mouseEventsInvertX;
- }
-
- public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
- this.mouseEventsInvertX = mouseEventsInvertX;
- }
-
- public void setSimulateMouse(boolean simulate) {
- mouseEventsEnabled = simulate;
- }
-
- public boolean getSimulateMouse() {
- return isSimulateMouse();
- }
-
- public boolean isSimulateMouse() {
- return mouseEventsEnabled;
- }
-
- public boolean isSimulateKeyboard() {
- return keyboardEventsEnabled;
- }
-
-}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
index 592baecaf..9f4729c66 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
@@ -1,265 +1,238 @@
-/*
- * 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.input.android;
-
-import android.opengl.GLSurfaceView;
-import android.os.Build;
-import android.view.View;
-import com.jme3.input.RawInputListener;
-import com.jme3.input.TouchInput;
-import com.jme3.input.event.InputEvent;
-import com.jme3.input.event.KeyInputEvent;
-import com.jme3.input.event.MouseButtonEvent;
-import com.jme3.input.event.MouseMotionEvent;
-import com.jme3.input.event.TouchEvent;
-import com.jme3.system.AppSettings;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * AndroidInput is the main class that connects the Android system
- * inputs to jME. It serves as the manager that gathers inputs from the various
- * Android input methods and provides them to jME's InputManager.
- *
- * @author iwgeric
- */
-public class AndroidInputHandler implements TouchInput {
- private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
-
- // Custom settings
- private boolean mouseEventsEnabled = true;
- private boolean mouseEventsInvertX = false;
- private boolean mouseEventsInvertY = false;
- private boolean keyboardEventsEnabled = false;
- private boolean joystickEventsEnabled = false;
- private boolean dontSendHistory = false;
-
-
- // Internal
- private GLSurfaceView view;
- private AndroidTouchHandler touchHandler;
- private AndroidKeyHandler keyHandler;
- private AndroidGestureHandler gestureHandler;
- private boolean initialized = false;
- private RawInputListener listener = null;
- private ConcurrentLinkedQueue inputEventQueue = new ConcurrentLinkedQueue();
- private final static int MAX_TOUCH_EVENTS = 1024;
- private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS);
- private float scaleX = 1f;
- private float scaleY = 1f;
-
-
- public AndroidInputHandler() {
- int buildVersion = Build.VERSION.SDK_INT;
- logger.log(Level.INFO, "Android Build Version: {0}", buildVersion);
- if (buildVersion >= 14) {
- // add support for onHover and GenericMotionEvent (ie. gamepads)
- gestureHandler = new AndroidGestureHandler(this);
- touchHandler = new AndroidTouchHandler14(this, gestureHandler);
- keyHandler = new AndroidKeyHandler(this);
- } else if (buildVersion >= 8){
- gestureHandler = new AndroidGestureHandler(this);
- touchHandler = new AndroidTouchHandler(this, gestureHandler);
- keyHandler = new AndroidKeyHandler(this);
- }
- }
-
- public AndroidInputHandler(AndroidTouchHandler touchInput,
- AndroidKeyHandler keyInput, AndroidGestureHandler gestureHandler) {
- this.touchHandler = touchInput;
- this.keyHandler = keyInput;
- this.gestureHandler = gestureHandler;
- }
-
- public void setView(View view) {
- if (touchHandler != null) {
- touchHandler.setView(view);
- }
- if (keyHandler != null) {
- keyHandler.setView(view);
- }
- if (gestureHandler != null) {
- gestureHandler.setView(view);
- }
- this.view = (GLSurfaceView)view;
- }
-
- public View getView() {
- return view;
- }
-
- public float invertX(float origX) {
- return getJmeX(view.getWidth()) - origX;
- }
-
- public float invertY(float origY) {
- return getJmeY(view.getHeight()) - origY;
- }
-
- public float getJmeX(float origX) {
- return origX * scaleX;
- }
-
- public float getJmeY(float origY) {
- return origY * scaleY;
- }
-
- public void loadSettings(AppSettings settings) {
- keyboardEventsEnabled = settings.isEmulateKeyboard();
- mouseEventsEnabled = settings.isEmulateMouse();
- mouseEventsInvertX = settings.isEmulateMouseFlipX();
- mouseEventsInvertY = settings.isEmulateMouseFlipY();
- joystickEventsEnabled = settings.useJoysticks();
-
- // view width and height are 0 until the view is displayed on the screen
- if (view.getWidth() != 0 && view.getHeight() != 0) {
- scaleX = (float)settings.getWidth() / (float)view.getWidth();
- scaleY = (float)settings.getHeight() / (float)view.getHeight();
- }
- logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
- new Object[]{scaleX, scaleY});
-
- }
-
- // -----------------------------------------
- // JME3 Input interface
- @Override
- public void initialize() {
- touchEventPool.initialize();
- if (touchHandler != null) {
- touchHandler.initialize();
- }
- if (keyHandler != null) {
- keyHandler.initialize();
- }
- if (gestureHandler != null) {
- gestureHandler.initialize();
- }
-
- initialized = true;
- }
-
- @Override
- public void destroy() {
- initialized = false;
-
- touchEventPool.destroy();
- if (touchHandler != null) {
- touchHandler.destroy();
- }
- if (keyHandler != null) {
- keyHandler.destroy();
- }
- if (gestureHandler != null) {
- gestureHandler.destroy();
- }
-
- setView(null);
- }
-
- @Override
- public boolean isInitialized() {
- return initialized;
- }
-
- @Override
- public void setInputListener(RawInputListener listener) {
- this.listener = listener;
- }
-
- @Override
- public long getInputTimeNanos() {
- return System.nanoTime();
- }
-
- public void update() {
- if (listener != null) {
- InputEvent inputEvent;
-
- while ((inputEvent = inputEventQueue.poll()) != null) {
- if (inputEvent instanceof TouchEvent) {
- listener.onTouchEvent((TouchEvent)inputEvent);
- } else if (inputEvent instanceof MouseButtonEvent) {
- listener.onMouseButtonEvent((MouseButtonEvent)inputEvent);
- } else if (inputEvent instanceof MouseMotionEvent) {
- listener.onMouseMotionEvent((MouseMotionEvent)inputEvent);
- } else if (inputEvent instanceof KeyInputEvent) {
- listener.onKeyEvent((KeyInputEvent)inputEvent);
- }
- }
- }
- }
-
- // -----------------------------------------
-
- public TouchEvent getFreeTouchEvent() {
- return touchEventPool.getNextFreeEvent();
- }
-
- public void addEvent(InputEvent event) {
- inputEventQueue.add(event);
- if (event instanceof TouchEvent) {
- touchEventPool.storeEvent((TouchEvent)event);
- }
- }
-
- public void setSimulateMouse(boolean simulate) {
- this.mouseEventsEnabled = simulate;
- }
-
- public boolean isSimulateMouse() {
- return mouseEventsEnabled;
- }
-
- public boolean isMouseEventsInvertX() {
- return mouseEventsInvertX;
- }
-
- public boolean isMouseEventsInvertY() {
- return mouseEventsInvertY;
- }
-
- public void setSimulateKeyboard(boolean simulate) {
- this.keyboardEventsEnabled = simulate;
- }
-
- public boolean isSimulateKeyboard() {
- return keyboardEventsEnabled;
- }
-
- public void setOmitHistoricEvents(boolean dontSendHistory) {
- this.dontSendHistory = dontSendHistory;
- }
-
-}
+/*
+ * 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.input.android;
+
+import android.opengl.GLSurfaceView;
+import android.view.GestureDetector;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import com.jme3.input.JoyInput;
+import com.jme3.input.TouchInput;
+import com.jme3.system.AppSettings;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AndroidInput is the main class that connects the Android system
+ * inputs to jME. It receives the inputs from the Android View and passes them
+ * to the appropriate classes based on the source of the input.
+ * This class is to be extended when new functionality is released in Android.
+ *
+ * @author iwgeric
+ */
+public class AndroidInputHandler implements View.OnTouchListener,
+ View.OnKeyListener {
+
+ private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
+
+ protected GLSurfaceView view;
+ protected AndroidTouchInput touchInput;
+ protected AndroidJoyInput joyInput;
+
+
+ public AndroidInputHandler() {
+ touchInput = new AndroidTouchInput(this);
+ joyInput = new AndroidJoyInput(this);
+ }
+
+ public void setView(View view) {
+ if (this.view != null && view != null && this.view.equals(view)) {
+ return;
+ }
+
+ if (this.view != null) {
+ removeListeners(this.view);
+ }
+
+ this.view = (GLSurfaceView)view;
+
+ if (this.view != null) {
+ addListeners(this.view);
+ }
+
+ joyInput.setView((GLSurfaceView)view);
+ }
+
+ public View getView() {
+ return view;
+ }
+
+ protected void removeListeners(GLSurfaceView view) {
+ view.setOnTouchListener(null);
+ view.setOnKeyListener(null);
+ touchInput.setGestureDetector(null);
+ touchInput.setScaleDetector(null);
+ }
+
+ protected void addListeners(GLSurfaceView view) {
+ view.setOnTouchListener(this);
+ view.setOnKeyListener(this);
+ AndroidGestureProcessor gestureHandler = new AndroidGestureProcessor(touchInput);
+ touchInput.setGestureDetector(new GestureDetector(
+ view.getContext(), gestureHandler));
+ touchInput.setScaleDetector(new ScaleGestureDetector(
+ view.getContext(), gestureHandler));
+ }
+
+ public void loadSettings(AppSettings settings) {
+ touchInput.loadSettings(settings);
+ }
+
+ public TouchInput getTouchInput() {
+ return touchInput;
+ }
+
+ public JoyInput getJoyInput() {
+ return joyInput;
+ }
+
+ /*
+ * Android input events include the source from which the input came from.
+ * We must look at the source of the input event to determine which type
+ * of jME input it belongs to.
+ * If the input is from a gamepad or joystick source, the event is sent
+ * to the JoyInput class to convert the event into jME joystick events.
+ *
+ * If the input is from a touchscreen source, the event is sent to the
+ * TouchProcessor to convert the event into touch events.
+ * The TouchProcessor also converts the events into Mouse and Key events
+ * if AppSettings is set to simulate Mouse or Keyboard events.
+ *
+ * Android reports the source as a bitmask as shown below.
+ *
+ * InputDevice Sources
+ * 0000 0000 0000 0000 0000 0000 0000 0000 - 32 bit bitmask
+ *
+ * 0000 0000 0000 0000 0000 0000 1111 1111 - SOURCE_CLASS_MASK (0x000000ff)
+ * 0000 0000 0000 0000 0000 0000 0000 0000 - SOURCE_CLASS_NONE (0x00000000)
+ * 0000 0000 0000 0000 0000 0000 0000 0001 - SOURCE_CLASS_BUTTON (0x00000001)
+ * 0000 0000 0000 0000 0000 0000 0000 0010 - SOURCE_CLASS_POINTER (0x00000002)
+ * 0000 0000 0000 0000 0000 0000 0000 0100 - SOURCE_CLASS_TRACKBALL (0x00000004)
+ * 0000 0000 0000 0000 0000 0000 0000 1000 - SOURCE_CLASS_POSITION (0x00000008)
+ * 0000 0000 0000 0000 0000 0000 0001 0000 - SOURCE_CLASS_JOYSTICK (0x00000010)
+ *
+ * 1111 1111 1111 1111 1111 1111 0000 0000 - Source_Any (0xffffff00)
+ * 0000 0000 0000 0000 0000 0000 0000 0000 - SOURCE_UNKNOWN (0x00000000)
+ * 0000 0000 0000 0000 0000 0001 0000 0001 - SOURCE_KEYBOARD (0x00000101)
+ * 0000 0000 0000 0000 0000 0010 0000 0001 - SOURCE_DPAD (0x00000201)
+ * 0000 0000 0000 0000 0000 0100 0000 0001 - SOURCE_GAMEPAD (0x00000401)
+ * 0000 0000 0000 0000 0001 0000 0000 0010 - SOURCE_TOUCHSCREEN (0x00001002)
+ * 0000 0000 0000 0000 0010 0000 0000 0010 - SOURCE_MOUSE (0x00002002)
+ * 0000 0000 0000 0000 0100 0000 0000 0010 - SOURCE_STYLUS (0x00004002)
+ * 0000 0000 0000 0001 0000 0000 0000 0100 - SOURCE_TRACKBALL (0x00010004)
+ * 0000 0000 0001 0000 0000 0000 0000 1000 - SOURCE_TOUCHPAD (0x00100008)
+ * 0000 0000 0010 0000 0000 0000 0000 0000 - SOURCE_TOUCH_NAVIGATION (0x00200000)
+ * 0000 0001 0000 0000 0000 0000 0001 0000 - SOURCE_JOYSTICK (0x01000010)
+ * 0000 0010 0000 0000 0000 0000 0000 0001 - SOURCE_HDMI (0x02000001)
+ *
+ * Example values reported by Android for Source
+ * 4,098 = 0x00001002 =
+ * 0000 0000 0000 0000 0001 0000 0000 0010 - SOURCE_CLASS_POINTER
+ * SOURCE_TOUCHSCREEN
+ * 1,281 = 0x00000501 =
+ * 0000 0000 0000 0000 0000 0101 0000 0001 - SOURCE_CLASS_BUTTON
+ * SOURCE_KEYBOARD
+ * SOURCE_GAMEPAD
+ * 16,777,232 = 0x01000010 =
+ * 0000 0001 0000 0000 0000 0000 0001 0000 - SOURCE_CLASS_JOYSTICK
+ * SOURCE_JOYSTICK
+ *
+ * 16,778,513 = 0x01000511 =
+ * 0000 0001 0000 0000 0000 0101 0001 0001 - SOURCE_CLASS_BUTTON
+ * SOURCE_CLASS_JOYSTICK
+ * SOURCE_GAMEPAD
+ * SOURCE_KEYBOARD
+ * SOURCE_JOYSTICK
+ *
+ * 257 = 0x00000101 =
+ * 0000 0000 0000 0000 0000 0001 0000 0001 - SOURCE_CLASS_BUTTON
+ * SOURCE_KEYBOARD
+ *
+ *
+ *
+ */
+
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = false;
+
+ int source = event.getSource();
+// logger.log(Level.INFO, "onTouch source: {0}", source);
+
+ boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN);
+// logger.log(Level.INFO, "onTouch source: {0}, isTouch: {1}",
+// new Object[]{source, isTouch});
+
+ if (isTouch && touchInput != null) {
+ // send the event to the touch processor
+ consumed = touchInput.onTouch(event);
+ }
+
+ return consumed;
+
+ }
+
+ @Override
+ public boolean onKey(View view, int keyCode, KeyEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = false;
+
+ int source = event.getSource();
+// logger.log(Level.INFO, "onKey source: {0}", source);
+
+ boolean isTouch =
+ ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) ||
+ ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD);
+// logger.log(Level.INFO, "onKey source: {0}, isTouch: {1}",
+// new Object[]{source, isTouch});
+
+ if (touchInput != null) {
+ consumed = touchInput.onKey(event);
+ }
+
+ return consumed;
+
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
new file mode 100644
index 000000000..4b39ed24c
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
@@ -0,0 +1,159 @@
+/*
+ * 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.input.android;
+
+import android.opengl.GLSurfaceView;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AndroidInputHandler14 extends AndroidInputHandler to
+ * add the onHover and onGenericMotion events that where added in Android rev 14 (Android 4.0).
+ * The onGenericMotion events are the main interface to Joystick axes. They
+ * were actually released in Android rev 12.
+ *
+ * @author iwgeric
+ */
+public class AndroidInputHandler14 extends AndroidInputHandler implements View.OnHoverListener,
+ View.OnGenericMotionListener {
+
+ private static final Logger logger = Logger.getLogger(AndroidInputHandler14.class.getName());
+
+ public AndroidInputHandler14() {
+ touchInput = new AndroidTouchInput14(this);
+ joyInput = new AndroidJoyInput14(this);
+ }
+
+ @Override
+ protected void removeListeners(GLSurfaceView view) {
+ super.removeListeners(view);
+ view.setOnHoverListener(null);
+ view.setOnGenericMotionListener(null);
+ }
+
+ @Override
+ protected void addListeners(GLSurfaceView view) {
+ super.addListeners(view);
+ view.setOnHoverListener(this);
+ view.setOnGenericMotionListener(this);
+ }
+
+ @Override
+ public boolean onHover(View view, MotionEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = false;
+
+ int source = event.getSource();
+// logger.log(Level.INFO, "onTouch source: {0}", source);
+
+ boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN);
+// logger.log(Level.INFO, "onTouch source: {0}, isTouch: {1}",
+// new Object[]{source, isTouch});
+
+ if (isTouch && touchInput != null) {
+ // send the event to the touch processor
+ consumed = ((AndroidTouchInput14)touchInput).onHover(event);
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean onGenericMotion(View view, MotionEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = false;
+
+ int source = event.getSource();
+// logger.log(Level.INFO, "onGenericMotion source: {0}", source);
+
+ boolean isJoystick =
+ ((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
+ ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK);
+
+ if (isJoystick && joyInput != null) {
+// logger.log(Level.INFO, "onGenericMotion source: {0}, isJoystick: {1}",
+// new Object[]{source, isJoystick});
+ // send the event to the touch processor
+ consumed = consumed || ((AndroidJoyInput14)joyInput).onGenericMotion(event);
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean onKey(View view, int keyCode, KeyEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = false;
+
+// logger.log(Level.INFO, "onKey keyCode: {0}, action: {1}, event: {2}",
+// new Object[]{KeyEvent.keyCodeToString(keyCode), event.getAction(), event});
+ int source = event.getSource();
+// logger.log(Level.INFO, "onKey source: {0}", source);
+
+ boolean isTouch =
+ ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) ||
+ ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD);
+ boolean isJoystick =
+ ((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
+ ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK);
+
+ if (isTouch && touchInput != null) {
+// logger.log(Level.INFO, "onKey source: {0}, isTouch: {1}",
+// new Object[]{source, isTouch});
+ consumed = touchInput.onKey(event);
+ }
+ if (isJoystick && joyInput != null) {
+// logger.log(Level.INFO, "onKey source: {0}, isJoystick: {1}",
+// new Object[]{source, isJoystick});
+ // use inclusive OR to make sure the onKey method is called.
+ consumed = consumed | ((AndroidJoyInput14)joyInput).onKey(event);
+ }
+
+ return consumed;
+
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java
new file mode 100644
index 000000000..1e610d24e
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2009-2015 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.input.android;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Vibrator;
+import com.jme3.input.InputManager;
+import com.jme3.input.JoyInput;
+import com.jme3.input.Joystick;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.InputEvent;
+import com.jme3.input.event.JoyAxisEvent;
+import com.jme3.input.event.JoyButtonEvent;
+import com.jme3.system.AppSettings;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Main class that manages various joystick devices. Joysticks can be many forms
+ * including a simulated joystick to communicate the device orientation as well
+ * as physical gamepads.
+ * This class manages all the joysticks and feeds the inputs from each back
+ * to jME's InputManager.
+ *
+ * This handler also supports the joystick.rumble(rumbleAmount) method. In this
+ * case, when joystick.rumble(rumbleAmount) is called, the Android device will vibrate
+ * if the device has a built in vibrate motor.
+ *
+ * Because Andorid does not allow for the user to define the intensity of the
+ * vibration, the rumble amount (ie strength) is converted into vibration pulses
+ * The stronger the strength amount, the shorter the delay between pulses. If
+ * amount is 1, then the vibration stays on the whole time. If amount is 0.5,
+ * the vibration will a pulse of equal parts vibration and delay.
+ * To turn off vibration, set rumble amount to 0.
+ *
+ * MainActivity needs the following line to enable Joysticks on Android platforms
+ * joystickEventsEnabled = true;
+ * This is done to allow for battery conservation when sensor data or gamepads
+ * are not required by the application.
+ *
+ * To use the joystick rumble feature, the following line needs to be
+ * added to the Android Manifest File
+ *
+ *
+ * @author iwgeric
+ */
+public class AndroidJoyInput implements JoyInput {
+ private static final Logger logger = Logger.getLogger(AndroidJoyInput.class.getName());
+ public static boolean disableSensors = false;
+
+ protected AndroidInputHandler inputHandler;
+ protected List joystickList = new ArrayList();
+// private boolean dontSendHistory = false;
+
+
+ // Internal
+ private boolean initialized = false;
+ private RawInputListener listener = null;
+ private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue();
+ private AndroidSensorJoyInput sensorJoyInput;
+ private Vibrator vibrator = null;
+ private boolean vibratorActive = false;
+ private long maxRumbleTime = 250; // 250ms
+
+ public AndroidJoyInput(AndroidInputHandler inputHandler) {
+ this.inputHandler = inputHandler;
+ sensorJoyInput = new AndroidSensorJoyInput(this);
+ }
+
+ public void setView(GLSurfaceView view) {
+ if (view == null) {
+ vibrator = null;
+ } else {
+ // Get instance of Vibrator from current Context
+ vibrator = (Vibrator) view.getContext().getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrator == null) {
+ logger.log(Level.FINE, "Vibrator Service not found.");
+ }
+ }
+
+ if (sensorJoyInput != null) {
+ sensorJoyInput.setView(view);
+ }
+ }
+
+ public void loadSettings(AppSettings settings) {
+
+ }
+
+ public void addEvent(InputEvent event) {
+ eventQueue.add(event);
+ }
+
+ /**
+ * Pauses the joystick device listeners to save battery life if they are not needed.
+ * Used to pause when the activity pauses
+ */
+ public void pauseJoysticks() {
+ if (sensorJoyInput != null) {
+ sensorJoyInput.pauseSensors();
+ }
+ if (vibrator != null && vibratorActive) {
+ vibrator.cancel();
+ }
+
+ }
+
+ /**
+ * Resumes the joystick device listeners.
+ * Used to resume when the activity comes to the top of the stack
+ */
+ public void resumeJoysticks() {
+ if (sensorJoyInput != null) {
+ sensorJoyInput.resumeSensors();
+ }
+
+ }
+
+ @Override
+ public void initialize() {
+ initialized = true;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ @Override
+ public void destroy() {
+ initialized = false;
+
+ if (sensorJoyInput != null) {
+ sensorJoyInput.destroy();
+ }
+
+ setView(null);
+ }
+
+ @Override
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+
+ @Override
+ public void setJoyRumble(int joyId, float amount) {
+ // convert amount to pulses since Android doesn't allow intensity
+ if (vibrator != null) {
+ final long rumbleOnDur = (long)(amount * maxRumbleTime); // ms to pulse vibration on
+ final long rumbleOffDur = maxRumbleTime - rumbleOnDur; // ms to delay between pulses
+ final long[] rumblePattern = {
+ 0, // start immediately
+ rumbleOnDur, // time to leave vibration on
+ rumbleOffDur // time to delay between vibrations
+ };
+ final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from
+
+// logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
+// new Object[]{amount, rumbleOnDur, rumbleOffDur});
+
+ if (rumbleOnDur > 0) {
+ vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
+ vibratorActive = true;
+ } else {
+ vibrator.cancel();
+ vibratorActive = false;
+ }
+ }
+ }
+
+ @Override
+ public Joystick[] loadJoysticks(InputManager inputManager) {
+ logger.log(Level.INFO, "loading joysticks for {0}", this.getClass().getName());
+ if (!disableSensors) {
+ joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager));
+ }
+ return joystickList.toArray( new Joystick[joystickList.size()] );
+ }
+
+ @Override
+ public void update() {
+ if (sensorJoyInput != null) {
+ sensorJoyInput.update();
+ }
+
+ if (listener != null) {
+ InputEvent inputEvent;
+
+ while ((inputEvent = eventQueue.poll()) != null) {
+ if (inputEvent instanceof JoyAxisEvent) {
+ listener.onJoyAxisEvent((JoyAxisEvent)inputEvent);
+ } else if (inputEvent instanceof JoyButtonEvent) {
+ listener.onJoyButtonEvent((JoyButtonEvent)inputEvent);
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput14.java
new file mode 100644
index 000000000..00478aea1
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput14.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009-2015 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.input.android;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import com.jme3.input.InputManager;
+import com.jme3.input.Joystick;
+import java.util.logging.Logger;
+
+/**
+ * AndroidJoyInput14 extends AndroidJoyInput
+ * to include support for physical joysticks/gamepads.
+ *
+ * @author iwgeric
+ */
+public class AndroidJoyInput14 extends AndroidJoyInput {
+ private static final Logger logger = Logger.getLogger(AndroidJoyInput14.class.getName());
+
+ private AndroidJoystickJoyInput14 joystickJoyInput;
+
+ public AndroidJoyInput14(AndroidInputHandler inputHandler) {
+ super(inputHandler);
+ joystickJoyInput = new AndroidJoystickJoyInput14(this);
+ }
+
+ /**
+ * Pauses the joystick device listeners to save battery life if they are not needed.
+ * Used to pause when the activity pauses
+ */
+ @Override
+ public void pauseJoysticks() {
+ super.pauseJoysticks();
+
+ if (joystickJoyInput != null) {
+ joystickJoyInput.pauseJoysticks();
+ }
+ }
+
+ /**
+ * Resumes the joystick device listeners.
+ * Used to resume when the activity comes to the top of the stack
+ */
+ @Override
+ public void resumeJoysticks() {
+ super.resumeJoysticks();
+ if (joystickJoyInput != null) {
+ joystickJoyInput.resumeJoysticks();
+ }
+
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ if (joystickJoyInput != null) {
+ joystickJoyInput.destroy();
+ }
+ }
+
+ @Override
+ public Joystick[] loadJoysticks(InputManager inputManager) {
+ // load the simulated joystick for device orientation
+ super.loadJoysticks(inputManager);
+ // load physical gamepads/joysticks
+ joystickList.addAll(joystickJoyInput.loadJoysticks(joystickList.size(), inputManager));
+ // return the list of joysticks back to InputManager
+ return joystickList.toArray( new Joystick[joystickList.size()] );
+ }
+
+ public boolean onGenericMotion(MotionEvent event) {
+ return joystickJoyInput.onGenericMotion(event);
+ }
+
+ public boolean onKey(KeyEvent event) {
+ return joystickJoyInput.onKey(event);
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java
new file mode 100644
index 000000000..1ed98977c
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2009-2015 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.input.android;
+
+import android.view.InputDevice;
+import android.view.InputDevice.MotionRange;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import com.jme3.input.AbstractJoystick;
+import com.jme3.input.DefaultJoystickAxis;
+import com.jme3.input.DefaultJoystickButton;
+import com.jme3.input.InputManager;
+import com.jme3.input.JoyInput;
+import com.jme3.input.Joystick;
+import com.jme3.input.JoystickAxis;
+import com.jme3.input.JoystickButton;
+import com.jme3.input.JoystickCompatibilityMappings;
+import com.jme3.input.event.JoyAxisEvent;
+import com.jme3.input.event.JoyButtonEvent;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Main class that creates and manages Android inputs for physical gamepads/joysticks.
+ *
+ * @author iwgeric
+ */
+public class AndroidJoystickJoyInput14 {
+ private static final Logger logger = Logger.getLogger(AndroidJoystickJoyInput14.class.getName());
+
+ private boolean loaded = false;
+ private AndroidJoyInput joyInput;
+ private Map joystickIndex = new HashMap();
+
+ private static int[] AndroidGamepadButtons = {
+ // Dpad buttons
+ KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.KEYCODE_DPAD_CENTER,
+
+ // pressing joystick down
+ KeyEvent.KEYCODE_BUTTON_THUMBL, KeyEvent.KEYCODE_BUTTON_THUMBR,
+
+ // buttons
+ KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B,
+ KeyEvent.KEYCODE_BUTTON_X, KeyEvent.KEYCODE_BUTTON_Y,
+
+ // buttons on back of device
+ KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_BUTTON_R1,
+ KeyEvent.KEYCODE_BUTTON_L2, KeyEvent.KEYCODE_BUTTON_R2,
+
+ // start / select buttons
+ KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_BUTTON_SELECT,
+ KeyEvent.KEYCODE_BUTTON_MODE,
+
+ };
+
+ public AndroidJoystickJoyInput14(AndroidJoyInput joyInput) {
+ this.joyInput = joyInput;
+ }
+
+
+ public void pauseJoysticks() {
+
+ }
+
+ public void resumeJoysticks() {
+
+ }
+
+ public void destroy() {
+
+ }
+
+ public List loadJoysticks(int joyId, InputManager inputManager) {
+ logger.log(Level.INFO, "loading Joystick devices");
+ ArrayList joysticks = new ArrayList();
+ joysticks.clear();
+ joystickIndex.clear();
+
+ ArrayList gameControllerDeviceIds = new ArrayList();
+ int[] deviceIds = InputDevice.getDeviceIds();
+ for (int deviceId : deviceIds) {
+ InputDevice dev = InputDevice.getDevice(deviceId);
+ int sources = dev.getSources();
+ logger.log(Level.FINE, "deviceId[{0}] sources: {1}", new Object[]{deviceId, sources});
+
+ // Verify that the device has gamepad buttons, control sticks, or both.
+ if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
+ ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
+ // This device is a game controller. Store its device ID.
+ if (!gameControllerDeviceIds.contains(deviceId)) {
+ gameControllerDeviceIds.add(deviceId);
+ logger.log(Level.FINE, "Attempting to create joystick for device: {0}", dev);
+ // Create an AndroidJoystick and store the InputDevice so we
+ // can later correspond the input from the InputDevice to the
+ // appropriate jME Joystick event
+ AndroidJoystick joystick = new AndroidJoystick(inputManager,
+ joyInput,
+ dev,
+ joyId+joysticks.size(),
+ dev.getName());
+ joystickIndex.put(deviceId, joystick);
+ joysticks.add(joystick);
+
+ // Each analog input is reported as a MotionRange
+ // The axis number corresponds to the type of axis
+ // The AndroidJoystick.addAxis(MotionRange) converts the axis
+ // type reported by Android into the jME Joystick axis
+ List motionRanges = dev.getMotionRanges();
+ for (MotionRange motionRange: motionRanges) {
+ logger.log(Level.INFO, "motion range: {0}", motionRange.toString());
+ logger.log(Level.INFO, "axis: {0}", motionRange.getAxis());
+ JoystickAxis axis = joystick.addAxis(motionRange);
+ logger.log(Level.INFO, "added axis: {0}", axis);
+ }
+
+ // InputDevice has a method for determining if a keyCode is
+ // supported (InputDevice public boolean[] hasKeys (int... keys)).
+ // But this method wasn't added until rev 19 (Android 4.4)
+ // Therefore, we only can query the entire device and see if
+ // any InputDevice supports the keyCode. This may result in
+ // buttons being configured that don't exist on the specific
+ // device, but I haven't found a better way yet.
+ for (int keyCode: AndroidGamepadButtons) {
+ logger.log(Level.INFO, "button[{0}]: {1}",
+ new Object[]{keyCode, KeyCharacterMap.deviceHasKey(keyCode)});
+ if (KeyCharacterMap.deviceHasKey(keyCode)) {
+ // add button even though we aren't sure if the button
+ // actually exists on this InputDevice
+ logger.log(Level.INFO, "button[{0}] exists somewhere", keyCode);
+ JoystickButton button = joystick.addButton(keyCode);
+ logger.log(Level.INFO, "added button: {0}", button);
+ }
+ }
+
+ }
+ }
+ }
+
+
+ loaded = true;
+ return joysticks;
+ }
+
+ public boolean onGenericMotion(MotionEvent event) {
+ boolean consumed = false;
+// logger.log(Level.INFO, "onGenericMotion event: {0}", event);
+ event.getDeviceId();
+ event.getSource();
+// logger.log(Level.INFO, "deviceId: {0}, source: {1}", new Object[]{event.getDeviceId(), event.getSource()});
+ AndroidJoystick joystick = joystickIndex.get(event.getDeviceId());
+ if (joystick != null) {
+ for (int androidAxis: joystick.getAndroidAxes()) {
+ String axisName = MotionEvent.axisToString(androidAxis);
+ float value = event.getAxisValue(androidAxis);
+ int action = event.getAction();
+ if (action == MotionEvent.ACTION_MOVE) {
+// logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}",
+// new Object[]{androidAxis, axisName, value});
+ JoystickAxis axis = joystick.getAxis(androidAxis);
+ if (axis != null) {
+// logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}, deadzone: {3}",
+// new Object[]{androidAxis, axisName, value, axis.getDeadZone()});
+ JoyAxisEvent axisEvent = new JoyAxisEvent(axis, value);
+ joyInput.addEvent(axisEvent);
+ consumed = true;
+ } else {
+// logger.log(Level.INFO, "axis was null for axisName: {0}", axisName);
+ }
+ } else {
+// logger.log(Level.INFO, "action: {0}", action);
+ }
+ }
+ }
+
+ return consumed;
+ }
+
+ public boolean onKey(KeyEvent event) {
+ boolean consumed = false;
+// logger.log(Level.INFO, "onKey event: {0}", event);
+
+ event.getDeviceId();
+ event.getSource();
+ AndroidJoystick joystick = joystickIndex.get(event.getDeviceId());
+ if (joystick != null) {
+ JoystickButton button = joystick.getButton(event.getKeyCode());
+ boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
+ if (button != null) {
+ JoyButtonEvent buttonEvent = new JoyButtonEvent(button, pressed);
+ joyInput.addEvent(buttonEvent);
+ consumed = true;
+ } else {
+ JoystickButton newButton = joystick.addButton(event.getKeyCode());
+ JoyButtonEvent buttonEvent = new JoyButtonEvent(newButton, pressed);
+ joyInput.addEvent(buttonEvent);
+ consumed = true;
+ }
+ }
+
+ return consumed;
+ }
+
+ protected class AndroidJoystick extends AbstractJoystick {
+
+ private JoystickAxis nullAxis;
+ private InputDevice device;
+ private JoystickAxis xAxis;
+ private JoystickAxis yAxis;
+ private JoystickAxis povX;
+ private JoystickAxis povY;
+ private Map axisIndex = new HashMap();
+ private Map buttonIndex = new HashMap();
+
+ public AndroidJoystick( InputManager inputManager, JoyInput joyInput, InputDevice device,
+ int joyId, String name ) {
+ super( inputManager, joyInput, joyId, name );
+
+ this.device = device;
+
+ this.nullAxis = new DefaultJoystickAxis( getInputManager(), this, -1,
+ "Null", "null", false, false, 0 );
+ this.xAxis = nullAxis;
+ this.yAxis = nullAxis;
+ this.povX = nullAxis;
+ this.povY = nullAxis;
+ }
+
+ protected JoystickAxis getAxis(int androidAxis) {
+ return axisIndex.get(androidAxis);
+ }
+
+ protected Set getAndroidAxes() {
+ return axisIndex.keySet();
+ }
+
+ protected JoystickButton getButton(int keyCode) {
+ return buttonIndex.get(keyCode);
+ }
+
+ protected JoystickButton addButton( int keyCode ) {
+
+// logger.log(Level.FINE, "Adding button: {0}", keyCode);
+
+ String name = KeyEvent.keyCodeToString(keyCode);
+ String original = KeyEvent.keyCodeToString(keyCode);
+ // A/B/X/Y buttons
+ if (keyCode == KeyEvent.KEYCODE_BUTTON_Y) {
+ original = JoystickButton.BUTTON_0;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_B) {
+ original = JoystickButton.BUTTON_1;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_A) {
+ original = JoystickButton.BUTTON_2;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_X) {
+ original = JoystickButton.BUTTON_3;
+ // Front buttons Some of these have the top ones and the bottoms ones flipped.
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_L1) {
+ original = JoystickButton.BUTTON_4;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_R1) {
+ original = JoystickButton.BUTTON_5;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_L2) {
+ original = JoystickButton.BUTTON_6;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_R2) {
+ original = JoystickButton.BUTTON_7;
+// // Dpad buttons
+// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+// original = JoystickButton.BUTTON_8;
+// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+// original = JoystickButton.BUTTON_9;
+// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+// original = JoystickButton.BUTTON_8;
+// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+// original = JoystickButton.BUTTON_9;
+ // Select and start buttons
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_SELECT) {
+ original = JoystickButton.BUTTON_8;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_START) {
+ original = JoystickButton.BUTTON_9;
+ // Joystick push buttons
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBL) {
+ original = JoystickButton.BUTTON_10;
+ } else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBR) {
+ original = JoystickButton.BUTTON_11;
+ }
+
+ String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original );
+ if( logicalId == null ? original != null : !logicalId.equals(original) ) {
+ logger.log(Level.FINE, "Remapped: {0} to: {1}",
+ new Object[]{original, logicalId});
+ }
+
+ JoystickButton button = new DefaultJoystickButton( getInputManager(), this, getButtonCount(),
+ name, logicalId );
+ addButton(button);
+ buttonIndex.put( keyCode, button );
+ return button;
+ }
+
+ protected JoystickAxis addAxis(MotionRange motionRange) {
+
+ String name = MotionEvent.axisToString(motionRange.getAxis());
+
+ String original = MotionEvent.axisToString(motionRange.getAxis());
+ if (motionRange.getAxis() == MotionEvent.AXIS_X) {
+ original = JoystickAxis.X_AXIS;
+ } else if (motionRange.getAxis() == MotionEvent.AXIS_Y) {
+ original = JoystickAxis.Y_AXIS;
+ } else if (motionRange.getAxis() == MotionEvent.AXIS_Z) {
+ original = JoystickAxis.Z_AXIS;
+ } else if (motionRange.getAxis() == MotionEvent.AXIS_RZ) {
+ original = JoystickAxis.Z_ROTATION;
+ } else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) {
+ original = JoystickAxis.POV_X;
+ } else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) {
+ original = JoystickAxis.POV_Y;
+ }
+ String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original );
+ if( logicalId == null ? original != null : !logicalId.equals(original) ) {
+ logger.log(Level.FINE, "Remapped: {0} to: {1}",
+ new Object[]{original, logicalId});
+ }
+
+ JoystickAxis axis = new DefaultJoystickAxis(getInputManager(),
+ this,
+ getAxisCount(),
+ name,
+ logicalId,
+ true,
+ true,
+ motionRange.getFlat());
+
+ if (motionRange.getAxis() == MotionEvent.AXIS_X) {
+ xAxis = axis;
+ }
+ if (motionRange.getAxis() == MotionEvent.AXIS_Y) {
+ yAxis = axis;
+ }
+ if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) {
+ povX = axis;
+ }
+ if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) {
+ povY = axis;
+ }
+
+ addAxis(axis);
+ axisIndex.put(motionRange.getAxis(), axis);
+ return axis;
+ }
+
+ @Override
+ public JoystickAxis getXAxis() {
+ return xAxis;
+ }
+
+ @Override
+ public JoystickAxis getYAxis() {
+ return yAxis;
+ }
+
+ @Override
+ public JoystickAxis getPovXAxis() {
+ return povX;
+ }
+
+ @Override
+ public JoystickAxis getPovYAxis() {
+ return povY;
+ }
+
+ @Override
+ public int getXAxisIndex(){
+ return xAxis.getAxisId();
+ }
+
+ @Override
+ public int getYAxisIndex(){
+ return yAxis.getAxisId();
+ }
+ }
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyHandler.java
deleted file mode 100644
index 997974c31..000000000
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyHandler.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.input.android;
-
-import android.content.Context;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import com.jme3.input.event.KeyInputEvent;
-import com.jme3.input.event.TouchEvent;
-import java.util.logging.Logger;
-
-/**
- * AndroidKeyHandler recieves onKey events from the Android system and creates
- * the jME KeyEvents. onKey is used by Android to receive keys from the keyboard
- * or device buttons. All key events are consumed by jME except for the Volume
- * buttons and menu button.
- *
- * This class also provides the functionality to display or hide the soft keyboard
- * for inputing single key events. Use OGLESContext to display an dialog to type
- * in complete strings.
- *
- * @author iwgeric
- */
-public class AndroidKeyHandler implements View.OnKeyListener {
- private static final Logger logger = Logger.getLogger(AndroidKeyHandler.class.getName());
-
- private AndroidInputHandler androidInput;
- private boolean sendKeyEvents = true;
-
- public AndroidKeyHandler(AndroidInputHandler androidInput) {
- this.androidInput = androidInput;
- }
-
- public void initialize() {
- }
-
- public void destroy() {
- }
-
- public void setView(View view) {
- if (view != null) {
- view.setOnKeyListener(this);
- } else {
- androidInput.getView().setOnKeyListener(null);
- }
- }
-
- /**
- * onKey gets called from android thread on key events
- */
- public boolean onKey(View view, int keyCode, KeyEvent event) {
- if (androidInput.isInitialized() && view != androidInput.getView()) {
- return false;
- }
-
- TouchEvent evt;
- // TODO: get touch event from pool
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- evt = new TouchEvent();
- evt.set(TouchEvent.Type.KEY_DOWN);
- evt.setKeyCode(keyCode);
- evt.setCharacters(event.getCharacters());
- evt.setTime(event.getEventTime());
-
- // Send the event
- androidInput.addEvent(evt);
-
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- evt = new TouchEvent();
- evt.set(TouchEvent.Type.KEY_UP);
- evt.setKeyCode(keyCode);
- evt.setCharacters(event.getCharacters());
- evt.setTime(event.getEventTime());
-
- // Send the event
- androidInput.addEvent(evt);
-
- }
-
- if (androidInput.isSimulateKeyboard()) {
- KeyInputEvent kie;
- char unicodeChar = (char)event.getUnicodeChar();
- int jmeKeyCode = AndroidKeyMapping.getJmeKey(keyCode);
-
- boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
- boolean repeating = pressed && event.getRepeatCount() > 0;
-
- kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating);
- kie.setTime(event.getEventTime());
- androidInput.addEvent(kie);
-// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
-// new Object[]{keyCode, jmeKeyCode, pressed, repeating});
-// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
- }
-
- // consume all keys ourself except Volume Up/Down and Menu
- // Don't do Menu so that typical Android Menus can be created and used
- // by the user in MainActivity
- if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) ||
- (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) ||
- (keyCode == KeyEvent.KEYCODE_MENU)) {
- return false;
- } else {
- return true;
- }
-
- }
-
-}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyMapping.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyMapping.java
index e99d8e9fc..32d1e0008 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyMapping.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidKeyMapping.java
@@ -37,13 +37,14 @@ import java.util.logging.Logger;
/**
* AndroidKeyMapping is just a utility to convert the Android keyCodes into
- * jME KeyCodes received in jME's KeyEvent will match between Desktop and Android.
- *
+ * jME KeyCodes so that events received in jME's KeyEvent will match between
+ * Desktop and Android.
+ *
* @author iwgeric
*/
public class AndroidKeyMapping {
private static final Logger logger = Logger.getLogger(AndroidKeyMapping.class.getName());
-
+
private static final int[] ANDROID_TO_JME = {
0x0, // unknown
0x0, // key code soft left
@@ -141,9 +142,13 @@ public class AndroidKeyMapping {
0x0,//media fastforward
0x0,//mute
};
-
+
public static int getJmeKey(int androidKey) {
- return ANDROID_TO_JME[androidKey];
+ if (androidKey > ANDROID_TO_JME.length) {
+ return androidKey;
+ } else {
+ return ANDROID_TO_JME[androidKey];
+ }
}
-
+
}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
index 034b068d8..cdd7e6494 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
@@ -37,7 +37,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.os.Vibrator;
+import android.opengl.GLSurfaceView;
import android.view.Surface;
import android.view.WindowManager;
import com.jme3.input.AbstractJoystick;
@@ -47,10 +47,8 @@ import com.jme3.input.JoyInput;
import com.jme3.input.Joystick;
import com.jme3.input.JoystickAxis;
import com.jme3.input.SensorJoystickAxis;
-import com.jme3.input.RawInputListener;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.math.FastMath;
-import com.jme3.system.android.JmeAndroidSystem;
import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry;
import java.util.ArrayList;
@@ -63,7 +61,7 @@ import java.util.logging.Logger;
* A single joystick is configured and includes data for all configured sensors
* as seperate axes of the joystick.
*
- * Each axis is named accounting to the static strings in SensorJoystickAxis.
+ * Each axis is named according to the static strings in SensorJoystickAxis.
* Refer to the strings defined in SensorJoystickAxis for a list of supported
* sensors and their axis data. Each sensor type defined in SensorJoystickAxis
* will be attempted to be configured. If the device does not support a particular
@@ -72,46 +70,21 @@ import java.util.logging.Logger;
* The joystick.getXAxis and getYAxis methods of the joystick are configured to
* return the device orientation values in the device's X and Y directions.
*
- * This joystick also supports the joystick.rumble(rumbleAmount) method. In this
- * case, when joystick.rumble(rumbleAmount) is called, the Android device will vibrate
- * if the device has a built in vibrate motor.
- *
- * Because Andorid does not allow for the user to define the intensity of the
- * vibration, the rumble amount (ie strength) is converted into vibration pulses
- * The stronger the strength amount, the shorter the delay between pulses. If
- * amount is 1, then the vibration stays on the whole time. If amount is 0.5,
- * the vibration will a pulse of equal parts vibration and delay.
- * To turn off vibration, set rumble amount to 0.
- *
- * MainActivity needs the following line to enable Joysticks on Android platforms
- * joystickEventsEnabled = true;
- * This is done to allow for battery conservation when sensor data is not required
- * by the application.
- *
- * To use the joystick rumble feature, the following line needs to be
- * added to the Android Manifest File
- *
- *
* @author iwgeric
*/
-public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
+public class AndroidSensorJoyInput implements SensorEventListener {
private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName());
- private Context context = null;
- private InputManager inputManager = null;
+ private AndroidJoyInput joyInput;
private SensorManager sensorManager = null;
private WindowManager windowManager = null;
- private Vibrator vibrator = null;
- private boolean vibratorActive = false;
- private long maxRumbleTime = 250; // 250ms
- private RawInputListener listener = null;
private IntMap sensors = new IntMap();
- private AndroidJoystick[] joysticks;
private int lastRotation = 0;
- private boolean initialized = false;
private boolean loaded = false;
- private final ArrayList eventQueue = new ArrayList();
+ public AndroidSensorJoyInput(AndroidJoyInput joyInput) {
+ this.joyInput = joyInput;
+ }
/**
* Internal class to enclose data for each sensor.
@@ -120,10 +93,10 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
int androidSensorType = -1;
int androidSensorSpeed = SensorManager.SENSOR_DELAY_GAME;
Sensor sensor = null;
- int sensorAccuracy = 0;
+ int sensorAccuracy = -1;
float[] lastValues;
final Object valuesLock = new Object();
- ArrayList axes = new ArrayList();
+ ArrayList axes = new ArrayList();
boolean enabled = false;
boolean haveData = false;
@@ -134,16 +107,19 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
}
- private void initSensorManager() {
- this.context = JmeAndroidSystem.getView().getContext();
- // Get instance of the WindowManager from the current Context
- windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- // Get instance of the SensorManager from the current Context
- sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- // Get instance of Vibrator from current Context
- vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrator == null) {
- logger.log(Level.FINE, "Vibrator Service not found.");
+ public void setView(GLSurfaceView view) {
+ pauseSensors();
+ if (sensorManager != null) {
+ sensorManager.unregisterListener(this);
+ }
+ if (view == null) {
+ windowManager = null;
+ sensorManager = null;
+ } else {
+ // Get instance of the WindowManager from the current Context
+ windowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
+ // Get instance of the SensorManager from the current Context
+ sensorManager = (SensorManager) view.getContext().getSystemService(Context.SENSOR_SERVICE);
}
}
@@ -222,9 +198,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
unRegisterListener(entry.getKey());
}
}
- if (vibrator != null && vibratorActive) {
- vibrator.cancel();
- }
}
/**
@@ -333,7 +306,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
*/
private boolean updateOrientation() {
SensorData sensorData;
- AndroidJoystickAxis axis;
+ AndroidSensorJoystickAxis axis;
final float[] curInclinationMat = new float[16];
final float[] curRotationMat = new float[16];
final float[] rotatedRotationMat = new float[16];
@@ -400,10 +373,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
if (!sensorData.haveData) {
sensorData.haveData = true;
} else {
- synchronized (eventQueue){
- if (axis.isChanged()) {
- eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
- }
+ if (axis.isChanged()) {
+ joyInput.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
}
}
}
@@ -428,47 +399,14 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
// Start of JoyInput methods
- public void setJoyRumble(int joyId, float amount) {
- // convert amount to pulses since Android doesn't allow intensity
- if (vibrator != null) {
- final long rumbleOnDur = (long)(amount * maxRumbleTime); // ms to pulse vibration on
- final long rumbleOffDur = maxRumbleTime - rumbleOnDur; // ms to delay between pulses
- final long[] rumblePattern = {
- 0, // start immediately
- rumbleOnDur, // time to leave vibration on
- rumbleOffDur // time to delay between vibrations
- };
- final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from
-
- logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
- new Object[]{amount, rumbleOnDur, rumbleOffDur});
-
- if (rumbleOnDur > 0) {
- vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
- vibratorActive = true;
- } else {
- vibrator.cancel();
- vibratorActive = false;
- }
- }
-
- }
-
- public Joystick[] loadJoysticks(InputManager inputManager) {
- this.inputManager = inputManager;
-
- initSensorManager();
-
+ public Joystick loadJoystick(int joyId, InputManager inputManager) {
SensorData sensorData;
- List list = new ArrayList();
- AndroidJoystick joystick;
- AndroidJoystickAxis axis;
+ AndroidSensorJoystickAxis axis;
- joystick = new AndroidJoystick(inputManager,
- this,
- list.size(),
+ AndroidSensorJoystick joystick = new AndroidSensorJoystick(inputManager,
+ joyInput,
+ joyId,
"AndroidSensorsJoystick");
- list.add(joystick);
List availSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor: availSensors) {
@@ -555,14 +493,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
// }
- joysticks = list.toArray( new AndroidJoystick[list.size()] );
loaded = true;
- return joysticks;
- }
-
- public void initialize() {
- initialized = true;
- loaded = false;
+ return joystick;
}
public void update() {
@@ -570,15 +502,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
return;
}
updateOrientation();
- synchronized (eventQueue){
- // flush events to listener
- if (listener != null && eventQueue.size() > 0) {
- for (int i = 0; i < eventQueue.size(); i++){
- listener.onJoyAxisEvent(eventQueue.get(i));
- }
- eventQueue.clear();
- }
- }
}
public void destroy() {
@@ -588,39 +511,27 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
sensorManager.unregisterListener(this);
}
sensors.clear();
- eventQueue.clear();
- initialized = false;
loaded = false;
- joysticks = null;
sensorManager = null;
- vibrator = null;
- context = null;
- }
-
- public boolean isInitialized() {
- return initialized;
- }
-
- public void setInputListener(RawInputListener listener) {
- this.listener = listener;
- }
-
- public long getInputTimeNanos() {
- return System.nanoTime();
}
- // End of JoyInput methods
-
// Start of Android SensorEventListener methods
+ @Override
public void onSensorChanged(SensorEvent se) {
- if (!initialized || !loaded) {
+ if (!loaded) {
return;
}
+// logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}",
+// new Object[]{se.sensor.getName(), se.accuracy, se.values});
int sensorType = se.sensor.getType();
SensorData sensorData = sensors.get(sensorType);
+ if (sensorData != null) {
+// logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}",
+// new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE});
+ }
if (sensorData != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) {
if (sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
@@ -632,8 +543,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
}
}
- if (sensorData != null && sensorData.axes.size() > 0) {
- AndroidJoystickAxis axis;
+ if (sensorData.axes.size() > 0) {
+ AndroidSensorJoystickAxis axis;
for (int i=0; i lastPositions = new HashMap();
-
- protected int numPointers = 0;
-
- protected AndroidInputHandler androidInput;
- protected AndroidGestureHandler gestureHandler;
-
- public AndroidTouchHandler(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) {
- this.androidInput = androidInput;
- this.gestureHandler = gestureHandler;
- }
-
- public void initialize() {
- }
-
- public void destroy() {
- setView(null);
- }
-
- public void setView(View view) {
- if (view != null) {
- view.setOnTouchListener(this);
- } else {
- androidInput.getView().setOnTouchListener(null);
- }
- }
-
- protected int getPointerIndex(MotionEvent event) {
- return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
-
- protected int getPointerId(MotionEvent event) {
- return event.getPointerId(getPointerIndex(event));
- }
-
- protected int getAction(MotionEvent event) {
- return event.getAction() & MotionEvent.ACTION_MASK;
- }
-
- /**
- * onTouch gets called from android thread on touch events
- */
- public boolean onTouch(View view, MotionEvent event) {
- if (!androidInput.isInitialized() || view != androidInput.getView()) {
- return false;
- }
-
- boolean bWasHandled = false;
- TouchEvent touch = null;
- // System.out.println("native : " + event.getAction());
- int action = getAction(event);
- int pointerIndex = getPointerIndex(event);
- int pointerId = getPointerId(event);
- Vector2f lastPos = lastPositions.get(pointerId);
- float jmeX;
- float jmeY;
-
- numPointers = event.getPointerCount();
-
- // final int historySize = event.getHistorySize();
- //final int pointerCount = event.getPointerCount();
- switch (getAction(event)) {
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_DOWN:
- jmeX = androidInput.getJmeX(event.getX(pointerIndex));
- jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
- touch = androidInput.getFreeTouchEvent();
- touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0);
- touch.setPointerId(pointerId);
- touch.setTime(event.getEventTime());
- touch.setPressure(event.getPressure(pointerIndex));
-
- lastPos = new Vector2f(jmeX, jmeY);
- lastPositions.put(pointerId, lastPos);
-
- processEvent(touch);
-
- bWasHandled = true;
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- jmeX = androidInput.getJmeX(event.getX(pointerIndex));
- jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
- touch = androidInput.getFreeTouchEvent();
- touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0);
- touch.setPointerId(pointerId);
- touch.setTime(event.getEventTime());
- touch.setPressure(event.getPressure(pointerIndex));
- lastPositions.remove(pointerId);
-
- processEvent(touch);
-
- bWasHandled = true;
- break;
- case MotionEvent.ACTION_MOVE:
- // Convert all pointers into events
- for (int p = 0; p < event.getPointerCount(); p++) {
- jmeX = androidInput.getJmeX(event.getX(p));
- jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p)));
- lastPos = lastPositions.get(event.getPointerId(p));
- if (lastPos == null) {
- lastPos = new Vector2f(jmeX, jmeY);
- lastPositions.put(event.getPointerId(p), lastPos);
- }
-
- float dX = jmeX - lastPos.x;
- float dY = jmeY - lastPos.y;
- if (dX != 0 || dY != 0) {
- touch = androidInput.getFreeTouchEvent();
- touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY);
- touch.setPointerId(event.getPointerId(p));
- touch.setTime(event.getEventTime());
- touch.setPressure(event.getPressure(p));
- lastPos.set(jmeX, jmeY);
-
- processEvent(touch);
-
- bWasHandled = true;
- }
- }
- break;
- case MotionEvent.ACTION_OUTSIDE:
- break;
-
- }
-
- // Try to detect gestures
- if (gestureHandler != null) {
- gestureHandler.detectGesture(event);
- }
-
- return bWasHandled;
- }
-
- protected void processEvent(TouchEvent event) {
- // Add the touch event
- androidInput.addEvent(event);
- // MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
- if (androidInput.isSimulateMouse() && numPointers == 1) {
- InputEvent mouseEvent = generateMouseEvent(event);
- if (mouseEvent != null) {
- // Add the mouse event
- androidInput.addEvent(mouseEvent);
- }
- }
-
- }
-
- // TODO: Ring Buffer for mouse events?
- protected InputEvent generateMouseEvent(TouchEvent event) {
- InputEvent inputEvent = null;
- int newX;
- int newY;
- int newDX;
- int newDY;
-
- if (androidInput.isMouseEventsInvertX()) {
- newX = (int) (androidInput.invertX(event.getX()));
- newDX = (int)event.getDeltaX() * -1;
- } else {
- newX = (int) event.getX();
- newDX = (int)event.getDeltaX();
- }
-
- if (androidInput.isMouseEventsInvertY()) {
- newY = (int) (androidInput.invertY(event.getY()));
- newDY = (int)event.getDeltaY() * -1;
- } else {
- newY = (int) event.getY();
- newDY = (int)event.getDeltaY();
- }
-
- switch (event.getType()) {
- case DOWN:
- // Handle mouse down event
- inputEvent = new MouseButtonEvent(0, true, newX, newY);
- inputEvent.setTime(event.getTime());
- break;
-
- case UP:
- // Handle mouse up event
- inputEvent = new MouseButtonEvent(0, false, newX, newY);
- inputEvent.setTime(event.getTime());
- break;
-
- case HOVER_MOVE:
- case MOVE:
- inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan());
- inputEvent.setTime(event.getTime());
- break;
- }
-
- return inputEvent;
- }
-
-}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java
new file mode 100644
index 000000000..bddc5fab3
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java
@@ -0,0 +1,475 @@
+/*
+ * 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.input.android;
+
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.TouchInput;
+import com.jme3.input.event.InputEvent;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.input.event.TouchEvent;
+import static com.jme3.input.event.TouchEvent.Type.DOWN;
+import static com.jme3.input.event.TouchEvent.Type.MOVE;
+import static com.jme3.input.event.TouchEvent.Type.UP;
+import com.jme3.math.Vector2f;
+import com.jme3.system.AppSettings;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AndroidTouchInput is the base class that receives touch inputs from the
+ * Android system and creates the TouchEvents for jME. This class is designed
+ * to handle the base touch events for Android rev 9 (Android 2.3). This is
+ * extended by other classes to add features that were introducted after
+ * Android rev 9.
+ *
+ * @author iwgeric
+ */
+public class AndroidTouchInput implements TouchInput {
+ private static final Logger logger = Logger.getLogger(AndroidTouchInput.class.getName());
+
+ private boolean mouseEventsEnabled = true;
+ private boolean mouseEventsInvertX = false;
+ private boolean mouseEventsInvertY = false;
+ private boolean keyboardEventsEnabled = false;
+ private boolean dontSendHistory = false;
+
+ protected int numPointers = 0;
+ final private HashMap lastPositions = new HashMap();
+ final private ConcurrentLinkedQueue inputEventQueue = new ConcurrentLinkedQueue();
+ private final static int MAX_TOUCH_EVENTS = 1024;
+ private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS);
+ private float scaleX = 1f;
+ private float scaleY = 1f;
+
+ private boolean initialized = false;
+ private RawInputListener listener = null;
+
+ private GestureDetector gestureDetector;
+ private ScaleGestureDetector scaleDetector;
+
+ protected AndroidInputHandler androidInput;
+
+ public AndroidTouchInput(AndroidInputHandler androidInput) {
+ this.androidInput = androidInput;
+ }
+
+ public GestureDetector getGestureDetector() {
+ return gestureDetector;
+ }
+
+ public void setGestureDetector(GestureDetector gestureDetector) {
+ this.gestureDetector = gestureDetector;
+ }
+
+ public ScaleGestureDetector getScaleDetector() {
+ return scaleDetector;
+ }
+
+ public void setScaleDetector(ScaleGestureDetector scaleDetector) {
+ this.scaleDetector = scaleDetector;
+ }
+
+ public float invertX(float origX) {
+ return getJmeX(androidInput.getView().getWidth()) - origX;
+ }
+
+ public float invertY(float origY) {
+ return getJmeY(androidInput.getView().getHeight()) - origY;
+ }
+
+ public float getJmeX(float origX) {
+ return origX * scaleX;
+ }
+
+ public float getJmeY(float origY) {
+ return origY * scaleY;
+ }
+
+ public void loadSettings(AppSettings settings) {
+ keyboardEventsEnabled = settings.isEmulateKeyboard();
+ mouseEventsEnabled = settings.isEmulateMouse();
+ mouseEventsInvertX = settings.isEmulateMouseFlipX();
+ mouseEventsInvertY = settings.isEmulateMouseFlipY();
+
+ // view width and height are 0 until the view is displayed on the screen
+ if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) {
+ scaleX = (float)settings.getWidth() / (float)androidInput.getView().getWidth();
+ scaleY = (float)settings.getHeight() / (float)androidInput.getView().getHeight();
+ }
+ logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
+ new Object[]{scaleX, scaleY});
+
+
+ }
+
+
+ protected int getPointerIndex(MotionEvent event) {
+ return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ }
+
+ protected int getPointerId(MotionEvent event) {
+ return event.getPointerId(getPointerIndex(event));
+ }
+
+ protected int getAction(MotionEvent event) {
+ return event.getAction() & MotionEvent.ACTION_MASK;
+ }
+
+ public boolean onTouch(MotionEvent event) {
+ if (!isInitialized()) {
+ return false;
+ }
+
+ boolean bWasHandled = false;
+ TouchEvent touch = null;
+ // System.out.println("native : " + event.getAction());
+ int action = getAction(event);
+ int pointerIndex = getPointerIndex(event);
+ int pointerId = getPointerId(event);
+ Vector2f lastPos = lastPositions.get(pointerId);
+ float jmeX;
+ float jmeY;
+
+ numPointers = event.getPointerCount();
+
+ // final int historySize = event.getHistorySize();
+ //final int pointerCount = event.getPointerCount();
+ switch (getAction(event)) {
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_DOWN:
+ jmeX = getJmeX(event.getX(pointerIndex));
+ jmeY = invertY(getJmeY(event.getY(pointerIndex)));
+ touch = getFreeTouchEvent();
+ touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+
+ lastPos = new Vector2f(jmeX, jmeY);
+ lastPositions.put(pointerId, lastPos);
+
+ addEvent(touch);
+ addEvent(generateMouseEvent(touch));
+
+ bWasHandled = true;
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ jmeX = getJmeX(event.getX(pointerIndex));
+ jmeY = invertY(getJmeY(event.getY(pointerIndex)));
+ touch = getFreeTouchEvent();
+ touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ lastPositions.remove(pointerId);
+
+ addEvent(touch);
+ addEvent(generateMouseEvent(touch));
+
+ bWasHandled = true;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Convert all pointers into events
+ for (int p = 0; p < event.getPointerCount(); p++) {
+ jmeX = getJmeX(event.getX(p));
+ jmeY = invertY(getJmeY(event.getY(p)));
+ lastPos = lastPositions.get(event.getPointerId(p));
+ if (lastPos == null) {
+ lastPos = new Vector2f(jmeX, jmeY);
+ lastPositions.put(event.getPointerId(p), lastPos);
+ }
+
+ float dX = jmeX - lastPos.x;
+ float dY = jmeY - lastPos.y;
+ if (dX != 0 || dY != 0) {
+ touch = getFreeTouchEvent();
+ touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY);
+ touch.setPointerId(event.getPointerId(p));
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(p));
+ lastPos.set(jmeX, jmeY);
+
+ addEvent(touch);
+ addEvent(generateMouseEvent(touch));
+
+ bWasHandled = true;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_OUTSIDE:
+ break;
+
+ }
+
+ // Try to detect gestures
+ if (gestureDetector != null) {
+ gestureDetector.onTouchEvent(event);
+ }
+ if (scaleDetector != null) {
+ scaleDetector.onTouchEvent(event);
+ }
+
+ return bWasHandled;
+ }
+
+ // TODO: Ring Buffer for mouse events?
+ public InputEvent generateMouseEvent(TouchEvent event) {
+ InputEvent inputEvent = null;
+ int newX;
+ int newY;
+ int newDX;
+ int newDY;
+
+ // MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
+ if (!isSimulateMouse() || numPointers > 1) {
+ return null;
+ }
+
+
+ if (isMouseEventsInvertX()) {
+ newX = (int) (invertX(event.getX()));
+ newDX = (int)event.getDeltaX() * -1;
+ } else {
+ newX = (int) event.getX();
+ newDX = (int)event.getDeltaX();
+ }
+
+ if (isMouseEventsInvertY()) {
+ newY = (int) (invertY(event.getY()));
+ newDY = (int)event.getDeltaY() * -1;
+ } else {
+ newY = (int) event.getY();
+ newDY = (int)event.getDeltaY();
+ }
+
+ switch (event.getType()) {
+ case DOWN:
+ // Handle mouse down event
+ inputEvent = new MouseButtonEvent(0, true, newX, newY);
+ inputEvent.setTime(event.getTime());
+ break;
+
+ case UP:
+ // Handle mouse up event
+ inputEvent = new MouseButtonEvent(0, false, newX, newY);
+ inputEvent.setTime(event.getTime());
+ break;
+
+ case HOVER_MOVE:
+ case MOVE:
+ inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan());
+ inputEvent.setTime(event.getTime());
+ break;
+ }
+
+ return inputEvent;
+ }
+
+
+ public boolean onKey(KeyEvent event) {
+ if (!isInitialized()) {
+ return false;
+ }
+
+ TouchEvent evt;
+ // TODO: get touch event from pool
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ evt = new TouchEvent();
+ evt.set(TouchEvent.Type.KEY_DOWN);
+ evt.setKeyCode(event.getKeyCode());
+ evt.setCharacters(event.getCharacters());
+ evt.setTime(event.getEventTime());
+
+ // Send the event
+ addEvent(evt);
+
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ evt = new TouchEvent();
+ evt.set(TouchEvent.Type.KEY_UP);
+ evt.setKeyCode(event.getKeyCode());
+ evt.setCharacters(event.getCharacters());
+ evt.setTime(event.getEventTime());
+
+ // Send the event
+ addEvent(evt);
+
+ }
+
+ if (isSimulateKeyboard()) {
+ KeyInputEvent kie;
+ char unicodeChar = (char)event.getUnicodeChar();
+ int jmeKeyCode = AndroidKeyMapping.getJmeKey(event.getKeyCode());
+
+ boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
+ boolean repeating = pressed && event.getRepeatCount() > 0;
+
+ kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating);
+ kie.setTime(event.getEventTime());
+ addEvent(kie);
+// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
+// new Object[]{event.getKeyCode(), jmeKeyCode, pressed, repeating});
+// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
+ }
+
+ // consume all keys ourself except Volume Up/Down and Menu
+ // Don't do Menu so that typical Android Menus can be created and used
+ // by the user in MainActivity
+ if ((event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) ||
+ (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) ||
+ (event.getKeyCode() == KeyEvent.KEYCODE_MENU)) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+
+
+
+ // -----------------------------------------
+ // JME3 Input interface
+ @Override
+ public void initialize() {
+ touchEventPool.initialize();
+
+ initialized = true;
+ }
+
+ @Override
+ public void destroy() {
+ initialized = false;
+
+ touchEventPool.destroy();
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ @Override
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+
+ @Override
+ public void update() {
+ if (listener != null) {
+ InputEvent inputEvent;
+
+ while ((inputEvent = inputEventQueue.poll()) != null) {
+ if (inputEvent instanceof TouchEvent) {
+ listener.onTouchEvent((TouchEvent)inputEvent);
+ } else if (inputEvent instanceof MouseButtonEvent) {
+ listener.onMouseButtonEvent((MouseButtonEvent)inputEvent);
+ } else if (inputEvent instanceof MouseMotionEvent) {
+ listener.onMouseMotionEvent((MouseMotionEvent)inputEvent);
+ } else if (inputEvent instanceof KeyInputEvent) {
+ listener.onKeyEvent((KeyInputEvent)inputEvent);
+ }
+ }
+ }
+ }
+
+ // -----------------------------------------
+
+ public TouchEvent getFreeTouchEvent() {
+ return touchEventPool.getNextFreeEvent();
+ }
+
+ public void addEvent(InputEvent event) {
+ if (event == null) {
+ return;
+ }
+
+ logger.log(Level.INFO, "event: {0}", event);
+
+ inputEventQueue.add(event);
+ if (event instanceof TouchEvent) {
+ touchEventPool.storeEvent((TouchEvent)event);
+ }
+
+ }
+
+ @Override
+ public void setSimulateMouse(boolean simulate) {
+ this.mouseEventsEnabled = simulate;
+ }
+
+ @Override
+ public boolean isSimulateMouse() {
+ return mouseEventsEnabled;
+ }
+
+ public boolean isMouseEventsInvertX() {
+ return mouseEventsInvertX;
+ }
+
+ public boolean isMouseEventsInvertY() {
+ return mouseEventsInvertY;
+ }
+
+ @Override
+ public void setSimulateKeyboard(boolean simulate) {
+ this.keyboardEventsEnabled = simulate;
+ }
+
+ @Override
+ public boolean isSimulateKeyboard() {
+ return keyboardEventsEnabled;
+ }
+
+ @Override
+ public void setOmitHistoricEvents(boolean dontSendHistory) {
+ this.dontSendHistory = dontSendHistory;
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchHandler14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput14.java
similarity index 67%
rename from jme3-android/src/main/java/com/jme3/input/android/AndroidTouchHandler14.java
rename to jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput14.java
index 1a785a5e9..617a4b719 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchHandler14.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput14.java
@@ -33,7 +33,6 @@
package com.jme3.input.android;
import android.view.MotionEvent;
-import android.view.View;
import com.jme3.input.event.TouchEvent;
import com.jme3.math.Vector2f;
import java.util.HashMap;
@@ -41,36 +40,20 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * AndroidTouchHandler14 is an extension of AndroidTouchHander that adds the
- * Android touch event functionality between Android rev 9 (Android 2.3) and
- * Android rev 14 (Android 4.0).
- *
+ * AndroidTouchHandler14 extends AndroidTouchHandler to process the onHover
+ * events added in Android rev 14 (Android 4.0).
+ *
* @author iwgeric
*/
-public class AndroidTouchHandler14 extends AndroidTouchHandler implements
- View.OnHoverListener {
- private static final Logger logger = Logger.getLogger(AndroidTouchHandler14.class.getName());
+public class AndroidTouchInput14 extends AndroidTouchInput {
+ private static final Logger logger = Logger.getLogger(AndroidTouchInput14.class.getName());
final private HashMap lastHoverPositions = new HashMap();
-
- public AndroidTouchHandler14(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) {
- super(androidInput, gestureHandler);
- }
- @Override
- public void setView(View view) {
- if (view != null) {
- view.setOnHoverListener(this);
- } else {
- androidInput.getView().setOnHoverListener(null);
- }
- super.setView(view);
+ public AndroidTouchInput14(AndroidInputHandler androidInput) {
+ super(androidInput);
}
-
- public boolean onHover(View view, MotionEvent event) {
- if (view == null || view != androidInput.getView()) {
- return false;
- }
-
+
+ public boolean onHover(MotionEvent event) {
boolean consumed = false;
int action = getAction(event);
int pointerId = getPointerId(event);
@@ -78,34 +61,34 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
Vector2f lastPos = lastHoverPositions.get(pointerId);
float jmeX;
float jmeY;
-
+
numPointers = event.getPointerCount();
-
- logger.log(Level.INFO, "onHover pointerId: {0}, action: {1}, x: {2}, y: {3}, numPointers: {4}",
- new Object[]{pointerId, action, event.getX(), event.getY(), event.getPointerCount()});
+
+// logger.log(Level.INFO, "onHover pointerId: {0}, action: {1}, x: {2}, y: {3}, numPointers: {4}",
+// new Object[]{pointerId, action, event.getX(), event.getY(), event.getPointerCount()});
TouchEvent touchEvent;
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
- jmeX = androidInput.getJmeX(event.getX(pointerIndex));
- jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
- touchEvent = androidInput.getFreeTouchEvent();
+ jmeX = getJmeX(event.getX(pointerIndex));
+ jmeY = invertY(getJmeY(event.getY(pointerIndex)));
+ touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_START, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(pointerId);
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(pointerIndex));
-
+
lastPos = new Vector2f(jmeX, jmeY);
lastHoverPositions.put(pointerId, lastPos);
-
- processEvent(touchEvent);
+
+ addEvent(touchEvent);
consumed = true;
break;
case MotionEvent.ACTION_HOVER_MOVE:
// Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) {
- jmeX = androidInput.getJmeX(event.getX(p));
- jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p)));
+ jmeX = getJmeX(event.getX(p));
+ jmeY = invertY(getJmeY(event.getY(p)));
lastPos = lastHoverPositions.get(event.getPointerId(p));
if (lastPos == null) {
lastPos = new Vector2f(jmeX, jmeY);
@@ -115,38 +98,39 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
float dX = jmeX - lastPos.x;
float dY = jmeY - lastPos.y;
if (dX != 0 || dY != 0) {
- touchEvent = androidInput.getFreeTouchEvent();
+ touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_MOVE, jmeX, jmeY, dX, dY);
touchEvent.setPointerId(event.getPointerId(p));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(p));
lastPos.set(jmeX, jmeY);
- processEvent(touchEvent);
+ addEvent(touchEvent);
}
}
consumed = true;
break;
case MotionEvent.ACTION_HOVER_EXIT:
- jmeX = androidInput.getJmeX(event.getX(pointerIndex));
- jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
- touchEvent = androidInput.getFreeTouchEvent();
+ jmeX = getJmeX(event.getX(pointerIndex));
+ jmeY = invertY(getJmeY(event.getY(pointerIndex)));
+ touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_END, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(pointerId);
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(pointerIndex));
lastHoverPositions.remove(pointerId);
- processEvent(touchEvent);
+ addEvent(touchEvent);
consumed = true;
break;
default:
consumed = false;
break;
}
-
+
return consumed;
+
}
-
+
}
diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
index 2a7b54023..b77c4a36d 100644
--- a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
+++ b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
@@ -35,39 +35,43 @@ import android.opengl.GLES20;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GLExt;
+import com.jme3.renderer.opengl.GLFbo;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
-public class AndroidGL implements GL, GLExt {
+public class AndroidGL implements GL, GLExt, GLFbo {
+
+ public void resetStats() {
+ }
private static int getLimitBytes(ByteBuffer buffer) {
checkLimit(buffer);
return buffer.limit();
}
-
+
private static int getLimitBytes(ShortBuffer buffer) {
checkLimit(buffer);
return buffer.limit() * 2;
}
-
+
private static int getLimitBytes(IntBuffer buffer) {
checkLimit(buffer);
return buffer.limit() * 4;
}
-
+
private static int getLimitBytes(FloatBuffer buffer) {
checkLimit(buffer);
return buffer.limit() * 4;
}
-
+
private static int getLimitCount(Buffer buffer, int elementSize) {
checkLimit(buffer);
return buffer.limit() / elementSize;
}
-
+
private static void checkLimit(Buffer buffer) {
if (buffer == null) {
return;
@@ -79,7 +83,7 @@ public class AndroidGL implements GL, GLExt {
throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
}
}
-
+
public void glActiveTexture(int texture) {
GLES20.glActiveTexture(texture);
}
@@ -127,7 +131,7 @@ public class AndroidGL implements GL, GLExt {
public void glBufferSubData(int target, long offset, ByteBuffer data) {
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
}
-
+
public void glGetBufferSubData(int target, long offset, ByteBuffer data) {
throw new UnsupportedOperationException("OpenGL ES 2 does not support glGetBufferSubData");
}
diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java b/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java
deleted file mode 100644
index cef3e9f66..000000000
--- a/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java
+++ /dev/null
@@ -1,2566 +0,0 @@
-/*
- * 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.renderer.android;
-
-import android.opengl.GLES20;
-import android.os.Build;
-import com.jme3.asset.AndroidImageInfo;
-import com.jme3.light.LightList;
-import com.jme3.material.RenderState;
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.math.Vector4f;
-import com.jme3.renderer.Caps;
-import com.jme3.renderer.IDList;
-import com.jme3.renderer.RenderContext;
-import com.jme3.renderer.Renderer;
-import com.jme3.renderer.RendererException;
-import com.jme3.renderer.Statistics;
-import com.jme3.renderer.android.TextureUtil.AndroidGLImageFormat;
-import com.jme3.renderer.opengl.GLRenderer;
-import com.jme3.scene.Mesh;
-import com.jme3.scene.Mesh.Mode;
-import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.VertexBuffer.Format;
-import com.jme3.scene.VertexBuffer.Type;
-import com.jme3.scene.VertexBuffer.Usage;
-import com.jme3.shader.Attribute;
-import com.jme3.shader.Shader;
-import com.jme3.shader.Shader.ShaderSource;
-import com.jme3.shader.Shader.ShaderType;
-import com.jme3.shader.Uniform;
-import com.jme3.texture.FrameBuffer;
-import com.jme3.texture.FrameBuffer.RenderBuffer;
-import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture.WrapAxis;
-import com.jme3.util.BufferUtils;
-import com.jme3.util.ListMap;
-import com.jme3.util.NativeObjectManager;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.nio.ShortBuffer;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import jme3tools.shader.ShaderDebug;
-
-/**
- * @deprecated Should not be used anymore. Use {@link GLRenderer} instead.
- */
-@Deprecated
-public class OGLESShaderRenderer implements Renderer {
-
- private static final Logger logger = Logger.getLogger(OGLESShaderRenderer.class.getName());
- private static final boolean VALIDATE_SHADER = false;
- private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
- private final StringBuilder stringBuf = new StringBuilder(250);
- private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1);
- private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16);
- private final RenderContext context = new RenderContext();
- private final NativeObjectManager objManager = new NativeObjectManager();
- private final EnumSet caps = EnumSet.noneOf(Caps.class);
- // current state
- private Shader boundShader;
- // initalDrawBuf and initialReadBuf are not used on ES,
- // http://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindFramebuffer.xml
- //private int initialDrawBuf, initialReadBuf;
- private int glslVer;
- private int vertexTextureUnits;
- private int fragTextureUnits;
- private int vertexUniforms;
- private int fragUniforms;
- private int vertexAttribs;
-// private int maxFBOSamples;
- private final int maxFBOAttachs = 1; // Only 1 color attachment on ES
- private final int maxMRTFBOAttachs = 1; // FIXME for now, not sure if > 1 is needed for ES
- private int maxRBSize;
- private int maxTexSize;
- private int maxCubeTexSize;
- private int maxVertCount;
- private int maxTriCount;
- private boolean tdc;
- private FrameBuffer lastFb = null;
- private FrameBuffer mainFbOverride = null;
- private final Statistics statistics = new Statistics();
- private int vpX, vpY, vpW, vpH;
- private int clipX, clipY, clipW, clipH;
- //private final GL10 gl;
- private boolean powerVr = false;
- private boolean useVBO = false;
-
- public OGLESShaderRenderer() {
- }
-
- protected void updateNameBuffer() {
- int len = stringBuf.length();
-
- nameBuf.position(0);
- nameBuf.limit(len);
- for (int i = 0; i < len; i++) {
- nameBuf.put((byte) stringBuf.charAt(i));
- }
-
- nameBuf.rewind();
- }
-
- public Statistics getStatistics() {
- return statistics;
- }
-
- public EnumSet getCaps() {
- return caps;
- }
-
- private static final Pattern VERSION = Pattern.compile(".*?(\\d+)\\.(\\d+).*");
-
- public static int extractVersion(String version) {
-
- Matcher m = VERSION.matcher(version);
- if (m.matches()) {
- int major = Integer.parseInt(m.group(1));
- int minor = Integer.parseInt(m.group(2));
-
- return major * 100 + minor * 10;
- } else {
- return -1;
- }
- }
-
- public void initialize() {
- logger.log(Level.FINE, "Vendor: {0}", GLES20.glGetString(GLES20.GL_VENDOR));
- logger.log(Level.FINE, "Renderer: {0}", GLES20.glGetString(GLES20.GL_RENDERER));
- logger.log(Level.FINE, "Version: {0}", GLES20.glGetString(GLES20.GL_VERSION));
- logger.log(Level.FINE, "Shading Language Version: {0}", GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION));
-
- powerVr = GLES20.glGetString(GLES20.GL_RENDERER).contains("PowerVR");
-
-
- //workaround, always assume we support GLSL100
- //some cards just don't report this correctly
- caps.add(Caps.GLSL100);
-
- /*
- // Fix issue in TestRenderToMemory when GL_FRONT is the main
- // buffer being used.
- initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
- initialReadBuf = glGetInteger(GL_READ_BUFFER);
-
- // XXX: This has to be GL_BACK for canvas on Mac
- // Since initialDrawBuf is GL_FRONT for pbuffer, gotta
- // change this value later on ...
-// initialDrawBuf = GL_BACK;
-// initialReadBuf = GL_BACK;
- */
-
- // Check OpenGL version
- int openGlVer = extractVersion(GLES20.glGetString(GLES20.GL_VERSION));
- if (openGlVer == -1) {
- glslVer = -1;
- throw new UnsupportedOperationException("OpenGL ES 2.0+ is required for OGLESShaderRenderer!");
- }
-
- // Check shader language version
- glslVer = extractVersion(GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION));
- switch (glslVer) {
- // TODO: When new versions of OpenGL ES shader language come out,
- // update this.
- default:
- caps.add(Caps.GLSL100);
- break;
- }
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16);
- vertexTextureUnits = intBuf16.get(0);
- logger.log(Level.FINE, "VTF Units: {0}", vertexTextureUnits);
- if (vertexTextureUnits > 0) {
- caps.add(Caps.VertexTextureFetch);
- }
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16);
- fragTextureUnits = intBuf16.get(0);
- logger.log(Level.FINE, "Texture Units: {0}", fragTextureUnits);
-
- // Multiply vector count by 4 to get float count.
- GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, intBuf16);
- vertexUniforms = intBuf16.get(0) * 4;
- logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, intBuf16);
- fragUniforms = intBuf16.get(0) * 4;
- logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_VARYING_VECTORS, intBuf16);
- int varyingFloats = intBuf16.get(0) * 4;
- logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats);
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_ATTRIBS, intBuf16);
- vertexAttribs = intBuf16.get(0);
- logger.log(Level.FINE, "Vertex Attributes: {0}", vertexAttribs);
-
- GLES20.glGetIntegerv(GLES20.GL_SUBPIXEL_BITS, intBuf16);
- int subpixelBits = intBuf16.get(0);
- logger.log(Level.FINE, "Subpixel Bits: {0}", subpixelBits);
-
-// GLES10.glGetIntegerv(GLES10.GL_MAX_ELEMENTS_VERTICES, intBuf16);
-// maxVertCount = intBuf16.get(0);
-// logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
-//
-// GLES10.glGetIntegerv(GLES10.GL_MAX_ELEMENTS_INDICES, intBuf16);
-// maxTriCount = intBuf16.get(0);
-// logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, intBuf16);
- maxTexSize = intBuf16.get(0);
- logger.log(Level.FINE, "Maximum Texture Resolution: {0}", maxTexSize);
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16);
- maxCubeTexSize = intBuf16.get(0);
- logger.log(Level.FINE, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
-
- GLES20.glGetIntegerv(GLES20.GL_MAX_RENDERBUFFER_SIZE, intBuf16);
- maxRBSize = intBuf16.get(0);
- logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
-
- /*
- if (ctxCaps.GL_ARB_color_buffer_float){
- // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
- if (ctxCaps.GL_ARB_half_float_pixel){
- caps.add(Caps.FloatColorBuffer);
- }
- }
-
- if (ctxCaps.GL_ARB_depth_buffer_float){
- caps.add(Caps.FloatDepthBuffer);
- }
-
- if (ctxCaps.GL_ARB_draw_instanced)
- caps.add(Caps.MeshInstancing);
-
- if (ctxCaps.GL_ARB_texture_buffer_object)
- caps.add(Caps.TextureBuffer);
-
- if (ctxCaps.GL_ARB_texture_float){
- if (ctxCaps.GL_ARB_half_float_pixel){
- caps.add(Caps.FloatTexture);
- }
- }
-
- if (ctxCaps.GL_EXT_packed_float){
- caps.add(Caps.PackedFloatColorBuffer);
- if (ctxCaps.GL_ARB_half_float_pixel){
- // because textures are usually uploaded as RGB16F
- // need half-float pixel
- caps.add(Caps.PackedFloatTexture);
- }
- }
-
- if (ctxCaps.GL_EXT_texture_array)
- caps.add(Caps.TextureArray);
-
- if (ctxCaps.GL_EXT_texture_shared_exponent)
- caps.add(Caps.SharedExponentTexture);
-
- if (ctxCaps.GL_EXT_framebuffer_object){
- caps.add(Caps.FrameBuffer);
-
- glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
- maxRBSize = intBuf16.get(0);
- logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
-
- glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16);
- maxFBOAttachs = intBuf16.get(0);
- logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
-
- if (ctxCaps.GL_EXT_framebuffer_multisample){
- caps.add(Caps.FrameBufferMultisample);
-
- glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
- maxFBOSamples = intBuf16.get(0);
- logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
- }
-
- if (ctxCaps.GL_ARB_draw_buffers){
- caps.add(Caps.FrameBufferMRT);
- glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16);
- maxMRTFBOAttachs = intBuf16.get(0);
- logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
- }
- }
-
- if (ctxCaps.GL_ARB_multisample){
- glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16);
- boolean available = intBuf16.get(0) != 0;
- glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16);
- int samples = intBuf16.get(0);
- logger.log(Level.FINER, "Samples: {0}", samples);
- boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB);
- if (samples > 0 && available && !enabled){
- glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
- }
- }
- */
-
- String extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS);
- logger.log(Level.FINE, "GL_EXTENSIONS: {0}", extensions);
-
- // Get number of compressed formats available.
- GLES20.glGetIntegerv(GLES20.GL_NUM_COMPRESSED_TEXTURE_FORMATS, intBuf16);
- int numCompressedFormats = intBuf16.get(0);
-
- // Allocate buffer for compressed formats.
- IntBuffer compressedFormats = BufferUtils.createIntBuffer(numCompressedFormats);
- GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, compressedFormats);
-
- // Check for errors after all glGet calls.
- RendererUtil.checkGLError();
-
- // Print compressed formats.
- for (int i = 0; i < numCompressedFormats; i++) {
- logger.log(Level.FINE, "Compressed Texture Formats: {0}", compressedFormats.get(i));
- }
-
- TextureUtil.loadTextureFeatures(extensions);
-
- applyRenderState(RenderState.DEFAULT);
- GLES20.glDisable(GLES20.GL_DITHER);
- RendererUtil.checkGLError();
-
- useVBO = false;
-
- // NOTE: SDK_INT is only available since 1.6,
- // but for jME3 it doesn't matter since android versions 1.5 and below
- // are not supported.
- if (Build.VERSION.SDK_INT >= 9){
- logger.log(Level.FINE, "Force-enabling VBO (Android 2.3 or higher)");
- useVBO = true;
- } else {
- useVBO = false;
- }
-
- logger.log(Level.FINE, "Caps: {0}", caps);
- }
-
- /**
- * resetGLObjects should be called when die GLView gets recreated to reset all GPU objects
- */
- public void resetGLObjects() {
- objManager.resetObjects();
- statistics.clearMemory();
- boundShader = null;
- lastFb = null;
- context.reset();
- }
-
- public void cleanup() {
- objManager.deleteAllObjects(this);
- statistics.clearMemory();
- }
-
- private void checkCap(Caps cap) {
- if (!caps.contains(cap)) {
- throw new UnsupportedOperationException("Required capability missing: " + cap.name());
- }
- }
-
- /*********************************************************************\
- |* Render State *|
- \*********************************************************************/
- public void setDepthRange(float start, float end) {
- GLES20.glDepthRangef(start, end);
- RendererUtil.checkGLError();
- }
-
- public void clearBuffers(boolean color, boolean depth, boolean stencil) {
- int bits = 0;
- if (color) {
- //See explanations of the depth below, we must enable color write to be able to clear the color buffer
- if (context.colorWriteEnabled == false) {
- GLES20.glColorMask(true, true, true, true);
- context.colorWriteEnabled = true;
- }
- bits = GLES20.GL_COLOR_BUFFER_BIT;
- }
- if (depth) {
- //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
- //here s some link on openl board
- //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
- //if depth clear is requested, we enable the depthMask
- if (context.depthWriteEnabled == false) {
- GLES20.glDepthMask(true);
- context.depthWriteEnabled = true;
- }
- bits |= GLES20.GL_DEPTH_BUFFER_BIT;
- }
- if (stencil) {
- bits |= GLES20.GL_STENCIL_BUFFER_BIT;
- }
- if (bits != 0) {
- GLES20.glClear(bits);
- RendererUtil.checkGLError();
- }
- }
-
- public void setBackgroundColor(ColorRGBA color) {
- GLES20.glClearColor(color.r, color.g, color.b, color.a);
- RendererUtil.checkGLError();
- }
-
- public void applyRenderState(RenderState state) {
- /*
- if (state.isWireframe() && !context.wireframe){
- GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_LINE);
- context.wireframe = true;
- }else if (!state.isWireframe() && context.wireframe){
- GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_FILL);
- context.wireframe = false;
- }
- */
- if (state.isDepthTest() && !context.depthTestEnabled) {
- GLES20.glEnable(GLES20.GL_DEPTH_TEST);
- GLES20.glDepthFunc(convertTestFunction(context.depthFunc));
- RendererUtil.checkGLError();
- context.depthTestEnabled = true;
- } else if (!state.isDepthTest() && context.depthTestEnabled) {
- GLES20.glDisable(GLES20.GL_DEPTH_TEST);
- RendererUtil.checkGLError();
- context.depthTestEnabled = false;
- }
- if (state.getDepthFunc() != context.depthFunc) {
- GLES20.glDepthFunc(convertTestFunction(state.getDepthFunc()));
- context.depthFunc = state.getDepthFunc();
- }
-
- if (state.isDepthWrite() && !context.depthWriteEnabled) {
- GLES20.glDepthMask(true);
- RendererUtil.checkGLError();
- context.depthWriteEnabled = true;
- } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
- GLES20.glDepthMask(false);
- RendererUtil.checkGLError();
- context.depthWriteEnabled = false;
- }
- if (state.isColorWrite() && !context.colorWriteEnabled) {
- GLES20.glColorMask(true, true, true, true);
- RendererUtil.checkGLError();
- context.colorWriteEnabled = true;
- } else if (!state.isColorWrite() && context.colorWriteEnabled) {
- GLES20.glColorMask(false, false, false, false);
- RendererUtil.checkGLError();
- context.colorWriteEnabled = false;
- }
-// if (state.isPointSprite() && !context.pointSprite) {
-//// GLES20.glEnable(GLES20.GL_POINT_SPRITE);
-//// GLES20.glTexEnvi(GLES20.GL_POINT_SPRITE, GLES20.GL_COORD_REPLACE, GLES20.GL_TRUE);
-//// GLES20.glEnable(GLES20.GL_VERTEX_PROGRAM_POINT_SIZE);
-//// GLES20.glPointParameterf(GLES20.GL_POINT_SIZE_MIN, 1.0f);
-// } else if (!state.isPointSprite() && context.pointSprite) {
-//// GLES20.glDisable(GLES20.GL_POINT_SPRITE);
-// }
-
- if (state.isPolyOffset()) {
- if (!context.polyOffsetEnabled) {
- GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
- GLES20.glPolygonOffset(state.getPolyOffsetFactor(),
- state.getPolyOffsetUnits());
- RendererUtil.checkGLError();
-
- context.polyOffsetEnabled = true;
- context.polyOffsetFactor = state.getPolyOffsetFactor();
- context.polyOffsetUnits = state.getPolyOffsetUnits();
- } else {
- if (state.getPolyOffsetFactor() != context.polyOffsetFactor
- || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
- GLES20.glPolygonOffset(state.getPolyOffsetFactor(),
- state.getPolyOffsetUnits());
- RendererUtil.checkGLError();
-
- context.polyOffsetFactor = state.getPolyOffsetFactor();
- context.polyOffsetUnits = state.getPolyOffsetUnits();
- }
- }
- } else {
- if (context.polyOffsetEnabled) {
- GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
- RendererUtil.checkGLError();
-
- context.polyOffsetEnabled = false;
- context.polyOffsetFactor = 0;
- context.polyOffsetUnits = 0;
- }
- }
- if (state.getFaceCullMode() != context.cullMode) {
- if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
- GLES20.glDisable(GLES20.GL_CULL_FACE);
- RendererUtil.checkGLError();
- } else {
- GLES20.glEnable(GLES20.GL_CULL_FACE);
- RendererUtil.checkGLError();
- }
-
- switch (state.getFaceCullMode()) {
- case Off:
- break;
- case Back:
- GLES20.glCullFace(GLES20.GL_BACK);
- RendererUtil.checkGLError();
- break;
- case Front:
- GLES20.glCullFace(GLES20.GL_FRONT);
- RendererUtil.checkGLError();
- break;
- case FrontAndBack:
- GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
- RendererUtil.checkGLError();
- break;
- default:
- throw new UnsupportedOperationException("Unrecognized face cull mode: "
- + state.getFaceCullMode());
- }
-
- context.cullMode = state.getFaceCullMode();
- }
-
- if (state.getBlendMode() != context.blendMode) {
- if (state.getBlendMode() == RenderState.BlendMode.Off) {
- GLES20.glDisable(GLES20.GL_BLEND);
- RendererUtil.checkGLError();
- } else {
- GLES20.glEnable(GLES20.GL_BLEND);
- switch (state.getBlendMode()) {
- case Off:
- break;
- case Additive:
- GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
- break;
- case AlphaAdditive:
- GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE);
- break;
- case Color:
- GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR);
- break;
- case Alpha:
- GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
- break;
- case PremultAlpha:
- GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
- break;
- case Modulate:
- GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_ZERO);
- break;
- case ModulateX2:
- GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_SRC_COLOR);
- break;
- case Screen:
- GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR);
- break;
- case Exclusion:
- GLES20.glBlendFunc(GLES20.GL_ONE_MINUS_DST_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR);
- break;
- default:
- throw new UnsupportedOperationException("Unrecognized blend mode: "
- + state.getBlendMode());
- }
- RendererUtil.checkGLError();
- }
- context.blendMode = state.getBlendMode();
- }
- }
-
- /*********************************************************************\
- |* Camera and World transforms *|
- \*********************************************************************/
- public void setViewPort(int x, int y, int w, int h) {
- if (x != vpX || vpY != y || vpW != w || vpH != h) {
- GLES20.glViewport(x, y, w, h);
- RendererUtil.checkGLError();
-
- vpX = x;
- vpY = y;
- vpW = w;
- vpH = h;
- }
- }
-
- public void setClipRect(int x, int y, int width, int height) {
- if (!context.clipRectEnabled) {
- GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
- RendererUtil.checkGLError();
- context.clipRectEnabled = true;
- }
- if (clipX != x || clipY != y || clipW != width || clipH != height) {
- GLES20.glScissor(x, y, width, height);
- RendererUtil.checkGLError();
- clipX = x;
- clipY = y;
- clipW = width;
- clipH = height;
- }
- }
-
- public void clearClipRect() {
- if (context.clipRectEnabled) {
- GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
- RendererUtil.checkGLError();
- context.clipRectEnabled = false;
-
- clipX = 0;
- clipY = 0;
- clipW = 0;
- clipH = 0;
- }
- }
-
- public void postFrame() {
- RendererUtil.checkGLErrorForced();
-
- objManager.deleteUnused(this);
- }
-
- /*********************************************************************\
- |* Shaders *|
- \*********************************************************************/
- protected void updateUniformLocation(Shader shader, Uniform uniform) {
- stringBuf.setLength(0);
- stringBuf.append(uniform.getName()).append('\0');
- updateNameBuffer();
- int loc = GLES20.glGetUniformLocation(shader.getId(), uniform.getName());
- RendererUtil.checkGLError();
-
- if (loc < 0) {
- uniform.setLocation(-1);
- // uniform is not declared in shader
- } else {
- uniform.setLocation(loc);
- }
- }
-
- protected void bindProgram(Shader shader) {
- int shaderId = shader.getId();
- if (context.boundShaderProgram != shaderId) {
- GLES20.glUseProgram(shaderId);
- RendererUtil.checkGLError();
-
- statistics.onShaderUse(shader, true);
- boundShader = shader;
- context.boundShaderProgram = shaderId;
- } else {
- statistics.onShaderUse(shader, false);
- }
- }
-
- protected void updateUniform(Shader shader, Uniform uniform) {
- assert uniform.getName() != null;
- assert shader.getId() > 0;
-
- bindProgram(shader);
-
- int loc = uniform.getLocation();
- if (loc == -1) {
- return;
- }
-
- if (loc == -2) {
- // get uniform location
- updateUniformLocation(shader, uniform);
- if (uniform.getLocation() == -1) {
- // not declared, ignore
- uniform.clearUpdateNeeded();
- return;
- }
- loc = uniform.getLocation();
- }
-
- if (uniform.getVarType() == null) {
- // removed logging the warning to avoid flooding the log
- // (LWJGL also doesn't post a warning)
- //logger.log(Level.FINEST, "Uniform value is not set yet. Shader: {0}, Uniform: {1}",
- // new Object[]{shader.toString(), uniform.toString()});
- return; // value not set yet..
- }
-
- statistics.onUniformSet();
-
- uniform.clearUpdateNeeded();
- FloatBuffer fb;
- IntBuffer ib;
- switch (uniform.getVarType()) {
- case Float:
- Float f = (Float) uniform.getValue();
- GLES20.glUniform1f(loc, f.floatValue());
- break;
- case Vector2:
- Vector2f v2 = (Vector2f) uniform.getValue();
- GLES20.glUniform2f(loc, v2.getX(), v2.getY());
- break;
- case Vector3:
- Vector3f v3 = (Vector3f) uniform.getValue();
- GLES20.glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
- break;
- case Vector4:
- Object val = uniform.getValue();
- if (val instanceof ColorRGBA) {
- ColorRGBA c = (ColorRGBA) val;
- GLES20.glUniform4f(loc, c.r, c.g, c.b, c.a);
- } else if (val instanceof Vector4f) {
- Vector4f c = (Vector4f) val;
- GLES20.glUniform4f(loc, c.x, c.y, c.z, c.w);
- } else {
- Quaternion c = (Quaternion) uniform.getValue();
- GLES20.glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
- }
- break;
- case Boolean:
- Boolean b = (Boolean) uniform.getValue();
- GLES20.glUniform1i(loc, b.booleanValue() ? GLES20.GL_TRUE : GLES20.GL_FALSE);
- break;
- case Matrix3:
- fb = (FloatBuffer) uniform.getValue();
- assert fb.remaining() == 9;
- GLES20.glUniformMatrix3fv(loc, 1, false, fb);
- break;
- case Matrix4:
- fb = (FloatBuffer) uniform.getValue();
- assert fb.remaining() == 16;
- GLES20.glUniformMatrix4fv(loc, 1, false, fb);
- break;
- case IntArray:
- ib = (IntBuffer) uniform.getValue();
- GLES20.glUniform1iv(loc, ib.limit(), ib);
- break;
- case FloatArray:
- fb = (FloatBuffer) uniform.getValue();
- GLES20.glUniform1fv(loc, fb.limit(), fb);
- break;
- case Vector2Array:
- fb = (FloatBuffer) uniform.getValue();
- GLES20.glUniform2fv(loc, fb.limit() / 2, fb);
- break;
- case Vector3Array:
- fb = (FloatBuffer) uniform.getValue();
- GLES20.glUniform3fv(loc, fb.limit() / 3, fb);
- break;
- case Vector4Array:
- fb = (FloatBuffer) uniform.getValue();
- GLES20.glUniform4fv(loc, fb.limit() / 4, fb);
- break;
- case Matrix4Array:
- fb = (FloatBuffer) uniform.getValue();
- GLES20.glUniformMatrix4fv(loc, fb.limit() / 16, false, fb);
- break;
- case Int:
- Integer i = (Integer) uniform.getValue();
- GLES20.glUniform1i(loc, i.intValue());
- break;
- default:
- throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
- }
- RendererUtil.checkGLError();
- }
-
- protected void updateShaderUniforms(Shader shader) {
- ListMap uniforms = shader.getUniformMap();
- for (int i = 0; i < uniforms.size(); i++) {
- Uniform uniform = uniforms.getValue(i);
- if (uniform.isUpdateNeeded()) {
- updateUniform(shader, uniform);
- }
- }
- }
-
- protected void resetUniformLocations(Shader shader) {
- ListMap uniforms = shader.getUniformMap();
- for (int i = 0; i < uniforms.size(); i++) {
- Uniform uniform = uniforms.getValue(i);
- uniform.reset(); // e.g check location again
- }
- }
-
- /*
- * (Non-javadoc)
- * Only used for fixed-function. Ignored.
- */
- public void setLighting(LightList list) {
- }
-
- public int convertShaderType(ShaderType type) {
- switch (type) {
- case Fragment:
- return GLES20.GL_FRAGMENT_SHADER;
- case Vertex:
- return GLES20.GL_VERTEX_SHADER;
-// case Geometry:
-// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB;
- default:
- throw new RuntimeException("Unrecognized shader type.");
- }
- }
-
- public void updateShaderSourceData(ShaderSource source) {
- int id = source.getId();
- if (id == -1) {
- // Create id
- id = GLES20.glCreateShader(convertShaderType(source.getType()));
- RendererUtil.checkGLError();
-
- if (id <= 0) {
- throw new RendererException("Invalid ID received when trying to create shader.");
- }
- source.setId(id);
- }
-
- if (!source.getLanguage().equals("GLSL100")) {
- throw new RendererException("This shader cannot run in OpenGL ES. "
- + "Only GLSL 1.0 shaders are supported.");
- }
-
- // upload shader source
- // merge the defines and source code
- byte[] definesCodeData = source.getDefines().getBytes();
- byte[] sourceCodeData = source.getSource().getBytes();
- ByteBuffer codeBuf = BufferUtils.createByteBuffer(definesCodeData.length
- + sourceCodeData.length);
- codeBuf.put(definesCodeData);
- codeBuf.put(sourceCodeData);
- codeBuf.flip();
-
- if (powerVr && source.getType() == ShaderType.Vertex) {
- // XXX: This is to fix a bug in old PowerVR, remove
- // when no longer applicable.
- GLES20.glShaderSource(
- id, source.getDefines()
- + source.getSource());
- } else {
- String precision ="";
- if (source.getType() == ShaderType.Fragment) {
- precision = "precision mediump float;\n";
- }
- GLES20.glShaderSource(
- id,
- precision
- +source.getDefines()
- + source.getSource());
- }
-// int range[] = new int[2];
-// int precision[] = new int[1];
-// GLES20.glGetShaderPrecisionFormat(GLES20.GL_VERTEX_SHADER, GLES20.GL_HIGH_FLOAT, range, 0, precision, 0);
-// System.out.println("PRECISION HIGH FLOAT VERTEX");
-// System.out.println("range "+range[0]+"," +range[1]);
-// System.out.println("precision "+precision[0]);
-
- GLES20.glCompileShader(id);
- RendererUtil.checkGLError();
-
- GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, intBuf1);
- RendererUtil.checkGLError();
-
- boolean compiledOK = intBuf1.get(0) == GLES20.GL_TRUE;
- String infoLog = null;
-
- if (VALIDATE_SHADER || !compiledOK) {
- // even if compile succeeded, check
- // log for warnings
- GLES20.glGetShaderiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1);
- RendererUtil.checkGLError();
- infoLog = GLES20.glGetShaderInfoLog(id);
- }
-
- if (compiledOK) {
- if (infoLog != null) {
- logger.log(Level.FINE, "compile success: {0}, {1}", new Object[]{source.getName(), infoLog});
- } else {
- logger.log(Level.FINE, "compile success: {0}", source.getName());
- }
- source.clearUpdateNeeded();
- } else {
- logger.log(Level.WARNING, "Bad compile of:\n{0}",
- new Object[]{ShaderDebug.formatShaderSource(stringBuf.toString() + source.getDefines() + source.getSource())});
- if (infoLog != null) {
- throw new RendererException("compile error in: " + source + "\n" + infoLog);
- } else {
- throw new RendererException("compile error in: " + source + "\nerror: ");
- }
- }
- }
-
- public void updateShaderData(Shader shader) {
- int id = shader.getId();
- boolean needRegister = false;
- if (id == -1) {
- // create program
- id = GLES20.glCreateProgram();
- RendererUtil.checkGLError();
-
- if (id <= 0) {
- throw new RendererException("Invalid ID received when trying to create shader program.");
- }
-
- shader.setId(id);
- needRegister = true;
- }
-
- for (ShaderSource source : shader.getSources()) {
- if (source.isUpdateNeeded()) {
- updateShaderSourceData(source);
- }
-
- GLES20.glAttachShader(id, source.getId());
- RendererUtil.checkGLError();
- }
-
- // link shaders to program
- GLES20.glLinkProgram(id);
- RendererUtil.checkGLError();
-
- GLES20.glGetProgramiv(id, GLES20.GL_LINK_STATUS, intBuf1);
- RendererUtil.checkGLError();
-
- boolean linkOK = intBuf1.get(0) == GLES20.GL_TRUE;
- String infoLog = null;
-
- if (VALIDATE_SHADER || !linkOK) {
- GLES20.glGetProgramiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1);
- RendererUtil.checkGLError();
-
- int length = intBuf1.get(0);
- if (length > 3) {
- // get infos
- infoLog = GLES20.glGetProgramInfoLog(id);
- RendererUtil.checkGLError();
- }
- }
-
- if (linkOK) {
- if (infoLog != null) {
- logger.log(Level.FINE, "shader link success. \n{0}", infoLog);
- } else {
- logger.fine("shader link success");
- }
- shader.clearUpdateNeeded();
- if (needRegister) {
- // Register shader for clean up if it was created in this method.
- objManager.registerObject(shader);
- statistics.onNewShader();
- } else {
- // OpenGL spec: uniform locations may change after re-link
- resetUniformLocations(shader);
- }
- } else {
- if (infoLog != null) {
- throw new RendererException("Shader link failure, shader: " + shader + "\n" + infoLog);
- } else {
- throw new RendererException("Shader link failure, shader: " + shader + "\ninfo: ");
- }
- }
- }
-
- public void setShader(Shader shader) {
- if (shader == null) {
- throw new IllegalArgumentException("Shader cannot be null");
- } else {
- if (shader.isUpdateNeeded()) {
- updateShaderData(shader);
- }
-
- // NOTE: might want to check if any of the
- // sources need an update?
-
- assert shader.getId() > 0;
-
- updateShaderUniforms(shader);
- bindProgram(shader);
- }
- }
-
- public void deleteShaderSource(ShaderSource source) {
- if (source.getId() < 0) {
- logger.warning("Shader source is not uploaded to GPU, cannot delete.");
- return;
- }
-
- source.clearUpdateNeeded();
-
- GLES20.glDeleteShader(source.getId());
- RendererUtil.checkGLError();
-
- source.resetObject();
- }
-
- public void deleteShader(Shader shader) {
- if (shader.getId() == -1) {
- logger.warning("Shader is not uploaded to GPU, cannot delete.");
- return;
- }
-
- for (ShaderSource source : shader.getSources()) {
- if (source.getId() != -1) {
- GLES20.glDetachShader(shader.getId(), source.getId());
- RendererUtil.checkGLError();
-
- deleteShaderSource(source);
- }
- }
-
- GLES20.glDeleteProgram(shader.getId());
- RendererUtil.checkGLError();
-
- statistics.onDeleteShader();
- shader.resetObject();
- }
-
- private int convertTestFunction(RenderState.TestFunction testFunc) {
- switch (testFunc) {
- case Never:
- return GLES20.GL_NEVER;
- case Less:
- return GLES20.GL_LESS;
- case LessOrEqual:
- return GLES20.GL_LEQUAL;
- case Greater:
- return GLES20.GL_GREATER;
- case GreaterOrEqual:
- return GLES20.GL_GEQUAL;
- case Equal:
- return GLES20.GL_EQUAL;
- case NotEqual:
- return GLES20.GL_NOTEQUAL;
- case Always:
- return GLES20.GL_ALWAYS;
- default:
- throw new UnsupportedOperationException("Unrecognized test function: " + testFunc);
- }
- }
-
- /*********************************************************************\
- |* Framebuffers *|
- \*********************************************************************/
-
- public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
- throw new RendererException("Copy framebuffer not implemented yet.");
-
-// if (GLContext.getCapabilities().GL_EXT_framebuffer_blit) {
-// int srcX0 = 0;
-// int srcY0 = 0;
-// int srcX1 = 0;
-// int srcY1 = 0;
-//
-// int dstX0 = 0;
-// int dstY0 = 0;
-// int dstX1 = 0;
-// int dstY1 = 0;
-//
-// int prevFBO = context.boundFBO;
-//
-// if (mainFbOverride != null) {
-// if (src == null) {
-// src = mainFbOverride;
-// }
-// if (dst == null) {
-// dst = mainFbOverride;
-// }
-// }
-//
-// if (src != null && src.isUpdateNeeded()) {
-// updateFrameBuffer(src);
-// }
-//
-// if (dst != null && dst.isUpdateNeeded()) {
-// updateFrameBuffer(dst);
-// }
-//
-// if (src == null) {
-// GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
-// srcX0 = vpX;
-// srcY0 = vpY;
-// srcX1 = vpX + vpW;
-// srcY1 = vpY + vpH;
-// } else {
-// GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, src.getId());
-// srcX1 = src.getWidth();
-// srcY1 = src.getHeight();
-// }
-// if (dst == null) {
-// GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
-// dstX0 = vpX;
-// dstY0 = vpY;
-// dstX1 = vpX + vpW;
-// dstY1 = vpY + vpH;
-// } else {
-// GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, dst.getId());
-// dstX1 = dst.getWidth();
-// dstY1 = dst.getHeight();
-// }
-//
-//
-// int mask = GL_COLOR_BUFFER_BIT;
-// if (copyDepth) {
-// mask |= GL_DEPTH_BUFFER_BIT;
-// }
-// GLES20.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
-// dstX0, dstY0, dstX1, dstY1, mask,
-// GL_NEAREST);
-//
-//
-// GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, prevFBO);
-// try {
-// checkFrameBufferError();
-// } catch (IllegalStateException ex) {
-// logger.log(Level.SEVERE, "Source FBO:\n{0}", src);
-// logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst);
-// throw ex;
-// }
-// } else {
-// throw new RendererException("EXT_framebuffer_blit required.");
-// // TODO: support non-blit copies?
-// }
- }
-
- private void checkFrameBufferStatus(FrameBuffer fb) {
- try {
- checkFrameBufferError();
- } catch (IllegalStateException ex) {
- logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb);
- printRealFrameBufferInfo(fb);
- throw ex;
- }
- }
-
- private void checkFrameBufferError() {
- int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
- switch (status) {
- case GLES20.GL_FRAMEBUFFER_COMPLETE:
- break;
- case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
- //Choose different formats
- throw new IllegalStateException("Framebuffer object format is "
- + "unsupported by the video hardware.");
- case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- throw new IllegalStateException("Framebuffer has erronous attachment.");
- case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached.");
- case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- throw new IllegalStateException("Framebuffer attachments must have same dimensions.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
-// throw new IllegalStateException("Framebuffer attachments must have same formats.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
-// throw new IllegalStateException("Incomplete draw buffer.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
-// throw new IllegalStateException("Incomplete read buffer.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
-// throw new IllegalStateException("Incomplete multisample buffer.");
- default:
- //Programming error; will fail on all hardware
- throw new IllegalStateException("Some video driver error "
- + "or programming error occured. "
- + "Framebuffer object status is invalid: " + status);
- }
- }
-
- private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name) {
- System.out.println("== Renderbuffer " + name + " ==");
- System.out.println("RB ID: " + rb.getId());
- System.out.println("Is proper? " + GLES20.glIsRenderbuffer(rb.getId()));
-
- int attachment = convertAttachmentSlot(rb.getSlot());
-
- intBuf16.clear();
- GLES20.glGetFramebufferAttachmentParameteriv(GLES20.GL_FRAMEBUFFER,
- attachment, GLES20.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, intBuf16);
- int type = intBuf16.get(0);
-
- intBuf16.clear();
- GLES20.glGetFramebufferAttachmentParameteriv(GLES20.GL_FRAMEBUFFER,
- attachment, GLES20.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, intBuf16);
- int rbName = intBuf16.get(0);
-
- switch (type) {
- case GLES20.GL_NONE:
- System.out.println("Type: None");
- break;
- case GLES20.GL_TEXTURE:
- System.out.println("Type: Texture");
- break;
- case GLES20.GL_RENDERBUFFER:
- System.out.println("Type: Buffer");
- System.out.println("RB ID: " + rbName);
- break;
- }
-
-
-
- }
-
- private void printRealFrameBufferInfo(FrameBuffer fb) {
-// boolean doubleBuffer = GLES20.glGetBooleanv(GLES20.GL_DOUBLEBUFFER);
- boolean doubleBuffer = false; // FIXME
-// String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER));
-// String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER));
-
- int fbId = fb.getId();
- intBuf16.clear();
-// int curDrawBinding = GLES20.glGetIntegerv(GLES20.GL_DRAW_FRAMEBUFFER_BINDING);
-// int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING);
-
- System.out.println("=== OpenGL FBO State ===");
- System.out.println("Context doublebuffered? " + doubleBuffer);
- System.out.println("FBO ID: " + fbId);
- System.out.println("Is proper? " + GLES20.glIsFramebuffer(fbId));
-// System.out.println("Is bound to draw? " + (fbId == curDrawBinding));
-// System.out.println("Is bound to read? " + (fbId == curReadBinding));
-// System.out.println("Draw buffer: " + drawBuf);
-// System.out.println("Read buffer: " + readBuf);
-
- if (context.boundFBO != fbId) {
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbId);
- context.boundFBO = fbId;
- }
-
- if (fb.getDepthBuffer() != null) {
- printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth");
- }
- for (int i = 0; i < fb.getNumColorBuffers(); i++) {
- printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i);
- }
- }
-
- private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
- int id = rb.getId();
- if (id == -1) {
- GLES20.glGenRenderbuffers(1, intBuf1);
- RendererUtil.checkGLError();
-
- id = intBuf1.get(0);
- rb.setId(id);
- }
-
- if (context.boundRB != id) {
- GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, id);
- RendererUtil.checkGLError();
-
- context.boundRB = id;
- }
-
- if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) {
- throw new RendererException("Resolution " + fb.getWidth()
- + ":" + fb.getHeight() + " is not supported.");
- }
-
- AndroidGLImageFormat imageFormat = TextureUtil.getImageFormat(rb.getFormat(), true);
- if (imageFormat.renderBufferStorageFormat == 0) {
- throw new RendererException("The format '" + rb.getFormat() + "' cannot be used for renderbuffers.");
- }
-
-// if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) {
- if (fb.getSamples() > 1) {
-// // FIXME
- throw new RendererException("Multisample FrameBuffer is not supported yet.");
-// int samples = fb.getSamples();
-// if (maxFBOSamples < samples) {
-// samples = maxFBOSamples;
-// }
-// glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
-// samples,
-// glFmt.internalFormat,
-// fb.getWidth(),
-// fb.getHeight());
- } else {
- GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
- imageFormat.renderBufferStorageFormat,
- fb.getWidth(),
- fb.getHeight());
-
- RendererUtil.checkGLError();
- }
- }
-
- private int convertAttachmentSlot(int attachmentSlot) {
- // can also add support for stencil here
- if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
- return GLES20.GL_DEPTH_ATTACHMENT;
-// if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
-// return GLES30.GL_DEPTH_STENCIL_ATTACHMENT;
- } else if (attachmentSlot == 0) {
- return GLES20.GL_COLOR_ATTACHMENT0;
- } else {
- throw new UnsupportedOperationException("Android does not support multiple color attachments to an FBO");
- }
- }
-
- public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
- Texture tex = rb.getTexture();
- Image image = tex.getImage();
- if (image.isUpdateNeeded()) {
- updateTexImageData(image, tex.getType());
-
- // NOTE: For depth textures, sets nearest/no-mips mode
- // Required to fix "framebuffer unsupported"
- // for old NVIDIA drivers!
- setupTextureParams(tex);
- }
-
- GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
- convertAttachmentSlot(rb.getSlot()),
- convertTextureType(tex.getType()),
- image.getId(),
- 0);
-
- RendererUtil.checkGLError();
- }
-
- public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
- boolean needAttach;
- if (rb.getTexture() == null) {
- // if it hasn't been created yet, then attach is required.
- needAttach = rb.getId() == -1;
- updateRenderBuffer(fb, rb);
- } else {
- needAttach = false;
- updateRenderTexture(fb, rb);
- }
- if (needAttach) {
- GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
- convertAttachmentSlot(rb.getSlot()),
- GLES20.GL_RENDERBUFFER,
- rb.getId());
-
- RendererUtil.checkGLError();
- }
- }
-
- public void updateFrameBuffer(FrameBuffer fb) {
- int id = fb.getId();
- if (id == -1) {
- intBuf1.clear();
- // create FBO
- GLES20.glGenFramebuffers(1, intBuf1);
- RendererUtil.checkGLError();
-
- id = intBuf1.get(0);
- fb.setId(id);
- objManager.registerObject(fb);
-
- statistics.onNewFrameBuffer();
- }
-
- if (context.boundFBO != id) {
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, id);
- RendererUtil.checkGLError();
-
- // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
- context.boundDrawBuf = 0;
- context.boundFBO = id;
- }
-
- FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
- if (depthBuf != null) {
- updateFrameBufferAttachment(fb, depthBuf);
- }
-
- for (int i = 0; i < fb.getNumColorBuffers(); i++) {
- FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
- updateFrameBufferAttachment(fb, colorBuf);
- }
-
- fb.clearUpdateNeeded();
- }
-
- public void setMainFrameBufferOverride(FrameBuffer fb){
- mainFbOverride = fb;
- }
-
- public void setFrameBuffer(FrameBuffer fb) {
- if (fb == null && mainFbOverride != null) {
- fb = mainFbOverride;
- }
-
- if (lastFb == fb) {
- if (fb == null || !fb.isUpdateNeeded()) {
- return;
- }
- }
-
- // generate mipmaps for last FB if needed
- if (lastFb != null) {
- for (int i = 0; i < lastFb.getNumColorBuffers(); i++) {
- RenderBuffer rb = lastFb.getColorBuffer(i);
- Texture tex = rb.getTexture();
- if (tex != null
- && tex.getMinFilter().usesMipMapLevels()) {
- setTexture(0, rb.getTexture());
-
-// int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace());
- int textureType = convertTextureType(tex.getType());
- GLES20.glGenerateMipmap(textureType);
- RendererUtil.checkGLError();
- }
- }
- }
-
- if (fb == null) {
- // unbind any fbos
- if (context.boundFBO != 0) {
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
- RendererUtil.checkGLError();
-
- statistics.onFrameBufferUse(null, true);
-
- context.boundFBO = 0;
- }
-
- /*
- // select back buffer
- if (context.boundDrawBuf != -1) {
- glDrawBuffer(initialDrawBuf);
- context.boundDrawBuf = -1;
- }
- if (context.boundReadBuf != -1) {
- glReadBuffer(initialReadBuf);
- context.boundReadBuf = -1;
- }
- */
-
- lastFb = null;
- } else {
- if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
- throw new IllegalArgumentException("The framebuffer: " + fb
- + "\nDoesn't have any color/depth buffers");
- }
-
- if (fb.isUpdateNeeded()) {
- updateFrameBuffer(fb);
- }
-
- if (context.boundFBO != fb.getId()) {
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb.getId());
- RendererUtil.checkGLError();
-
- statistics.onFrameBufferUse(fb, true);
-
- // update viewport to reflect framebuffer's resolution
- setViewPort(0, 0, fb.getWidth(), fb.getHeight());
-
- context.boundFBO = fb.getId();
- } else {
- statistics.onFrameBufferUse(fb, false);
- }
- if (fb.getNumColorBuffers() == 0) {
-// // make sure to select NONE as draw buf
-// // no color buffer attached. select NONE
- if (context.boundDrawBuf != -2) {
-// glDrawBuffer(GL_NONE);
- context.boundDrawBuf = -2;
- }
- if (context.boundReadBuf != -2) {
-// glReadBuffer(GL_NONE);
- context.boundReadBuf = -2;
- }
- } else {
- if (fb.getNumColorBuffers() > maxFBOAttachs) {
- throw new RendererException("Framebuffer has more color "
- + "attachments than are supported"
- + " by the video hardware!");
- }
- if (fb.isMultiTarget()) {
- if (fb.getNumColorBuffers() > maxMRTFBOAttachs) {
- throw new RendererException("Framebuffer has more"
- + " multi targets than are supported"
- + " by the video hardware!");
- }
-
- if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
- intBuf16.clear();
- for (int i = 0; i < fb.getNumColorBuffers(); i++) {
- intBuf16.put(GLES20.GL_COLOR_ATTACHMENT0 + i);
- }
-
- intBuf16.flip();
-// glDrawBuffers(intBuf16);
- context.boundDrawBuf = 100 + fb.getNumColorBuffers();
- }
- } else {
- RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
- // select this draw buffer
- if (context.boundDrawBuf != rb.getSlot()) {
- GLES20.glActiveTexture(convertAttachmentSlot(rb.getSlot()));
- RendererUtil.checkGLError();
-
- context.boundDrawBuf = rb.getSlot();
- }
- }
- }
-
- assert fb.getId() >= 0;
- assert context.boundFBO == fb.getId();
-
- lastFb = fb;
-
- checkFrameBufferStatus(fb);
- }
- }
-
- /**
- * Reads the Color Buffer from OpenGL and stores into the ByteBuffer.
- * Make sure to call setViewPort with the appropriate viewport size before
- * calling readFrameBuffer.
- * @param fb FrameBuffer
- * @param byteBuf ByteBuffer to store the Color Buffer from OpenGL
- */
- public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
- if (fb != null) {
- RenderBuffer rb = fb.getColorBuffer();
- if (rb == null) {
- throw new IllegalArgumentException("Specified framebuffer"
- + " does not have a colorbuffer");
- }
-
- setFrameBuffer(fb);
- } else {
- setFrameBuffer(null);
- }
-
- GLES20.glReadPixels(vpX, vpY, vpW, vpH, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuf);
- RendererUtil.checkGLError();
- }
-
- private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
- intBuf1.put(0, rb.getId());
- GLES20.glDeleteRenderbuffers(1, intBuf1);
- RendererUtil.checkGLError();
- }
-
- public void deleteFrameBuffer(FrameBuffer fb) {
- if (fb.getId() != -1) {
- if (context.boundFBO == fb.getId()) {
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
- RendererUtil.checkGLError();
-
- context.boundFBO = 0;
- }
-
- if (fb.getDepthBuffer() != null) {
- deleteRenderBuffer(fb, fb.getDepthBuffer());
- }
- if (fb.getColorBuffer() != null) {
- deleteRenderBuffer(fb, fb.getColorBuffer());
- }
-
- intBuf1.put(0, fb.getId());
- GLES20.glDeleteFramebuffers(1, intBuf1);
- RendererUtil.checkGLError();
-
- fb.resetObject();
-
- statistics.onDeleteFrameBuffer();
- }
- }
-
- /*********************************************************************\
- |* Textures *|
- \*********************************************************************/
- private int convertTextureType(Texture.Type type) {
- switch (type) {
- case TwoDimensional:
- return GLES20.GL_TEXTURE_2D;
- // case TwoDimensionalArray:
- // return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT;
-// case ThreeDimensional:
- // return GLES20.GL_TEXTURE_3D;
- case CubeMap:
- return GLES20.GL_TEXTURE_CUBE_MAP;
- default:
- throw new UnsupportedOperationException("Unknown texture type: " + type);
- }
- }
-
- private int convertMagFilter(Texture.MagFilter filter) {
- switch (filter) {
- case Bilinear:
- return GLES20.GL_LINEAR;
- case Nearest:
- return GLES20.GL_NEAREST;
- default:
- throw new UnsupportedOperationException("Unknown mag filter: " + filter);
- }
- }
-
- private int convertMinFilter(Texture.MinFilter filter) {
- switch (filter) {
- case Trilinear:
- return GLES20.GL_LINEAR_MIPMAP_LINEAR;
- case BilinearNearestMipMap:
- return GLES20.GL_LINEAR_MIPMAP_NEAREST;
- case NearestLinearMipMap:
- return GLES20.GL_NEAREST_MIPMAP_LINEAR;
- case NearestNearestMipMap:
- return GLES20.GL_NEAREST_MIPMAP_NEAREST;
- case BilinearNoMipMaps:
- return GLES20.GL_LINEAR;
- case NearestNoMipMaps:
- return GLES20.GL_NEAREST;
- default:
- throw new UnsupportedOperationException("Unknown min filter: " + filter);
- }
- }
-
- private int convertWrapMode(Texture.WrapMode mode) {
- switch (mode) {
- case BorderClamp:
- case Clamp:
- case EdgeClamp:
- return GLES20.GL_CLAMP_TO_EDGE;
- case Repeat:
- return GLES20.GL_REPEAT;
- case MirroredRepeat:
- return GLES20.GL_MIRRORED_REPEAT;
- default:
- throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
- }
- }
-
- /**
- * setupTextureParams sets the OpenGL context texture parameters
- * @param tex the Texture to set the texture parameters from
- */
- private void setupTextureParams(Texture tex) {
- int target = convertTextureType(tex.getType());
-
- // filter things
- int minFilter = convertMinFilter(tex.getMinFilter());
- int magFilter = convertMagFilter(tex.getMagFilter());
-
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MIN_FILTER, minFilter);
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MAG_FILTER, magFilter);
- RendererUtil.checkGLError();
-
- /*
- if (tex.getAnisotropicFilter() > 1){
-
- if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
- glTexParameterf(target,
- EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT,
- tex.getAnisotropicFilter());
- }
-
- }
- */
- // repeat modes
-
- switch (tex.getType()) {
- case ThreeDimensional:
- case CubeMap: // cubemaps use 3D coords
- // GL_TEXTURE_WRAP_R is not available in api 8
- //GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
- case TwoDimensional:
- case TwoDimensionalArray:
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
-
- // fall down here is intentional..
-// case OneDimensional:
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
-
- RendererUtil.checkGLError();
- break;
- default:
- throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
- }
-
- // R to Texture compare mode
-/*
- if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off){
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_MODE, GLES20.GL_COMPARE_R_TO_TEXTURE);
- GLES20.glTexParameteri(target, GLES20.GL_DEPTH_TEXTURE_MODE, GLES20.GL_INTENSITY);
- if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual){
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_GEQUAL);
- }else{
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_LEQUAL);
- }
- }
- */
- }
-
- /**
- * activates and binds the texture
- * @param img
- * @param type
- */
- public void updateTexImageData(Image img, Texture.Type type) {
- int texId = img.getId();
- if (texId == -1) {
- // create texture
- GLES20.glGenTextures(1, intBuf1);
- RendererUtil.checkGLError();
-
- texId = intBuf1.get(0);
- img.setId(texId);
- objManager.registerObject(img);
-
- statistics.onNewTexture();
- }
-
- // bind texture
- int target = convertTextureType(type);
- if (context.boundTextures[0] != img) {
- if (context.boundTextureUnit != 0) {
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- RendererUtil.checkGLError();
-
- context.boundTextureUnit = 0;
- }
-
- GLES20.glBindTexture(target, texId);
- RendererUtil.checkGLError();
-
- context.boundTextures[0] = img;
- }
-
- boolean needMips = false;
- if (img.isGeneratedMipmapsRequired()) {
- needMips = true;
- img.setMipmapsGenerated(true);
- }
-
- if (target == GLES20.GL_TEXTURE_CUBE_MAP) {
- // Check max texture size before upload
- if (img.getWidth() > maxCubeTexSize || img.getHeight() > maxCubeTexSize) {
- throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + maxCubeTexSize);
- }
- } else {
- if (img.getWidth() > maxTexSize || img.getHeight() > maxTexSize) {
- throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + maxTexSize);
- }
- }
-
- if (target == GLES20.GL_TEXTURE_CUBE_MAP) {
- // Upload a cube map / sky box
- @SuppressWarnings("unchecked")
- List bmps = (List) img.getEfficentData();
- if (bmps != null) {
- // Native android bitmap
- if (bmps.size() != 6) {
- throw new UnsupportedOperationException("Invalid texture: " + img
- + "Cubemap textures must contain 6 data units.");
- }
- for (int i = 0; i < 6; i++) {
- TextureUtil.uploadTextureBitmap(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, bmps.get(i).getBitmap(), needMips);
- bmps.get(i).notifyBitmapUploaded();
- }
- } else {
- // Standard jme3 image data
- List data = img.getData();
- if (data.size() != 6) {
- throw new UnsupportedOperationException("Invalid texture: " + img
- + "Cubemap textures must contain 6 data units.");
- }
- for (int i = 0; i < 6; i++) {
- TextureUtil.uploadTextureAny(img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, needMips);
- }
- }
- } else {
- TextureUtil.uploadTextureAny(img, target, 0, needMips);
- if (img.getEfficentData() instanceof AndroidImageInfo) {
- AndroidImageInfo info = (AndroidImageInfo) img.getEfficentData();
- info.notifyBitmapUploaded();
- }
- }
-
- img.clearUpdateNeeded();
- }
-
- public void setTexture(int unit, Texture tex) {
- Image image = tex.getImage();
- if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated()) ) {
- updateTexImageData(image, tex.getType());
- }
-
- int texId = image.getId();
- assert texId != -1;
-
- if (texId == -1) {
- logger.warning("error: texture image has -1 id");
- }
-
- Image[] textures = context.boundTextures;
-
- int type = convertTextureType(tex.getType());
-// if (!context.textureIndexList.moveToNew(unit)) {
-// if (context.boundTextureUnit != unit){
-// glActiveTexture(GL_TEXTURE0 + unit);
-// context.boundTextureUnit = unit;
-// }
-// glEnable(type);
-// }
-
- if (textures[unit] != image) {
- if (context.boundTextureUnit != unit) {
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
- context.boundTextureUnit = unit;
- }
-
- GLES20.glBindTexture(type, texId);
- RendererUtil.checkGLError();
-
- textures[unit] = image;
-
- statistics.onTextureUse(tex.getImage(), true);
- } else {
- statistics.onTextureUse(tex.getImage(), false);
- }
-
- setupTextureParams(tex);
- }
-
- public void modifyTexture(Texture tex, Image pixels, int x, int y) {
- setTexture(0, tex);
- TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y);
- }
-
- public void clearTextureUnits() {
- IDList textureList = context.textureIndexList;
- Image[] textures = context.boundTextures;
- for (int i = 0; i < textureList.oldLen; i++) {
- int idx = textureList.oldList[i];
-// if (context.boundTextureUnit != idx){
-// glActiveTexture(GL_TEXTURE0 + idx);
-// context.boundTextureUnit = idx;
-// }
-// glDisable(convertTextureType(textures[idx].getType()));
- textures[idx] = null;
- }
- context.textureIndexList.copyNewToOld();
- }
-
- public void deleteImage(Image image) {
- int texId = image.getId();
- if (texId != -1) {
- intBuf1.put(0, texId);
- intBuf1.position(0).limit(1);
-
- GLES20.glDeleteTextures(1, intBuf1);
- RendererUtil.checkGLError();
-
- image.resetObject();
-
- statistics.onDeleteTexture();
- }
- }
-
- /*********************************************************************\
- |* Vertex Buffers and Attributes *|
- \*********************************************************************/
- private int convertUsage(Usage usage) {
- switch (usage) {
- case Static:
- return GLES20.GL_STATIC_DRAW;
- case Dynamic:
- return GLES20.GL_DYNAMIC_DRAW;
- case Stream:
- return GLES20.GL_STREAM_DRAW;
- default:
- throw new RuntimeException("Unknown usage type.");
- }
- }
-
- private int convertVertexBufferFormat(Format format) {
- switch (format) {
- case Byte:
- return GLES20.GL_BYTE;
- case UnsignedByte:
- return GLES20.GL_UNSIGNED_BYTE;
- case Short:
- return GLES20.GL_SHORT;
- case UnsignedShort:
- return GLES20.GL_UNSIGNED_SHORT;
- case Int:
- return GLES20.GL_INT;
- case UnsignedInt:
- return GLES20.GL_UNSIGNED_INT;
- /*
- case Half:
- return NVHalfFloat.GL_HALF_FLOAT_NV;
- // return ARBHalfFloatVertex.GL_HALF_FLOAT;
- */
- case Float:
- return GLES20.GL_FLOAT;
-// case Double:
-// return GLES20.GL_DOUBLE;
- default:
- throw new RuntimeException("Unknown buffer format.");
-
- }
- }
-
- public void updateBufferData(VertexBuffer vb) {
- int bufId = vb.getId();
- boolean created = false;
- if (bufId == -1) {
- // create buffer
- GLES20.glGenBuffers(1, intBuf1);
- RendererUtil.checkGLError();
-
- bufId = intBuf1.get(0);
- vb.setId(bufId);
- objManager.registerObject(vb);
-
- created = true;
- }
-
- // bind buffer
- int target;
- if (vb.getBufferType() == VertexBuffer.Type.Index) {
- target = GLES20.GL_ELEMENT_ARRAY_BUFFER;
- if (context.boundElementArrayVBO != bufId) {
- GLES20.glBindBuffer(target, bufId);
- RendererUtil.checkGLError();
-
- context.boundElementArrayVBO = bufId;
- }
- } else {
- target = GLES20.GL_ARRAY_BUFFER;
- if (context.boundArrayVBO != bufId) {
- GLES20.glBindBuffer(target, bufId);
- RendererUtil.checkGLError();
-
- context.boundArrayVBO = bufId;
- }
- }
-
- int usage = convertUsage(vb.getUsage());
- vb.getData().rewind();
-
- // if (created || vb.hasDataSizeChanged()) {
- // upload data based on format
- int size = vb.getData().limit() * vb.getFormat().getComponentSize();
-
- switch (vb.getFormat()) {
- case Byte:
- case UnsignedByte:
- GLES20.glBufferData(target, size, (ByteBuffer) vb.getData(), usage);
- RendererUtil.checkGLError();
- break;
- case Short:
- case UnsignedShort:
- GLES20.glBufferData(target, size, (ShortBuffer) vb.getData(), usage);
- RendererUtil.checkGLError();
- break;
- case Int:
- case UnsignedInt:
- GLES20.glBufferData(target, size, (IntBuffer) vb.getData(), usage);
- RendererUtil.checkGLError();
- break;
- case Float:
- GLES20.glBufferData(target, size, (FloatBuffer) vb.getData(), usage);
- RendererUtil.checkGLError();
- break;
- default:
- throw new RuntimeException("Unknown buffer format.");
- }
-// } else {
-// int size = vb.getData().limit() * vb.getFormat().getComponentSize();
-//
-// switch (vb.getFormat()) {
-// case Byte:
-// case UnsignedByte:
-// GLES20.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData());
-// RendererUtil.checkGLError();
-// break;
-// case Short:
-// case UnsignedShort:
-// GLES20.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData());
-// RendererUtil.checkGLError();
-// break;
-// case Int:
-// case UnsignedInt:
-// GLES20.glBufferSubData(target, 0, size, (IntBuffer) vb.getData());
-// RendererUtil.checkGLError();
-// break;
-// case Float:
-// GLES20.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData());
-// RendererUtil.checkGLError();
-// break;
-// default:
-// throw new RuntimeException("Unknown buffer format.");
-// }
-// }
- vb.clearUpdateNeeded();
- }
-
- public void deleteBuffer(VertexBuffer vb) {
- int bufId = vb.getId();
- if (bufId != -1) {
- // delete buffer
- intBuf1.put(0, bufId);
- intBuf1.position(0).limit(1);
-
- GLES20.glDeleteBuffers(1, intBuf1);
- RendererUtil.checkGLError();
-
- vb.resetObject();
- }
- }
-
- public void clearVertexAttribs() {
- IDList attribList = context.attribIndexList;
- for (int i = 0; i < attribList.oldLen; i++) {
- int idx = attribList.oldList[i];
-
- GLES20.glDisableVertexAttribArray(idx);
- RendererUtil.checkGLError();
-
- context.boundAttribs[idx] = null;
- }
- context.attribIndexList.copyNewToOld();
- }
-
- public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
- if (vb.getBufferType() == VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
- }
-
- if (vb.isUpdateNeeded() && idb == null) {
- updateBufferData(vb);
- }
-
- int programId = context.boundShaderProgram;
- if (programId > 0) {
- Attribute attrib = boundShader.getAttribute(vb.getBufferType());
- int loc = attrib.getLocation();
- if (loc == -1) {
- return; // not defined
- }
-
- if (loc == -2) {
-// stringBuf.setLength(0);
-// stringBuf.append("in").append(vb.getBufferType().name()).append('\0');
-// updateNameBuffer();
-
- String attributeName = "in" + vb.getBufferType().name();
- loc = GLES20.glGetAttribLocation(programId, attributeName);
- RendererUtil.checkGLError();
-
- // not really the name of it in the shader (inPosition\0) but
- // the internal name of the enum (Position).
- if (loc < 0) {
- attrib.setLocation(-1);
- return; // not available in shader.
- } else {
- attrib.setLocation(loc);
- }
- }
-
- VertexBuffer[] attribs = context.boundAttribs;
- if (!context.attribIndexList.moveToNew(loc)) {
- GLES20.glEnableVertexAttribArray(loc);
- RendererUtil.checkGLError();
- //System.out.println("Enabled ATTRIB IDX: "+loc);
- }
- if (attribs[loc] != vb) {
- // NOTE: Use id from interleaved buffer if specified
- int bufId = idb != null ? idb.getId() : vb.getId();
- assert bufId != -1;
-
- if (bufId == -1) {
- logger.warning("invalid buffer id");
- }
-
- if (context.boundArrayVBO != bufId) {
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufId);
- RendererUtil.checkGLError();
-
- context.boundArrayVBO = bufId;
- }
-
- vb.getData().rewind();
-
- GLES20.glVertexAttribPointer(loc,
- vb.getNumComponents(),
- convertVertexBufferFormat(vb.getFormat()),
- vb.isNormalized(),
- vb.getStride(),
- 0);
-
- RendererUtil.checkGLError();
-
- attribs[loc] = vb;
- }
- } else {
- throw new IllegalStateException("Cannot render mesh without shader bound");
- }
- }
-
- public void setVertexAttrib(VertexBuffer vb) {
- setVertexAttrib(vb, null);
- }
-
- public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
- /* if (count > 1){
- ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0,
- vertCount, count);
- }else{*/
- GLES20.glDrawArrays(convertElementMode(mode), 0, vertCount);
- RendererUtil.checkGLError();
- /*
- }*/
- }
-
- public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
- if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
- }
-
- if (indexBuf.isUpdateNeeded()) {
- updateBufferData(indexBuf);
- }
-
- int bufId = indexBuf.getId();
- assert bufId != -1;
-
- if (bufId == -1) {
- throw new RendererException("Invalid buffer ID");
- }
-
- if (context.boundElementArrayVBO != bufId) {
- GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufId);
- RendererUtil.checkGLError();
-
- context.boundElementArrayVBO = bufId;
- }
-
- int vertCount = mesh.getVertexCount();
- boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
-
- Buffer indexData = indexBuf.getData();
-
- if (indexBuf.getFormat() == Format.UnsignedInt) {
- throw new RendererException("OpenGL ES does not support 32-bit index buffers." +
- "Split your models to avoid going over 65536 vertices.");
- }
-
- if (mesh.getMode() == Mode.Hybrid) {
- int[] modeStart = mesh.getModeStart();
- int[] elementLengths = mesh.getElementLengths();
-
- int elMode = convertElementMode(Mode.Triangles);
- int fmt = convertVertexBufferFormat(indexBuf.getFormat());
- int elSize = indexBuf.getFormat().getComponentSize();
- int listStart = modeStart[0];
- int stripStart = modeStart[1];
- int fanStart = modeStart[2];
- int curOffset = 0;
- for (int i = 0; i < elementLengths.length; i++) {
- if (i == stripStart) {
- elMode = convertElementMode(Mode.TriangleStrip);
- } else if (i == fanStart) {
- elMode = convertElementMode(Mode.TriangleStrip);
- }
- int elementLength = elementLengths[i];
-
- if (useInstancing) {
- //ARBDrawInstanced.
- throw new IllegalArgumentException("instancing is not supported.");
- /*
- GLES20.glDrawElementsInstancedARB(elMode,
- elementLength,
- fmt,
- curOffset,
- count);
- */
- } else {
- indexBuf.getData().position(curOffset);
- GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
- RendererUtil.checkGLError();
- /*
- glDrawRangeElements(elMode,
- 0,
- vertCount,
- elementLength,
- fmt,
- curOffset);
- */
- }
-
- curOffset += elementLength * elSize;
- }
- } else {
- if (useInstancing) {
- throw new IllegalArgumentException("instancing is not supported.");
- //ARBDrawInstanced.
-/*
- GLES20.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- 0,
- count);
- */
- } else {
- indexData.rewind();
- GLES20.glDrawElements(
- convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- 0);
- RendererUtil.checkGLError();
- }
- }
- }
-
- /*********************************************************************\
- |* Render Calls *|
- \*********************************************************************/
- public int convertElementMode(Mesh.Mode mode) {
- switch (mode) {
- case Points:
- return GLES20.GL_POINTS;
- case Lines:
- return GLES20.GL_LINES;
- case LineLoop:
- return GLES20.GL_LINE_LOOP;
- case LineStrip:
- return GLES20.GL_LINE_STRIP;
- case Triangles:
- return GLES20.GL_TRIANGLES;
- case TriangleFan:
- return GLES20.GL_TRIANGLE_FAN;
- case TriangleStrip:
- return GLES20.GL_TRIANGLE_STRIP;
- default:
- throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
- }
- }
-
- public void updateVertexArray(Mesh mesh) {
- logger.log(Level.FINE, "updateVertexArray({0})", mesh);
- int id = mesh.getId();
- /*
- if (id == -1){
- IntBuffer temp = intBuf1;
- // ARBVertexArrayObject.glGenVertexArrays(temp);
- GLES20.glGenVertexArrays(temp);
- id = temp.get(0);
- mesh.setId(id);
- }
-
- if (context.boundVertexArray != id){
- // ARBVertexArrayObject.glBindVertexArray(id);
- GLES20.glBindVertexArray(id);
- context.boundVertexArray = id;
- }
- */
- VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
- if (interleavedData != null && interleavedData.isUpdateNeeded()) {
- updateBufferData(interleavedData);
- }
-
-
- for (VertexBuffer vb : mesh.getBufferList().getArray()){
-
- if (vb.getBufferType() == Type.InterleavedData
- || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
- || vb.getBufferType() == Type.Index) {
- continue;
- }
-
- if (vb.getStride() == 0) {
- // not interleaved
- setVertexAttrib(vb);
- } else {
- // interleaved
- setVertexAttrib(vb, interleavedData);
- }
- }
- }
-
- /**
- * renderMeshVertexArray renders a mesh using vertex arrays
- */
- private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
- for (VertexBuffer vb : mesh.getBufferList().getArray()) {
- if (vb.getBufferType() == Type.InterleavedData
- || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
- || vb.getBufferType() == Type.Index) {
- continue;
- }
-
- if (vb.getStride() == 0) {
- // not interleaved
- setVertexAttrib_Array(vb);
- } else {
- // interleaved
- VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
- setVertexAttrib_Array(vb, interleavedData);
- }
- }
-
- VertexBuffer indices;
- if (mesh.getNumLodLevels() > 0) {
- indices = mesh.getLodLevel(lod);
- } else {
- indices = mesh.getBuffer(Type.Index);//buffers.get(Type.Index.ordinal());
- }
- if (indices != null) {
- drawTriangleList_Array(indices, mesh, count);
- } else {
- GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
- RendererUtil.checkGLError();
- }
- clearVertexAttribs();
- clearTextureUnits();
- }
-
- private void renderMeshDefault(Mesh mesh, int lod, int count) {
- VertexBuffer indices;
- VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
- if (interleavedData != null && interleavedData.isUpdateNeeded()) {
- updateBufferData(interleavedData);
- }
-
- //IntMap buffers = mesh.getBuffers(); ;
- if (mesh.getNumLodLevels() > 0) {
- indices = mesh.getLodLevel(lod);
- } else {
- indices = mesh.getBuffer(Type.Index);// buffers.get(Type.Index.ordinal());
- }
- for (VertexBuffer vb : mesh.getBufferList().getArray()){
- if (vb.getBufferType() == Type.InterleavedData
- || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
- || vb.getBufferType() == Type.Index) {
- continue;
- }
-
- if (vb.getStride() == 0) {
- // not interleaved
- setVertexAttrib(vb);
- } else {
- // interleaved
- setVertexAttrib(vb, interleavedData);
- }
- }
- if (indices != null) {
- drawTriangleList(indices, mesh, count);
- } else {
-// throw new UnsupportedOperationException("Cannot render without index buffer");
- GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
- RendererUtil.checkGLError();
- }
- clearVertexAttribs();
- clearTextureUnits();
- }
-
- public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
- if (mesh.getVertexCount() == 0) {
- return;
- }
-
- /*
- * NOTE: not supported in OpenGL ES 2.0.
- if (context.pointSize != mesh.getPointSize()) {
- GLES10.glPointSize(mesh.getPointSize());
- context.pointSize = mesh.getPointSize();
- }
- */
- if (context.lineWidth != mesh.getLineWidth()) {
- GLES20.glLineWidth(mesh.getLineWidth());
- RendererUtil.checkGLError();
- context.lineWidth = mesh.getLineWidth();
- }
-
- statistics.onMeshDrawn(mesh, lod);
-// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
-// renderMeshVertexArray(mesh, lod, count);
-// }else{
-
- if (useVBO) {
- renderMeshDefault(mesh, lod, count);
- } else {
- renderMeshVertexArray(mesh, lod, count);
- }
- }
-
- /**
- * drawTriangleList_Array uses Vertex Array
- * @param indexBuf
- * @param mesh
- * @param count
- */
- public void drawTriangleList_Array(VertexBuffer indexBuf, Mesh mesh, int count) {
- if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
- }
-
- boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
- if (useInstancing) {
- throw new IllegalArgumentException("Caps.MeshInstancing is not supported.");
- }
-
- int vertCount = mesh.getVertexCount();
- Buffer indexData = indexBuf.getData();
- indexData.rewind();
-
- if (mesh.getMode() == Mode.Hybrid) {
- int[] modeStart = mesh.getModeStart();
- int[] elementLengths = mesh.getElementLengths();
-
- int elMode = convertElementMode(Mode.Triangles);
- int fmt = convertVertexBufferFormat(indexBuf.getFormat());
- int elSize = indexBuf.getFormat().getComponentSize();
- int listStart = modeStart[0];
- int stripStart = modeStart[1];
- int fanStart = modeStart[2];
- int curOffset = 0;
- for (int i = 0; i < elementLengths.length; i++) {
- if (i == stripStart) {
- elMode = convertElementMode(Mode.TriangleStrip);
- } else if (i == fanStart) {
- elMode = convertElementMode(Mode.TriangleFan);
- }
- int elementLength = elementLengths[i];
-
- indexBuf.getData().position(curOffset);
- GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
- RendererUtil.checkGLError();
-
- curOffset += elementLength * elSize;
- }
- } else {
- GLES20.glDrawElements(
- convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- indexBuf.getData());
- RendererUtil.checkGLError();
- }
- }
-
- /**
- * setVertexAttrib_Array uses Vertex Array
- * @param vb
- * @param idb
- */
- public void setVertexAttrib_Array(VertexBuffer vb, VertexBuffer idb) {
- if (vb.getBufferType() == VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
- }
-
- // Get shader
- int programId = context.boundShaderProgram;
- if (programId > 0) {
- VertexBuffer[] attribs = context.boundAttribs;
-
- Attribute attrib = boundShader.getAttribute(vb.getBufferType());
- int loc = attrib.getLocation();
- if (loc == -1) {
- //throw new IllegalArgumentException("Location is invalid for attrib: [" + vb.getBufferType().name() + "]");
- return;
- } else if (loc == -2) {
- String attributeName = "in" + vb.getBufferType().name();
-
- loc = GLES20.glGetAttribLocation(programId, attributeName);
- RendererUtil.checkGLError();
-
- if (loc < 0) {
- attrib.setLocation(-1);
- return; // not available in shader.
- } else {
- attrib.setLocation(loc);
- }
-
- } // if (loc == -2)
-
- if ((attribs[loc] != vb) || vb.isUpdateNeeded()) {
- // NOTE: Use data from interleaved buffer if specified
- VertexBuffer avb = idb != null ? idb : vb;
- avb.getData().rewind();
- avb.getData().position(vb.getOffset());
-
- // Upload attribute data
- GLES20.glVertexAttribPointer(loc,
- vb.getNumComponents(),
- convertVertexBufferFormat(vb.getFormat()),
- vb.isNormalized(),
- vb.getStride(),
- avb.getData());
-
- RendererUtil.checkGLError();
-
- GLES20.glEnableVertexAttribArray(loc);
- RendererUtil.checkGLError();
-
- attribs[loc] = vb;
- } // if (attribs[loc] != vb)
- } else {
- throw new IllegalStateException("Cannot render mesh without shader bound");
- }
- }
-
- /**
- * setVertexAttrib_Array uses Vertex Array
- * @param vb
- */
- public void setVertexAttrib_Array(VertexBuffer vb) {
- setVertexAttrib_Array(vb, null);
- }
-
- public void setAlphaToCoverage(boolean value) {
- if (value) {
- GLES20.glEnable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE);
- RendererUtil.checkGLError();
- } else {
- GLES20.glDisable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE);
- RendererUtil.checkGLError();
- }
- }
-
- @Override
- public void invalidateState() {
- context.reset();
- boundShader = null;
- lastFb = null;
- }
-
- public void setMainFrameBufferSrgb(boolean srgb) {
- //TODO once opglES3.0 is supported maybe....
- }
-
- public void setLinearizeSrgbImages(boolean linearize) {
- //TODO once opglES3.0 is supported maybe....
- }
-
- public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
- throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly");
- }
-}
diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
index cd849b1dd..991ad25c0 100644
--- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
+++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
@@ -46,17 +46,16 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.EditText;
import android.widget.FrameLayout;
import com.jme3.input.*;
-import com.jme3.input.android.AndroidSensorJoyInput;
import com.jme3.input.android.AndroidInputHandler;
+import com.jme3.input.android.AndroidInputHandler14;
import com.jme3.input.controls.SoftTextDialogInputListener;
import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.renderer.android.AndroidGL;
import com.jme3.renderer.opengl.GL;
-import com.jme3.renderer.opengl.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 java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
@@ -79,7 +78,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
protected AndroidInputHandler androidInput;
protected long minFrameDuration = 0; // No FPS cap
protected long lastUpdateTime = 0;
- protected JoyInput androidSensorJoyInput = null;
public OGLESContext() {
}
@@ -113,8 +111,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
// Start to set up the view
GLSurfaceView view = new GLSurfaceView(context);
+ logger.log(Level.INFO, "Android Build Version: {0}", Build.VERSION.SDK_INT);
if (androidInput == null) {
- androidInput = new AndroidInputHandler();
+ if (Build.VERSION.SDK_INT >= 14) {
+ androidInput = new AndroidInputHandler14();
+ } else if (Build.VERSION.SDK_INT >= 9){
+ androidInput = new AndroidInputHandler();
+ }
}
androidInput.setView(view);
androidInput.loadSettings(settings);
@@ -194,7 +197,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
Object gl = new AndroidGL();
// gl = GLTracer.createGlesTracer((GL)gl, (GLExt)gl);
// gl = new GLDebugES((GL)gl, (GLExt)gl);
- renderer = new GLRenderer((GL)gl, (GLExt)gl);
+ renderer = new GLRenderer((GL)gl, (GLExt)gl, (GLFbo)gl);
renderer.initialize();
JmeSystem.setSoftTextDialogInput(this);
@@ -267,15 +270,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
@Override
public JoyInput getJoyInput() {
- if (androidSensorJoyInput == null) {
- androidSensorJoyInput = new AndroidSensorJoyInput();
- }
- return androidSensorJoyInput;
+ return androidInput.getJoyInput();
}
@Override
public TouchInput getTouchInput() {
- return androidInput;
+ return androidInput.getTouchInput();
}
@Override
diff --git a/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java b/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java
index 42c621a4f..61d0d6868 100644
--- a/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java
+++ b/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java
@@ -7,21 +7,19 @@ import com.jme3.app.AndroidHarness;
public class DemoAndroidHarness extends AndroidHarness
{
@Override
- public void onCreate(Bundle savedInstanceState)
- {
+ public void onCreate(Bundle savedInstanceState)
+ {
// Set the application class to run
// First Extract the bundle from intent
Bundle bundle = getIntent().getExtras();
//Next extract the values using the key as
- appClass = bundle.getString("APPCLASSNAME");
-
+ appClass = bundle.getString("APPCLASSNAME");
+
exitDialogTitle = "Close Demo?";
exitDialogMessage = "Press Yes";
- screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-
- super.onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
}
}
diff --git a/jme3-blender/build.gradle b/jme3-blender/build.gradle
index 1b42c7109..a556d7a65 100644
--- a/jme3-blender/build.gradle
+++ b/jme3-blender/build.gradle
@@ -6,4 +6,7 @@ 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')
+}
\ No newline at end of file
diff --git a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
index c215d0677..c412c07b3 100644
--- a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
+++ b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
@@ -32,36 +32,19 @@
package com.jme3.asset;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
-import com.jme3.animation.Animation;
-import com.jme3.bounding.BoundingVolume;
-import com.jme3.collision.Collidable;
-import com.jme3.collision.CollisionResults;
-import com.jme3.collision.UnsupportedCollisionException;
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;
-import com.jme3.math.ColorRGBA;
-import com.jme3.post.Filter;
-import com.jme3.scene.CameraNode;
-import com.jme3.scene.LightNode;
-import com.jme3.scene.Node;
-import com.jme3.scene.SceneGraphVisitor;
-import com.jme3.scene.Spatial;
-import com.jme3.texture.Texture;
/**
* 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
@@ -72,7 +55,7 @@ public class BlenderKey extends ModelKey {
* This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
*/
protected int featuresToLoad = FeaturesToLoad.ALL;
- /** This variable determines if assets that are not linked to the objects should be loaded. */
+ /** 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;
@@ -268,6 +251,7 @@ public class BlenderKey extends ModelKey {
* @param featuresToLoad
* bitwise flag of FeaturesToLoad interface values
*/
+ @Deprecated
public void includeInLoading(int featuresToLoad) {
this.featuresToLoad |= featuresToLoad;
}
@@ -277,10 +261,12 @@ public class BlenderKey extends ModelKey {
* @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;
}
@@ -290,6 +276,7 @@ public class BlenderKey extends ModelKey {
* the blender file loader.
* @return features that will be loaded by the blender file loader
*/
+ @Deprecated
public int getFeaturesToLoad() {
return featuresToLoad;
}
@@ -317,15 +304,6 @@ public class BlenderKey extends ModelKey {
this.loadUnlinkedAssets = loadUnlinkedAssets;
}
- /**
- * This method creates an object where loading results will be stores. Only those features will be allowed to store
- * that were specified by features-to-load flag.
- * @return an object to store loading results
- */
- public LoadingResults prepareLoadingResults() {
- return new LoadingResults(featuresToLoad);
- }
-
/**
* 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.
@@ -699,8 +677,11 @@ public class BlenderKey extends ModelKey {
/**
* 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;
@@ -745,281 +726,4 @@ public class BlenderKey extends ModelKey {
*/
ALL_NAMES_MATCH;
}
-
- /**
- * This class holds the loading results according to the given loading flag.
- * @author Marcin Roguski (Kaelthas)
- */
- public static class LoadingResults extends Spatial {
-
- /** Bitwise mask of features that are to be loaded. */
- private final int featuresToLoad;
- /** The scenes from the file. */
- private List scenes;
- /** Objects from all scenes. */
- private List objects;
- /** Materials from all objects. */
- private List materials;
- /** Textures from all objects. */
- private List textures;
- /** Animations of all objects. */
- private List animations;
- /** All cameras from the file. */
- private List cameras;
- /** All lights from the file. */
- private List lights;
- /** Loaded sky. */
- private Spatial sky;
- /** Scene filters (ie. FOG). */
- private List filters;
- /**
- * 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;
-
- /**
- * Private constructor prevents users to create an instance of this class from outside the
- * @param featuresToLoad
- * bitwise mask of features that are to be loaded
- * @see FeaturesToLoad FeaturesToLoad
- */
- private LoadingResults(int featuresToLoad) {
- this.featuresToLoad = featuresToLoad;
- if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) {
- scenes = new ArrayList();
- }
- if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
- objects = new ArrayList();
- if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
- materials = new ArrayList();
- if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
- textures = new ArrayList();
- }
- }
- if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
- animations = new ArrayList();
- }
- }
- if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
- cameras = new ArrayList();
- }
- if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
- lights = new ArrayList();
- }
- }
-
- /**
- * This method returns a bitwise flag describing what features of the blend file will be included in the result.
- * @return bitwise mask of features that are to be loaded
- * @see FeaturesToLoad FeaturesToLoad
- */
- public int getLoadedFeatures() {
- return featuresToLoad;
- }
-
- /**
- * This method adds a scene to the result set.
- * @param scene
- * scene to be added to the result set
- */
- public void addScene(Node scene) {
- if (scenes != null) {
- scenes.add(scene);
- }
- }
-
- /**
- * This method adds an object to the result set.
- * @param object
- * object to be added to the result set
- */
- public void addObject(Node object) {
- if (objects != null) {
- objects.add(object);
- }
- }
-
- /**
- * This method adds a material to the result set.
- * @param material
- * material to be added to the result set
- */
- public void addMaterial(Material material) {
- if (materials != null) {
- materials.add(material);
- }
- }
-
- /**
- * This method adds a texture to the result set.
- * @param texture
- * texture to be added to the result set
- */
- public void addTexture(Texture texture) {
- if (textures != null) {
- textures.add(texture);
- }
- }
-
- /**
- * This method adds a camera to the result set.
- * @param camera
- * camera to be added to the result set
- */
- public void addCamera(CameraNode camera) {
- if (cameras != null) {
- cameras.add(camera);
- }
- }
-
- /**
- * This method adds a light to the result set.
- * @param light
- * light to be added to the result set
- */
- public void addLight(LightNode light) {
- if (lights != null) {
- lights.add(light);
- }
- }
-
- /**
- * This method sets the sky of the scene. Only one sky can be set.
- * @param sky
- * the sky to be set
- */
- public void setSky(Spatial sky) {
- this.sky = sky;
- }
-
- /**
- * This method adds a scene filter. Filters are used to load FOG or other
- * scene effects that blender can define.
- * @param filter
- * the filter to be added
- */
- public void addFilter(Filter filter) {
- if (filter != null) {
- if (filters == null) {
- filters = new ArrayList(5);
- }
- filters.add(filter);
- }
- }
-
- /**
- * @param backgroundColor
- * the background color
- */
- public void setBackgroundColor(ColorRGBA backgroundColor) {
- this.backgroundColor = backgroundColor;
- }
-
- /**
- * @return all loaded scenes
- */
- public List getScenes() {
- return scenes;
- }
-
- /**
- * @return all loaded objects
- */
- public List getObjects() {
- return objects;
- }
-
- /**
- * @return all loaded materials
- */
- public List getMaterials() {
- return materials;
- }
-
- /**
- * @return all loaded textures
- */
- public List getTextures() {
- return textures;
- }
-
- /**
- * @return all loaded animations
- */
- public List getAnimations() {
- return animations;
- }
-
- /**
- * @return all loaded cameras
- */
- public List getCameras() {
- return cameras;
- }
-
- /**
- * @return all loaded lights
- */
- public List getLights() {
- return lights;
- }
-
- /**
- * @return the scene's sky
- */
- public Spatial getSky() {
- return sky;
- }
-
- /**
- * @return scene filters
- */
- public List getFilters() {
- return filters;
- }
-
- /**
- * @return the background color
- */
- public ColorRGBA getBackgroundColor() {
- return backgroundColor;
- }
-
- @Override
- public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
- return 0;
- }
-
- @Override
- public void updateModelBound() {
- }
-
- @Override
- public void setModelBound(BoundingVolume modelBound) {
- }
-
- @Override
- public int getVertexCount() {
- return 0;
- }
-
- @Override
- public int getTriangleCount() {
- return 0;
- }
-
- @Override
- public Spatial deepClone() {
- return null;
- }
-
- @Override
- public void depthFirstTraversal(SceneGraphVisitor visitor) {
- }
-
- @Override
- protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) {
- }
- }
}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
index eff993f76..e11101c14 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
+++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
@@ -31,17 +31,34 @@
*/
package com.jme3.scene.plugins.blender;
+import java.io.File;
+import java.util.ArrayList;
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.animation.Animation;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable;
+import com.jme3.light.Light;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
+import com.jme3.post.Filter;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Node;
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.materials.MaterialContext;
+import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.objects.Properties;
+import com.jme3.texture.Texture;
/**
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
@@ -49,14 +66,16 @@ import com.jme3.scene.plugins.blender.objects.Properties;
* @author Marcin Roguski
*/
public abstract class AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName());
+
/** The blender context. */
- protected BlenderContext blenderContext;
+ protected BlenderContext blenderContext;
/** The version of the blend file. */
- protected final int blenderVersion;
+ protected final int blenderVersion;
/** This variable indicates if the Y asxis is the UP axis or not. */
- protected boolean fixUpAxis;
+ protected boolean fixUpAxis;
/** Quaternion used to rotate data when Y is up axis. */
- protected Quaternion upAxisRotationQuaternion;
+ protected Quaternion upAxisRotationQuaternion;
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
@@ -129,4 +148,115 @@ public abstract class AbstractBlenderHelper {
}
}
}
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("unchecked")
+ 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)) {
+ File file = new File(path);
+ List pathsToCheck = new ArrayList();
+ String currentPath = file.getName();
+ do {
+ pathsToCheck.add(currentPath);
+ file = file.getParentFile();
+ if (file != null) {
+ currentPath = file.getName() + '/' + currentPath;
+ }
+ } while (file != null);
+
+ Spatial loadedAsset = null;
+ BlenderKey blenderKey = null;
+ for (String p : pathsToCheck) {
+ blenderKey = new BlenderKey(p);
+ blenderKey.setLoadUnlinkedAssets(true);
+ try {
+ loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
+ break;// break if no exception was thrown
+ } catch (AssetNotFoundException e) {
+ LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p);
+ }
+ }
+
+ if (loadedAsset != null) {
+ Map> linkedData = loadedAsset.getUserData("linkedData");
+ for (Entry> entry : linkedData.entrySet()) {
+ String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
+
+ List scenes = (List) entry.getValue().get("scenes");
+ for (Node scene : scenes) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene);
+ }
+ List objects = (List) entry.getValue().get("objects");
+ for (Node object : objects) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object);
+ }
+ List meshes = (List) entry.getValue().get("meshes");
+ for (TemporalMesh mesh : meshes) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh);
+ }
+ List materials = (List) entry.getValue().get("materials");
+ for (MaterialContext materialContext : materials) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext);
+ }
+ List textures = (List) entry.getValue().get("textures");
+ for (Texture texture : textures) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture);
+ }
+ List images = (List) entry.getValue().get("images");
+ for (Texture image : images) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image);
+ }
+ List animations = (List) entry.getValue().get("animations");
+ for (Animation animation : animations) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation);
+ }
+ List cameras = (List) entry.getValue().get("cameras");
+ for (Camera camera : cameras) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera);
+ }
+ List lights = (List) entry.getValue().get("lights");
+ for (Light light : lights) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light);
+ }
+ Spatial sky = (Spatial) entry.getValue().get("sky");
+ if (sky != null) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky);
+ }
+ List filters = (List) entry.getValue().get("filters");
+ for (Filter filter : filters) {
+ blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter);
+ }
+ }
+ } 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;
+ }
}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
index cde38e327..6e8042b09 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
+++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
@@ -53,6 +53,7 @@ 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;
/**
@@ -64,49 +65,51 @@ import com.jme3.scene.plugins.blender.file.Structure;
*/
public class BlenderContext {
/** The blender file version. */
- private int blenderVersion;
+ private int blenderVersion;
/** The blender key. */
- private BlenderKey blenderKey;
+ private BlenderKey blenderKey;
/** The header of the file block. */
- private DnaBlockData dnaBlockData;
+ private DnaBlockData dnaBlockData;
/** The scene structure. */
- private Structure sceneStructure;
+ private Structure sceneStructure;
/** The input stream of the blend file. */
- private BlenderInputStream inputStream;
+ private BlenderInputStream inputStream;
/** The asset manager. */
- private AssetManager assetManager;
+ private AssetManager assetManager;
/** The blocks read from the file. */
- protected List blocks;
+ protected List blocks;
/**
* A map containing the file block headers. The key is the old memory address.
*/
- private Map fileBlockHeadersByOma = new HashMap();
+ private Map fileBlockHeadersByOma = new HashMap();
/** A map containing the file block headers. The key is the block code. */
- private Map> fileBlockHeadersByCode = new HashMap>();
+ private Map> fileBlockHeadersByCode = new HashMap>();
/**
* 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> loadedFeatures = new HashMap>();
+ private Map> loadedFeatures = new HashMap>();
+ /** 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> linkedFeatures = new HashMap>();
/** A stack that hold the parent structure of currently loaded feature. */
- private Stack parentStack = new Stack();
+ private Stack parentStack = new Stack();
/** A list of constraints for the specified object. */
- protected Map> constraints = new HashMap>();
+ protected Map> constraints = new HashMap>();
/** Animations loaded for features. */
- private Map> animations = new HashMap>();
+ private Map> animations = new HashMap>();
/** Loaded skeletons. */
- private Map skeletons = new HashMap();
+ private Map skeletons = new HashMap();
/** A map between skeleton and node it modifies. */
- private Map nodesWithSkeletons = new HashMap();
+ private Map nodesWithSkeletons = new HashMap();
/** A map of bone contexts. */
- protected Map boneContexts = new HashMap();
+ protected Map boneContexts = new HashMap();
/** A map og helpers that perform loading. */
- private Map helpers = new HashMap();
+ private Map helpers = new HashMap();
/** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */
- private Map> markers = new HashMap>();
+ private Map> markers = new HashMap>();
/** A map of blender actions. The key is the action name and the value is the action itself. */
- private Map actions = new HashMap();
+ private Map actions = new HashMap();
/**
* This method sets the blender file version.
@@ -231,10 +234,10 @@ public class BlenderContext {
*/
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
- List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
+ List headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) {
headers = new ArrayList();
- fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
+ fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers);
}
headers.add(fileBlockHeader);
}
@@ -258,7 +261,7 @@ public class BlenderContext {
* the code of file blocks
* @return a list of file blocks' headers of a specified code
*/
- public List getFileBlocks(Integer code) {
+ public List getFileBlocks(BlockCode code) {
return fileBlockHeadersByCode.get(code);
}
@@ -299,7 +302,7 @@ public class BlenderContext {
throw new IllegalArgumentException("One of the given arguments is null!");
}
Map map = loadedFeatures.get(oldMemoryAddress);
- if(map == null) {
+ if (map == null) {
map = new HashMap();
loadedFeatures.put(oldMemoryAddress, map);
}
@@ -325,6 +328,48 @@ public class BlenderContext {
return null;
}
+ /**
+ * The method adds linked content to the blender context.
+ * @param blenderFilePath
+ * the path of linked blender file
+ * @param featureName
+ * the linked feature name
+ * @param feature
+ * the linked feature
+ */
+ public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) {
+ if (feature != null) {
+ Map linkedFeatures = this.linkedFeatures.get(blenderFilePath);
+ if (linkedFeatures == null) {
+ linkedFeatures = new HashMap();
+ this.linkedFeatures.put(blenderFilePath, linkedFeatures);
+ }
+ if (!linkedFeatures.containsKey(featureName)) {
+ linkedFeatures.put(featureName, feature);
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ public Object getLinkedFeature(String blenderFilePath, String featureName) {
+ Map linkedFeatures = this.linkedFeatures.get(blenderFilePath);
+ return linkedFeatures != null ? linkedFeatures.get(featureName) : null;
+ }
+
+ /**
+ * @return all linked features for the current blend file
+ */
+ public Map> getLinkedFeatures() {
+ return linkedFeatures;
+ }
+
/**
* This method adds the structure to the parent stack.
*
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
index 200f83b39..dd0160063 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
+++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
@@ -33,17 +33,21 @@ package com.jme3.scene.plugins.blender;
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.AssetLoader;
import com.jme3.asset.BlenderKey;
-import com.jme3.asset.BlenderKey.FeaturesToLoad;
-import com.jme3.asset.BlenderKey.LoadingResults;
import com.jme3.asset.ModelKey;
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;
@@ -55,16 +59,20 @@ 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.
@@ -83,72 +91,130 @@ public class BlenderLoader implements AssetLoader {
try {
this.setup(assetInfo);
- List sceneBlocks = new ArrayList();
- BlenderKey blenderKey = blenderContext.getBlenderKey();
- LoadingResults loadingResults = blenderKey.prepareLoadingResults();
-
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations();
-
+
+ BlenderKey blenderKey = blenderContext.getBlenderKey();
+ LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) {
switch (block.getCode()) {
- case FileBlockHeader.BLOCK_OB00:// Object
+ case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
- Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
- if (object instanceof LightNode) {
- loadingResults.addLight((LightNode) object);
- } else if (object instanceof CameraNode) {
- loadingResults.addCamera((CameraNode) 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 (this.isRootObject(loadingResults, (Node) object)) {
- loadingResults.addObject((Node) object);
- }
+ 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 FileBlockHeader.BLOCK_MA00:// Material
-// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-// MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
-// if (blenderKey.isLoadUnlinkedAssets() && blenderKey.shouldLoad(FeaturesToLoad.MATERIALS)) {
-// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
-// }
-// break;
- case FileBlockHeader.BLOCK_SC00:// Scene
- if (blenderKey.shouldLoad(FeaturesToLoad.SCENES)) {
- sceneBlocks.add(block);
+ 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 FileBlockHeader.BLOCK_WO00:// World
- if (blenderKey.shouldLoad(FeaturesToLoad.WORLD)) {
- Structure worldStructure = block.getStructure(blenderContext);
- String worldName = worldStructure.getName();
- if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
- LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class);
- Light ambientLight = landscapeHelper.toAmbientLight(worldStructure);
- if(ambientLight != null) {
- loadingResults.addLight(new LightNode(null, ambientLight));
- }
- loadingResults.setSky(landscapeHelper.toSky(worldStructure));
- loadingResults.addFilter(landscapeHelper.toFog(worldStructure));
- loadingResults.setBackgroundColor(landscapeHelper.toBackgroundColor(worldStructure));
+ 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());
}
}
- // bake constraints after everything is loaded
+ LOGGER.fine("Baking constraints after every feature is loaded.");
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.bakeConstraints(blenderContext);
- // load the scene at the very end so that the root nodes have no parent during loading or constraints applying
- for (FileBlockHeader sceneBlock : sceneBlocks) {
- loadingResults.addScene(this.toScene(sceneBlock.getStructure(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)));
+ }
+
+ 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);
}
- return loadingResults;
+ if (blenderKey.isLoadUnlinkedAssets()) {
+ LOGGER.fine("Setting loaded content as user data in resulting sptaial.");
+ Map> linkedData = new HashMap>();
+
+ Map thisFileData = new HashMap();
+ thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList