Merge branch 'master' into PBRisComing
This commit is contained in:
commit
0594e5dc7e
16
.travis.yml
16
.travis.yml
@ -1,11 +1,23 @@
|
|||||||
language: java
|
language: java
|
||||||
# jdk:
|
sudo: false
|
||||||
# - oraclejdk8
|
env:
|
||||||
|
- GRADLE_USER_HOME=gradle-cache
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- gradle-cache
|
||||||
|
- netbeans
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
slack:
|
||||||
|
secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s="
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
# required libs for android build tools
|
# required libs for android build tools
|
||||||
# sudo apt-get update
|
# sudo apt-get update
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
jMonkeyEngine
|
jMonkeyEngine
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
[](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.
|
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:
|
The engine is used by several commercial game studios and computer-science courses. Here's a taste:
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'maven'
|
apply plugin: 'maven'
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
String mavenGroupId = 'com.jme3'
|
group = 'com.jme3'
|
||||||
String mavenVersion = jmeVersion + '-' + jmeVersionTag //'-SNAPSHOT'
|
version = jmeVersion + '-' + jmeVersionTag
|
||||||
|
|
||||||
sourceCompatibility = '1.6'
|
sourceCompatibility = '1.6'
|
||||||
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
||||||
@ -23,11 +24,6 @@ dependencies {
|
|||||||
testCompile group: 'junit', name: 'junit', version: '4.10'
|
testCompile group: 'junit', name: 'junit', version: '4.10'
|
||||||
}
|
}
|
||||||
|
|
||||||
String mavenArtifactId = name
|
|
||||||
|
|
||||||
group = mavenGroupId
|
|
||||||
version = mavenVersion
|
|
||||||
|
|
||||||
javadoc {
|
javadoc {
|
||||||
failOnError = false
|
failOnError = false
|
||||||
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
|
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
|
||||||
@ -60,11 +56,40 @@ artifacts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(install.repositories.mavenInstaller) {
|
publishing {
|
||||||
pom.project {
|
publications {
|
||||||
groupId = mavenGroupId
|
maven(MavenPublication) {
|
||||||
artifactId = mavenArtifactId
|
from components.java
|
||||||
version = mavenVersion
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,3 +23,14 @@ bulletZipFile = bullet.zip
|
|||||||
|
|
||||||
# Path for downloading NetBeans Base
|
# Path for downloading NetBeans Base
|
||||||
netbeansUrl = http://download.netbeans.org/netbeans/8.0.2/final/zip/netbeans-8.0.2-201411181905-javase.zip
|
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
|
||||||
|
@ -525,4 +525,9 @@ public class AndroidMediaPlayerAudioRenderer implements AudioRenderer,
|
|||||||
@Override
|
@Override
|
||||||
public void deleteFilter(Filter filter) {
|
public void deleteFilter(Filter filter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getSourcePlaybackTime(AudioSource src) {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,6 @@ package com.jme3.input.android;
|
|||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.ScaleGestureDetector;
|
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 com.jme3.input.event.TouchEvent;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -50,152 +47,75 @@ import java.util.logging.Logger;
|
|||||||
*
|
*
|
||||||
* @author iwgeric
|
* @author iwgeric
|
||||||
*/
|
*/
|
||||||
public class AndroidGestureHandler implements
|
public class AndroidGestureProcessor implements
|
||||||
GestureDetector.OnGestureListener,
|
GestureDetector.OnGestureListener,
|
||||||
GestureDetector.OnDoubleTapListener,
|
GestureDetector.OnDoubleTapListener,
|
||||||
ScaleGestureDetector.OnScaleGestureListener {
|
ScaleGestureDetector.OnScaleGestureListener {
|
||||||
private static final Logger logger = Logger.getLogger(AndroidGestureHandler.class.getName());
|
private static final Logger logger = Logger.getLogger(AndroidGestureProcessor.class.getName());
|
||||||
private AndroidInputHandler androidInput;
|
|
||||||
private GestureDetector gestureDetector;
|
private AndroidTouchInput touchInput;
|
||||||
private ScaleGestureDetector scaleDetector;
|
|
||||||
float gestureDownX = -1f;
|
float gestureDownX = -1f;
|
||||||
float gestureDownY = -1f;
|
float gestureDownY = -1f;
|
||||||
float scaleStartX = -1f;
|
float scaleStartX = -1f;
|
||||||
float scaleStartY = -1f;
|
float scaleStartY = -1f;
|
||||||
|
|
||||||
public AndroidGestureHandler(AndroidInputHandler androidInput) {
|
public AndroidGestureProcessor(AndroidTouchInput touchInput) {
|
||||||
this.androidInput = androidInput;
|
this.touchInput = touchInput;
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 */
|
/* Events from onGestureListener */
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onDown(MotionEvent event) {
|
public boolean onDown(MotionEvent event) {
|
||||||
// start of all GestureListeners. Not really a gesture by itself
|
// start of all GestureListeners. Not really a gesture by itself
|
||||||
// so we don't create an event.
|
// so we don't create an event.
|
||||||
// However, reset the scaleInProgress here since this is the beginning
|
// However, reset the scaleInProgress here since this is the beginning
|
||||||
// of a series of gesture events.
|
// of a series of gesture events.
|
||||||
// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
gestureDownX = androidInput.getJmeX(event.getX());
|
gestureDownX = touchInput.getJmeX(event.getX());
|
||||||
gestureDownY = androidInput.invertY(androidInput.getJmeY(event.getY()));
|
gestureDownY = touchInput.invertY(touchInput.getJmeY(event.getY()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onSingleTapUp(MotionEvent event) {
|
public boolean onSingleTapUp(MotionEvent event) {
|
||||||
// Up of single tap. May be followed by a double tap later.
|
// Up of single tap. May be followed by a double tap later.
|
||||||
// use onSingleTapConfirmed instead.
|
// use onSingleTapConfirmed instead.
|
||||||
// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onShowPress(MotionEvent event) {
|
public void onShowPress(MotionEvent event) {
|
||||||
// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
float jmeX = androidInput.getJmeX(event.getX());
|
float jmeX = touchInput.getJmeX(event.getX());
|
||||||
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
|
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
|
touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
|
||||||
touchEvent.setPointerId(getPointerId(event));
|
touchEvent.setPointerId(touchInput.getPointerId(event));
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
touchEvent.setPressure(event.getPressure());
|
touchEvent.setPressure(event.getPressure());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onLongPress(MotionEvent event) {
|
public void onLongPress(MotionEvent event) {
|
||||||
// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
float jmeX = androidInput.getJmeX(event.getX());
|
float jmeX = touchInput.getJmeX(event.getX());
|
||||||
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
|
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
|
touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
|
||||||
touchEvent.setPointerId(getPointerId(event));
|
touchEvent.setPointerId(touchInput.getPointerId(event));
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
touchEvent.setPressure(event.getPressure());
|
touchEvent.setPressure(event.getPressure());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) {
|
public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) {
|
||||||
// if not scaleInProgess, send scroll events. This is to avoid sending
|
// 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.
|
// scroll events when one of the fingers is lifted just before the other one.
|
||||||
@ -204,23 +124,23 @@ public class AndroidGestureHandler implements
|
|||||||
// 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
|
// 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.
|
// for the fact that jME has y=0 at bottom where Android has y=0 at top.
|
||||||
// if (!scaleInProgress) {
|
if (!touchInput.getScaleDetector().isInProgress()) {
|
||||||
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}",
|
// 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});
|
// 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 jmeX = touchInput.getJmeX(endEvent.getX());
|
||||||
float jmeY = androidInput.invertY(androidInput.getJmeY(endEvent.getY()));
|
float jmeY = touchInput.invertY(touchInput.getJmeY(endEvent.getY()));
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, androidInput.getJmeX(-distX), androidInput.getJmeY(distY));
|
touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, touchInput.getJmeX(-distX), touchInput.getJmeY(distY));
|
||||||
touchEvent.setPointerId(getPointerId(endEvent));
|
touchEvent.setPointerId(touchInput.getPointerId(endEvent));
|
||||||
touchEvent.setTime(endEvent.getEventTime());
|
touchEvent.setTime(endEvent.getEventTime());
|
||||||
touchEvent.setPressure(endEvent.getPressure());
|
touchEvent.setPressure(endEvent.getPressure());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float velocityX, float velocityY) {
|
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 happens only once at the end of the gesture (all fingers up).
|
||||||
// Fling returns the velocity of the finger movement in pixels/sec.
|
// Fling returns the velocity of the finger movement in pixels/sec.
|
||||||
@ -228,121 +148,127 @@ public class AndroidGestureHandler implements
|
|||||||
// Since this does not track the movement, use the start position and velocity 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}",
|
// 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});
|
// new Object[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
|
||||||
|
|
||||||
float jmeX = androidInput.getJmeX(startEvent.getX());
|
float jmeX = touchInput.getJmeX(startEvent.getX());
|
||||||
float jmeY = androidInput.invertY(androidInput.getJmeY(startEvent.getY()));
|
float jmeY = touchInput.invertY(touchInput.getJmeY(startEvent.getY()));
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
|
touchEvent.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
|
||||||
touchEvent.setPointerId(getPointerId(endEvent));
|
touchEvent.setPointerId(touchInput.getPointerId(endEvent));
|
||||||
touchEvent.setTime(endEvent.getEventTime());
|
touchEvent.setTime(endEvent.getEventTime());
|
||||||
touchEvent.setPressure(endEvent.getPressure());
|
touchEvent.setPressure(endEvent.getPressure());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Events from onDoubleTapListener */
|
/* Events from onDoubleTapListener */
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onSingleTapConfirmed(MotionEvent event) {
|
public boolean onSingleTapConfirmed(MotionEvent event) {
|
||||||
// Up of single tap when no double tap followed.
|
// Up of single tap when no double tap followed.
|
||||||
// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
float jmeX = androidInput.getJmeX(event.getX());
|
float jmeX = touchInput.getJmeX(event.getX());
|
||||||
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
|
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
|
touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
|
||||||
touchEvent.setPointerId(getPointerId(event));
|
touchEvent.setPointerId(touchInput.getPointerId(event));
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
touchEvent.setPressure(event.getPressure());
|
touchEvent.setPressure(event.getPressure());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onDoubleTap(MotionEvent event) {
|
public boolean onDoubleTap(MotionEvent event) {
|
||||||
//The down motion event of the first tap of the double-tap
|
//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
|
// DoubleTapEvent with a check for the UP action
|
||||||
// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
float jmeX = androidInput.getJmeX(event.getX());
|
float jmeX = touchInput.getJmeX(event.getX());
|
||||||
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
|
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
|
touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
|
||||||
touchEvent.setPointerId(getPointerId(event));
|
touchEvent.setPointerId(touchInput.getPointerId(event));
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
touchEvent.setPressure(event.getPressure());
|
touchEvent.setPressure(event.getPressure());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onDoubleTapEvent(MotionEvent event) {
|
public boolean onDoubleTapEvent(MotionEvent event) {
|
||||||
//Notified when an event within a double-tap gesture occurs, including the down, move(s), and up events.
|
//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
|
// 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}",
|
// logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
|
||||||
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
|
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
|
||||||
// if (getAction(event) == MotionEvent.ACTION_UP) {
|
if (touchInput.getAction(event) == MotionEvent.ACTION_UP) {
|
||||||
// TouchEvent touchEvent = touchEventPool.getNextFreeEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
// touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), androidInput.invertY(event.getY()), 0, 0);
|
touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), touchInput.invertY(event.getY()), 0, 0);
|
||||||
// touchEvent.setPointerId(getPointerId(event));
|
touchEvent.setPointerId(touchInput.getPointerId(event));
|
||||||
// touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
// touchEvent.setPressure(event.getPressure());
|
touchEvent.setPressure(event.getPressure());
|
||||||
// processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
// }
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Events from ScaleGestureDetector */
|
/* Events from ScaleGestureDetector */
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
||||||
// Scale uses a focusX and focusY instead of x and y. Focus is the middle
|
// 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
|
// 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.
|
// 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
|
// return true or all gestures for this beginning event will be discarded
|
||||||
logger.log(Level.INFO, "onScaleBegin");
|
// logger.log(Level.INFO, "onScaleBegin");
|
||||||
scaleStartX = gestureDownX;
|
scaleStartX = gestureDownX;
|
||||||
scaleStartY = gestureDownY;
|
scaleStartY = gestureDownY;
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f);
|
touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f);
|
||||||
touchEvent.setPointerId(0);
|
touchEvent.setPointerId(0);
|
||||||
touchEvent.setTime(scaleGestureDetector.getEventTime());
|
touchEvent.setTime(scaleGestureDetector.getEventTime());
|
||||||
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
|
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
|
||||||
touchEvent.setDeltaScaleSpan(0f);
|
touchEvent.setDeltaScaleSpan(0f);
|
||||||
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
|
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
|
||||||
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
|
touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
||||||
// return true or all gestures for this event will be accumulated
|
// return true or all gestures for this event will be accumulated
|
||||||
logger.log(Level.INFO, "onScale");
|
// logger.log(Level.INFO, "onScale");
|
||||||
scaleStartX = gestureDownX;
|
scaleStartX = gestureDownX;
|
||||||
scaleStartY = gestureDownY;
|
scaleStartY = gestureDownY;
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f);
|
touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f);
|
||||||
touchEvent.setPointerId(0);
|
touchEvent.setPointerId(0);
|
||||||
touchEvent.setTime(scaleGestureDetector.getEventTime());
|
touchEvent.setTime(scaleGestureDetector.getEventTime());
|
||||||
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
|
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
|
||||||
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
|
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
|
||||||
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
|
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
|
||||||
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
|
touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
||||||
logger.log(Level.INFO, "onScaleEnd");
|
// logger.log(Level.INFO, "onScaleEnd");
|
||||||
scaleStartX = gestureDownX;
|
scaleStartX = gestureDownX;
|
||||||
scaleStartY = gestureDownY;
|
scaleStartY = gestureDownY;
|
||||||
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
|
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f);
|
touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f);
|
||||||
touchEvent.setPointerId(0);
|
touchEvent.setPointerId(0);
|
||||||
touchEvent.setTime(scaleGestureDetector.getEventTime());
|
touchEvent.setTime(scaleGestureDetector.getEventTime());
|
||||||
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
|
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
|
||||||
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
|
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
|
||||||
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
|
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
|
||||||
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
|
touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
|
||||||
processEvent(touchEvent);
|
touchInput.addEvent(touchEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>AndroidInput</code> 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<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
|
|
||||||
final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
|
|
||||||
final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
|
|
||||||
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -33,231 +33,206 @@
|
|||||||
package com.jme3.input.android;
|
package com.jme3.input.android;
|
||||||
|
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
import android.os.Build;
|
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 android.view.View;
|
||||||
import com.jme3.input.RawInputListener;
|
import com.jme3.input.JoyInput;
|
||||||
import com.jme3.input.TouchInput;
|
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 com.jme3.system.AppSettings;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>AndroidInput</code> is the main class that connects the Android system
|
* <code>AndroidInput</code> is the main class that connects the Android system
|
||||||
* inputs to jME. It serves as the manager that gathers inputs from the various
|
* inputs to jME. It receives the inputs from the Android View and passes them
|
||||||
* Android input methods and provides them to jME's <code>InputManager</code>.
|
* to the appropriate classes based on the source of the input.</br>
|
||||||
|
* This class is to be extended when new functionality is released in Android.
|
||||||
*
|
*
|
||||||
* @author iwgeric
|
* @author iwgeric
|
||||||
*/
|
*/
|
||||||
public class AndroidInputHandler implements TouchInput {
|
public class AndroidInputHandler implements View.OnTouchListener,
|
||||||
|
View.OnKeyListener {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
|
private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
|
||||||
|
|
||||||
// Custom settings
|
protected GLSurfaceView view;
|
||||||
private boolean mouseEventsEnabled = true;
|
protected AndroidTouchInput touchInput;
|
||||||
private boolean mouseEventsInvertX = false;
|
protected AndroidJoyInput joyInput;
|
||||||
private boolean mouseEventsInvertY = false;
|
|
||||||
private boolean keyboardEventsEnabled = 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<InputEvent> inputEventQueue = new ConcurrentLinkedQueue<InputEvent>();
|
|
||||||
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() {
|
public AndroidInputHandler() {
|
||||||
int buildVersion = Build.VERSION.SDK_INT;
|
touchInput = new AndroidTouchInput(this);
|
||||||
logger.log(Level.INFO, "Android Build Version: {0}", buildVersion);
|
joyInput = new AndroidJoyInput(this);
|
||||||
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) {
|
public void setView(View view) {
|
||||||
if (touchHandler != null) {
|
if (this.view != null && view != null && this.view.equals(view)) {
|
||||||
touchHandler.setView(view);
|
return;
|
||||||
}
|
}
|
||||||
if (keyHandler != null) {
|
|
||||||
keyHandler.setView(view);
|
if (this.view != null) {
|
||||||
}
|
removeListeners(this.view);
|
||||||
if (gestureHandler != null) {
|
|
||||||
gestureHandler.setView(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.view = (GLSurfaceView)view;
|
this.view = (GLSurfaceView)view;
|
||||||
|
|
||||||
|
if (this.view != null) {
|
||||||
|
addListeners(this.view);
|
||||||
|
}
|
||||||
|
|
||||||
|
joyInput.setView((GLSurfaceView)view);
|
||||||
}
|
}
|
||||||
|
|
||||||
public View getView() {
|
public View getView() {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float invertX(float origX) {
|
protected void removeListeners(GLSurfaceView view) {
|
||||||
return getJmeX(view.getWidth()) - origX;
|
view.setOnTouchListener(null);
|
||||||
|
view.setOnKeyListener(null);
|
||||||
|
touchInput.setGestureDetector(null);
|
||||||
|
touchInput.setScaleDetector(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float invertY(float origY) {
|
protected void addListeners(GLSurfaceView view) {
|
||||||
return getJmeY(view.getHeight()) - origY;
|
view.setOnTouchListener(this);
|
||||||
}
|
view.setOnKeyListener(this);
|
||||||
|
AndroidGestureProcessor gestureHandler = new AndroidGestureProcessor(touchInput);
|
||||||
public float getJmeX(float origX) {
|
touchInput.setGestureDetector(new GestureDetector(
|
||||||
return origX * scaleX;
|
view.getContext(), gestureHandler));
|
||||||
}
|
touchInput.setScaleDetector(new ScaleGestureDetector(
|
||||||
|
view.getContext(), gestureHandler));
|
||||||
public float getJmeY(float origY) {
|
|
||||||
return origY * scaleY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadSettings(AppSettings settings) {
|
public void loadSettings(AppSettings settings) {
|
||||||
keyboardEventsEnabled = settings.isEmulateKeyboard();
|
touchInput.loadSettings(settings);
|
||||||
mouseEventsEnabled = settings.isEmulateMouse();
|
|
||||||
mouseEventsInvertX = settings.isEmulateMouseFlipX();
|
|
||||||
mouseEventsInvertY = settings.isEmulateMouseFlipY();
|
|
||||||
|
|
||||||
// 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});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
public TouchInput getTouchInput() {
|
||||||
// JME3 Input interface
|
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.
|
||||||
|
* </br>
|
||||||
|
* 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.</br>
|
||||||
|
*
|
||||||
|
* 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
|
@Override
|
||||||
public void initialize() {
|
public boolean onTouch(View view, MotionEvent event) {
|
||||||
touchEventPool.initialize();
|
if (view != getView()) {
|
||||||
if (touchHandler != null) {
|
return false;
|
||||||
touchHandler.initialize();
|
|
||||||
}
|
|
||||||
if (keyHandler != null) {
|
|
||||||
keyHandler.initialize();
|
|
||||||
}
|
|
||||||
if (gestureHandler != null) {
|
|
||||||
gestureHandler.initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized = true;
|
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
|
@Override
|
||||||
public void destroy() {
|
public boolean onKey(View view, int keyCode, KeyEvent event) {
|
||||||
initialized = false;
|
if (view != getView()) {
|
||||||
|
return false;
|
||||||
touchEventPool.destroy();
|
|
||||||
if (touchHandler != null) {
|
|
||||||
touchHandler.destroy();
|
|
||||||
}
|
|
||||||
if (keyHandler != null) {
|
|
||||||
keyHandler.destroy();
|
|
||||||
}
|
|
||||||
if (gestureHandler != null) {
|
|
||||||
gestureHandler.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setView(null);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return consumed;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AndroidInputHandler14</code> extends <code>AndroidInputHandler</code> to
|
||||||
|
* add the onHover and onGenericMotion events that where added in Android rev 14 (Android 4.0).</br>
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,9 +33,7 @@ package com.jme3.input.android;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.view.View;
|
|
||||||
import com.jme3.input.InputManager;
|
import com.jme3.input.InputManager;
|
||||||
import com.jme3.input.JoyInput;
|
import com.jme3.input.JoyInput;
|
||||||
import com.jme3.input.Joystick;
|
import com.jme3.input.Joystick;
|
||||||
@ -79,15 +77,16 @@ import java.util.logging.Logger;
|
|||||||
*
|
*
|
||||||
* @author iwgeric
|
* @author iwgeric
|
||||||
*/
|
*/
|
||||||
public class AndroidJoyInputHandler implements JoyInput {
|
public class AndroidJoyInput implements JoyInput {
|
||||||
private static final Logger logger = Logger.getLogger(AndroidJoyInputHandler.class.getName());
|
private static final Logger logger = Logger.getLogger(AndroidJoyInput.class.getName());
|
||||||
|
public static boolean disableSensors = false;
|
||||||
|
|
||||||
private List<Joystick> joystickList = new ArrayList<Joystick>();
|
protected AndroidInputHandler inputHandler;
|
||||||
|
protected List<Joystick> joystickList = new ArrayList<Joystick>();
|
||||||
// private boolean dontSendHistory = false;
|
// private boolean dontSendHistory = false;
|
||||||
|
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
private GLSurfaceView view;
|
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
private RawInputListener listener = null;
|
private RawInputListener listener = null;
|
||||||
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>();
|
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>();
|
||||||
@ -96,34 +95,29 @@ public class AndroidJoyInputHandler implements JoyInput {
|
|||||||
private boolean vibratorActive = false;
|
private boolean vibratorActive = false;
|
||||||
private long maxRumbleTime = 250; // 250ms
|
private long maxRumbleTime = 250; // 250ms
|
||||||
|
|
||||||
public AndroidJoyInputHandler() {
|
public AndroidJoyInput(AndroidInputHandler inputHandler) {
|
||||||
int buildVersion = Build.VERSION.SDK_INT;
|
this.inputHandler = inputHandler;
|
||||||
logger.log(Level.INFO, "Android Build Version: {0}", buildVersion);
|
|
||||||
// if (buildVersion >= 14) {
|
|
||||||
// touchHandler = new AndroidTouchHandler14(this);
|
|
||||||
// } else if (buildVersion >= 8){
|
|
||||||
// touchHandler = new AndroidTouchHandler(this);
|
|
||||||
// }
|
|
||||||
sensorJoyInput = new AndroidSensorJoyInput(this);
|
sensorJoyInput = new AndroidSensorJoyInput(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setView(GLSurfaceView view) {
|
public void setView(GLSurfaceView view) {
|
||||||
// if (touchHandler != null) {
|
if (view == null) {
|
||||||
// touchHandler.setView(view);
|
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) {
|
if (sensorJoyInput != null) {
|
||||||
sensorJoyInput.setView(view);
|
sensorJoyInput.setView(view);
|
||||||
}
|
}
|
||||||
this.view = (GLSurfaceView)view;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getView() {
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadSettings(AppSettings settings) {
|
public void loadSettings(AppSettings settings) {
|
||||||
// sensorEventsEnabled = settings.useSensors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEvent(InputEvent event) {
|
public void addEvent(InputEvent event) {
|
||||||
@ -155,20 +149,8 @@ public class AndroidJoyInputHandler implements JoyInput {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
// if (sensorJoyInput != null) {
|
|
||||||
// sensorJoyInput.initialize();
|
|
||||||
// }
|
|
||||||
// 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.");
|
|
||||||
}
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,8 +193,8 @@ public class AndroidJoyInputHandler implements JoyInput {
|
|||||||
};
|
};
|
||||||
final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from
|
final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from
|
||||||
|
|
||||||
logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
|
// logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
|
||||||
new Object[]{amount, rumbleOnDur, rumbleOffDur});
|
// new Object[]{amount, rumbleOnDur, rumbleOffDur});
|
||||||
|
|
||||||
if (rumbleOnDur > 0) {
|
if (rumbleOnDur > 0) {
|
||||||
vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
|
vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
|
||||||
@ -226,9 +208,10 @@ public class AndroidJoyInputHandler implements JoyInput {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Joystick[] loadJoysticks(InputManager inputManager) {
|
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));
|
joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager));
|
||||||
|
}
|
||||||
|
|
||||||
return joystickList.toArray( new Joystick[joystickList.size()] );
|
return joystickList.toArray( new Joystick[joystickList.size()] );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +235,4 @@ public class AndroidJoyInputHandler implements JoyInput {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AndroidJoyInput14</code> extends <code>AndroidJoyInput</code>
|
||||||
|
* to include support for physical joysticks/gamepads.</br>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Integer, AndroidJoystick> joystickIndex = new HashMap<Integer, AndroidJoystick>();
|
||||||
|
|
||||||
|
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<Joystick> loadJoysticks(int joyId, InputManager inputManager) {
|
||||||
|
logger.log(Level.INFO, "loading Joystick devices");
|
||||||
|
ArrayList<Joystick> joysticks = new ArrayList<Joystick>();
|
||||||
|
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<MotionRange> 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<Integer, JoystickAxis> axisIndex = new HashMap<Integer, JoystickAxis>();
|
||||||
|
private Map<Integer, JoystickButton> buttonIndex = new HashMap<Integer, JoystickButton>();
|
||||||
|
|
||||||
|
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<Integer> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -37,7 +37,8 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* AndroidKeyMapping is just a utility to convert the Android keyCodes into
|
* 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
|
* @author iwgeric
|
||||||
*/
|
*/
|
||||||
@ -143,7 +144,11 @@ public class AndroidKeyMapping {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public static int getJmeKey(int androidKey) {
|
public static int getJmeKey(int androidKey) {
|
||||||
|
if (androidKey > ANDROID_TO_JME.length) {
|
||||||
|
return androidKey;
|
||||||
|
} else {
|
||||||
return ANDROID_TO_JME[androidKey];
|
return ANDROID_TO_JME[androidKey];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,15 +75,15 @@ import java.util.logging.Logger;
|
|||||||
public class AndroidSensorJoyInput implements SensorEventListener {
|
public class AndroidSensorJoyInput implements SensorEventListener {
|
||||||
private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName());
|
private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName());
|
||||||
|
|
||||||
private AndroidJoyInputHandler joyHandler;
|
private AndroidJoyInput joyInput;
|
||||||
private SensorManager sensorManager = null;
|
private SensorManager sensorManager = null;
|
||||||
private WindowManager windowManager = null;
|
private WindowManager windowManager = null;
|
||||||
private IntMap<SensorData> sensors = new IntMap<SensorData>();
|
private IntMap<SensorData> sensors = new IntMap<SensorData>();
|
||||||
private int lastRotation = 0;
|
private int lastRotation = 0;
|
||||||
private boolean loaded = false;
|
private boolean loaded = false;
|
||||||
|
|
||||||
public AndroidSensorJoyInput(AndroidJoyInputHandler joyHandler) {
|
public AndroidSensorJoyInput(AndroidJoyInput joyInput) {
|
||||||
this.joyHandler = joyHandler;
|
this.joyInput = joyInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,7 +96,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
int sensorAccuracy = -1;
|
int sensorAccuracy = -1;
|
||||||
float[] lastValues;
|
float[] lastValues;
|
||||||
final Object valuesLock = new Object();
|
final Object valuesLock = new Object();
|
||||||
ArrayList<AndroidJoystickAxis> axes = new ArrayList<AndroidJoystickAxis>();
|
ArrayList<AndroidSensorJoystickAxis> axes = new ArrayList<AndroidSensorJoystickAxis>();
|
||||||
boolean enabled = false;
|
boolean enabled = false;
|
||||||
boolean haveData = false;
|
boolean haveData = false;
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
*/
|
*/
|
||||||
private boolean updateOrientation() {
|
private boolean updateOrientation() {
|
||||||
SensorData sensorData;
|
SensorData sensorData;
|
||||||
AndroidJoystickAxis axis;
|
AndroidSensorJoystickAxis axis;
|
||||||
final float[] curInclinationMat = new float[16];
|
final float[] curInclinationMat = new float[16];
|
||||||
final float[] curRotationMat = new float[16];
|
final float[] curRotationMat = new float[16];
|
||||||
final float[] rotatedRotationMat = new float[16];
|
final float[] rotatedRotationMat = new float[16];
|
||||||
@ -374,7 +374,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
sensorData.haveData = true;
|
sensorData.haveData = true;
|
||||||
} else {
|
} else {
|
||||||
if (axis.isChanged()) {
|
if (axis.isChanged()) {
|
||||||
joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
|
joyInput.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,10 +401,10 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
|
|
||||||
public Joystick loadJoystick(int joyId, InputManager inputManager) {
|
public Joystick loadJoystick(int joyId, InputManager inputManager) {
|
||||||
SensorData sensorData;
|
SensorData sensorData;
|
||||||
AndroidJoystickAxis axis;
|
AndroidSensorJoystickAxis axis;
|
||||||
|
|
||||||
AndroidJoystick joystick = new AndroidJoystick(inputManager,
|
AndroidSensorJoystick joystick = new AndroidSensorJoystick(inputManager,
|
||||||
joyHandler,
|
joyInput,
|
||||||
joyId,
|
joyId,
|
||||||
"AndroidSensorsJoystick");
|
"AndroidSensorsJoystick");
|
||||||
|
|
||||||
@ -522,15 +522,15 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}",
|
// logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}",
|
||||||
new Object[]{se.sensor.getName(), se.accuracy, se.values});
|
// new Object[]{se.sensor.getName(), se.accuracy, se.values});
|
||||||
|
|
||||||
int sensorType = se.sensor.getType();
|
int sensorType = se.sensor.getType();
|
||||||
|
|
||||||
SensorData sensorData = sensors.get(sensorType);
|
SensorData sensorData = sensors.get(sensorType);
|
||||||
if (sensorData != null) {
|
if (sensorData != null) {
|
||||||
logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}",
|
// logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}",
|
||||||
new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE});
|
// 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 != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) {
|
||||||
|
|
||||||
@ -543,8 +543,8 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sensorData != null && sensorData.axes.size() > 0) {
|
if (sensorData.axes.size() > 0) {
|
||||||
AndroidJoystickAxis axis;
|
AndroidSensorJoystickAxis axis;
|
||||||
for (int i=0; i<se.values.length; i++) {
|
for (int i=0; i<se.values.length; i++) {
|
||||||
axis = sensorData.axes.get(i);
|
axis = sensorData.axes.get(i);
|
||||||
if (axis != null) {
|
if (axis != null) {
|
||||||
@ -554,8 +554,8 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
} else {
|
} else {
|
||||||
if (axis.isChanged()) {
|
if (axis.isChanged()) {
|
||||||
JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue());
|
JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue());
|
||||||
logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event);
|
// logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event);
|
||||||
joyHandler.addEvent(event);
|
joyInput.addEvent(event);
|
||||||
// joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
|
// joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,14 +585,14 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
|
|
||||||
// End of SensorEventListener methods
|
// End of SensorEventListener methods
|
||||||
|
|
||||||
protected class AndroidJoystick extends AbstractJoystick {
|
protected class AndroidSensorJoystick extends AbstractJoystick {
|
||||||
private JoystickAxis nullAxis;
|
private JoystickAxis nullAxis;
|
||||||
private JoystickAxis xAxis;
|
private JoystickAxis xAxis;
|
||||||
private JoystickAxis yAxis;
|
private JoystickAxis yAxis;
|
||||||
private JoystickAxis povX;
|
private JoystickAxis povX;
|
||||||
private JoystickAxis povY;
|
private JoystickAxis povY;
|
||||||
|
|
||||||
public AndroidJoystick( InputManager inputManager, JoyInput joyInput,
|
public AndroidSensorJoystick( InputManager inputManager, JoyInput joyInput,
|
||||||
int joyId, String name){
|
int joyId, String name){
|
||||||
|
|
||||||
super( inputManager, joyInput, joyId, name );
|
super( inputManager, joyInput, joyId, name );
|
||||||
@ -606,10 +606,10 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AndroidJoystickAxis addAxis(String axisName, String logicalName, int axisNum, float maxRawValue) {
|
protected AndroidSensorJoystickAxis addAxis(String axisName, String logicalName, int axisNum, float maxRawValue) {
|
||||||
AndroidJoystickAxis axis;
|
AndroidSensorJoystickAxis axis;
|
||||||
|
|
||||||
axis = new AndroidJoystickAxis(
|
axis = new AndroidSensorJoystickAxis(
|
||||||
getInputManager(), // InputManager (InputManager)
|
getInputManager(), // InputManager (InputManager)
|
||||||
this, // parent Joystick (Joystick)
|
this, // parent Joystick (Joystick)
|
||||||
axisNum, // Axis Index (int)
|
axisNum, // Axis Index (int)
|
||||||
@ -654,7 +654,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AndroidJoystickAxis extends DefaultJoystickAxis implements SensorJoystickAxis {
|
public class AndroidSensorJoystickAxis extends DefaultJoystickAxis implements SensorJoystickAxis {
|
||||||
float zeroRawValue = 0f;
|
float zeroRawValue = 0f;
|
||||||
float curRawValue = 0f;
|
float curRawValue = 0f;
|
||||||
float lastRawValue = 0f;
|
float lastRawValue = 0f;
|
||||||
@ -662,7 +662,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
|
|||||||
float maxRawValue = FastMath.HALF_PI;
|
float maxRawValue = FastMath.HALF_PI;
|
||||||
boolean enabled = true;
|
boolean enabled = true;
|
||||||
|
|
||||||
public AndroidJoystickAxis(InputManager inputManager, Joystick parent,
|
public AndroidSensorJoystickAxis(InputManager inputManager, Joystick parent,
|
||||||
int axisIndex, String name, String logicalId,
|
int axisIndex, String name, String logicalId,
|
||||||
boolean isAnalog, boolean isRelative, float deadZone,
|
boolean isAnalog, boolean isRelative, float deadZone,
|
||||||
float maxRawValue) {
|
float maxRawValue) {
|
||||||
|
@ -1,257 +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.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import com.jme3.input.event.InputEvent;
|
|
||||||
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 java.util.HashMap;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AndroidTouchHandler 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 AndroidTouchHandler implements View.OnTouchListener {
|
|
||||||
private static final Logger logger = Logger.getLogger(AndroidTouchHandler.class.getName());
|
|
||||||
|
|
||||||
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
|
||||||
|
final private ConcurrentLinkedQueue<InputEvent> inputEventQueue = new ConcurrentLinkedQueue<InputEvent>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,7 +33,6 @@
|
|||||||
package com.jme3.input.android;
|
package com.jme3.input.android;
|
||||||
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
|
||||||
import com.jme3.input.event.TouchEvent;
|
import com.jme3.input.event.TouchEvent;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -41,36 +40,20 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AndroidTouchHandler14 is an extension of AndroidTouchHander that adds the
|
* AndroidTouchHandler14 extends AndroidTouchHandler to process the onHover
|
||||||
* Android touch event functionality between Android rev 9 (Android 2.3) and
|
* events added in Android rev 14 (Android 4.0).
|
||||||
* Android rev 14 (Android 4.0).
|
|
||||||
*
|
*
|
||||||
* @author iwgeric
|
* @author iwgeric
|
||||||
*/
|
*/
|
||||||
public class AndroidTouchHandler14 extends AndroidTouchHandler implements
|
public class AndroidTouchInput14 extends AndroidTouchInput {
|
||||||
View.OnHoverListener {
|
private static final Logger logger = Logger.getLogger(AndroidTouchInput14.class.getName());
|
||||||
private static final Logger logger = Logger.getLogger(AndroidTouchHandler14.class.getName());
|
|
||||||
final private HashMap<Integer, Vector2f> lastHoverPositions = new HashMap<Integer, Vector2f>();
|
final private HashMap<Integer, Vector2f> lastHoverPositions = new HashMap<Integer, Vector2f>();
|
||||||
|
|
||||||
public AndroidTouchHandler14(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) {
|
public AndroidTouchInput14(AndroidInputHandler androidInput) {
|
||||||
super(androidInput, gestureHandler);
|
super(androidInput);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setView(View view) {
|
|
||||||
if (view != null) {
|
|
||||||
view.setOnHoverListener(this);
|
|
||||||
} else {
|
|
||||||
androidInput.getView().setOnHoverListener(null);
|
|
||||||
}
|
|
||||||
super.setView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onHover(View view, MotionEvent event) {
|
|
||||||
if (view == null || view != androidInput.getView()) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean onHover(MotionEvent event) {
|
||||||
boolean consumed = false;
|
boolean consumed = false;
|
||||||
int action = getAction(event);
|
int action = getAction(event);
|
||||||
int pointerId = getPointerId(event);
|
int pointerId = getPointerId(event);
|
||||||
@ -81,15 +64,15 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
|
|||||||
|
|
||||||
numPointers = event.getPointerCount();
|
numPointers = event.getPointerCount();
|
||||||
|
|
||||||
logger.log(Level.INFO, "onHover pointerId: {0}, action: {1}, x: {2}, y: {3}, numPointers: {4}",
|
// 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()});
|
// new Object[]{pointerId, action, event.getX(), event.getY(), event.getPointerCount()});
|
||||||
|
|
||||||
TouchEvent touchEvent;
|
TouchEvent touchEvent;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MotionEvent.ACTION_HOVER_ENTER:
|
case MotionEvent.ACTION_HOVER_ENTER:
|
||||||
jmeX = androidInput.getJmeX(event.getX(pointerIndex));
|
jmeX = getJmeX(event.getX(pointerIndex));
|
||||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
|
jmeY = invertY(getJmeY(event.getY(pointerIndex)));
|
||||||
touchEvent = androidInput.getFreeTouchEvent();
|
touchEvent = getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.HOVER_START, jmeX, jmeY, 0, 0);
|
touchEvent.set(TouchEvent.Type.HOVER_START, jmeX, jmeY, 0, 0);
|
||||||
touchEvent.setPointerId(pointerId);
|
touchEvent.setPointerId(pointerId);
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
@ -98,14 +81,14 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
|
|||||||
lastPos = new Vector2f(jmeX, jmeY);
|
lastPos = new Vector2f(jmeX, jmeY);
|
||||||
lastHoverPositions.put(pointerId, lastPos);
|
lastHoverPositions.put(pointerId, lastPos);
|
||||||
|
|
||||||
processEvent(touchEvent);
|
addEvent(touchEvent);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_HOVER_MOVE:
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
// Convert all pointers into events
|
// Convert all pointers into events
|
||||||
for (int p = 0; p < event.getPointerCount(); p++) {
|
for (int p = 0; p < event.getPointerCount(); p++) {
|
||||||
jmeX = androidInput.getJmeX(event.getX(p));
|
jmeX = getJmeX(event.getX(p));
|
||||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p)));
|
jmeY = invertY(getJmeY(event.getY(p)));
|
||||||
lastPos = lastHoverPositions.get(event.getPointerId(p));
|
lastPos = lastHoverPositions.get(event.getPointerId(p));
|
||||||
if (lastPos == null) {
|
if (lastPos == null) {
|
||||||
lastPos = new Vector2f(jmeX, jmeY);
|
lastPos = new Vector2f(jmeX, jmeY);
|
||||||
@ -115,30 +98,30 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
|
|||||||
float dX = jmeX - lastPos.x;
|
float dX = jmeX - lastPos.x;
|
||||||
float dY = jmeY - lastPos.y;
|
float dY = jmeY - lastPos.y;
|
||||||
if (dX != 0 || dY != 0) {
|
if (dX != 0 || dY != 0) {
|
||||||
touchEvent = androidInput.getFreeTouchEvent();
|
touchEvent = getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.HOVER_MOVE, jmeX, jmeY, dX, dY);
|
touchEvent.set(TouchEvent.Type.HOVER_MOVE, jmeX, jmeY, dX, dY);
|
||||||
touchEvent.setPointerId(event.getPointerId(p));
|
touchEvent.setPointerId(event.getPointerId(p));
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
touchEvent.setPressure(event.getPressure(p));
|
touchEvent.setPressure(event.getPressure(p));
|
||||||
lastPos.set(jmeX, jmeY);
|
lastPos.set(jmeX, jmeY);
|
||||||
|
|
||||||
processEvent(touchEvent);
|
addEvent(touchEvent);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_HOVER_EXIT:
|
case MotionEvent.ACTION_HOVER_EXIT:
|
||||||
jmeX = androidInput.getJmeX(event.getX(pointerIndex));
|
jmeX = getJmeX(event.getX(pointerIndex));
|
||||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
|
jmeY = invertY(getJmeY(event.getY(pointerIndex)));
|
||||||
touchEvent = androidInput.getFreeTouchEvent();
|
touchEvent = getFreeTouchEvent();
|
||||||
touchEvent.set(TouchEvent.Type.HOVER_END, jmeX, jmeY, 0, 0);
|
touchEvent.set(TouchEvent.Type.HOVER_END, jmeX, jmeY, 0, 0);
|
||||||
touchEvent.setPointerId(pointerId);
|
touchEvent.setPointerId(pointerId);
|
||||||
touchEvent.setTime(event.getEventTime());
|
touchEvent.setTime(event.getEventTime());
|
||||||
touchEvent.setPressure(event.getPressure(pointerIndex));
|
touchEvent.setPressure(event.getPressure(pointerIndex));
|
||||||
lastHoverPositions.remove(pointerId);
|
lastHoverPositions.remove(pointerId);
|
||||||
|
|
||||||
processEvent(touchEvent);
|
addEvent(touchEvent);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -147,6 +130,7 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -35,13 +35,17 @@ import android.opengl.GLES20;
|
|||||||
import com.jme3.renderer.RendererException;
|
import com.jme3.renderer.RendererException;
|
||||||
import com.jme3.renderer.opengl.GL;
|
import com.jme3.renderer.opengl.GL;
|
||||||
import com.jme3.renderer.opengl.GLExt;
|
import com.jme3.renderer.opengl.GLExt;
|
||||||
|
import com.jme3.renderer.opengl.GLFbo;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
public class AndroidGL implements GL, GLExt {
|
public class AndroidGL implements GL, GLExt, GLFbo {
|
||||||
|
|
||||||
|
public void resetStats() {
|
||||||
|
}
|
||||||
|
|
||||||
private static int getLimitBytes(ByteBuffer buffer) {
|
private static int getLimitBytes(ByteBuffer buffer) {
|
||||||
checkLimit(buffer);
|
checkLimit(buffer);
|
||||||
|
@ -47,13 +47,14 @@ import android.widget.EditText;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import com.jme3.input.*;
|
import com.jme3.input.*;
|
||||||
import com.jme3.input.android.AndroidInputHandler;
|
import com.jme3.input.android.AndroidInputHandler;
|
||||||
import com.jme3.input.android.AndroidJoyInputHandler;
|
import com.jme3.input.android.AndroidInputHandler14;
|
||||||
import com.jme3.input.controls.SoftTextDialogInputListener;
|
import com.jme3.input.controls.SoftTextDialogInputListener;
|
||||||
import com.jme3.input.dummy.DummyKeyInput;
|
import com.jme3.input.dummy.DummyKeyInput;
|
||||||
import com.jme3.input.dummy.DummyMouseInput;
|
import com.jme3.input.dummy.DummyMouseInput;
|
||||||
import com.jme3.renderer.android.AndroidGL;
|
import com.jme3.renderer.android.AndroidGL;
|
||||||
import com.jme3.renderer.opengl.GL;
|
import com.jme3.renderer.opengl.GL;
|
||||||
import com.jme3.renderer.opengl.GLExt;
|
import com.jme3.renderer.opengl.GLExt;
|
||||||
|
import com.jme3.renderer.opengl.GLFbo;
|
||||||
import com.jme3.renderer.opengl.GLRenderer;
|
import com.jme3.renderer.opengl.GLRenderer;
|
||||||
import com.jme3.system.*;
|
import com.jme3.system.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@ -75,7 +76,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
protected SystemListener listener;
|
protected SystemListener listener;
|
||||||
protected boolean autoFlush = true;
|
protected boolean autoFlush = true;
|
||||||
protected AndroidInputHandler androidInput;
|
protected AndroidInputHandler androidInput;
|
||||||
protected AndroidJoyInputHandler androidJoyInput = null;
|
|
||||||
protected long minFrameDuration = 0; // No FPS cap
|
protected long minFrameDuration = 0; // No FPS cap
|
||||||
protected long lastUpdateTime = 0;
|
protected long lastUpdateTime = 0;
|
||||||
|
|
||||||
@ -111,18 +111,17 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
|
|
||||||
// Start to set up the view
|
// Start to set up the view
|
||||||
GLSurfaceView view = new GLSurfaceView(context);
|
GLSurfaceView view = new GLSurfaceView(context);
|
||||||
|
logger.log(Level.INFO, "Android Build Version: {0}", Build.VERSION.SDK_INT);
|
||||||
if (androidInput == null) {
|
if (androidInput == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 14) {
|
||||||
|
androidInput = new AndroidInputHandler14();
|
||||||
|
} else if (Build.VERSION.SDK_INT >= 9){
|
||||||
androidInput = new AndroidInputHandler();
|
androidInput = new AndroidInputHandler();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
androidInput.setView(view);
|
androidInput.setView(view);
|
||||||
androidInput.loadSettings(settings);
|
androidInput.loadSettings(settings);
|
||||||
|
|
||||||
if (androidJoyInput == null) {
|
|
||||||
androidJoyInput = new AndroidJoyInputHandler();
|
|
||||||
}
|
|
||||||
androidJoyInput.setView(view);
|
|
||||||
androidJoyInput.loadSettings(settings);
|
|
||||||
|
|
||||||
// setEGLContextClientVersion must be set before calling setRenderer
|
// setEGLContextClientVersion must be set before calling setRenderer
|
||||||
// this means it cannot be set in AndroidConfigChooser (too late)
|
// this means it cannot be set in AndroidConfigChooser (too late)
|
||||||
view.setEGLContextClientVersion(2);
|
view.setEGLContextClientVersion(2);
|
||||||
@ -198,7 +197,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
Object gl = new AndroidGL();
|
Object gl = new AndroidGL();
|
||||||
// gl = GLTracer.createGlesTracer((GL)gl, (GLExt)gl);
|
// gl = GLTracer.createGlesTracer((GL)gl, (GLExt)gl);
|
||||||
// gl = new GLDebugES((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();
|
renderer.initialize();
|
||||||
|
|
||||||
JmeSystem.setSoftTextDialogInput(this);
|
JmeSystem.setSoftTextDialogInput(this);
|
||||||
@ -235,9 +234,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
if (androidInput != null) {
|
if (androidInput != null) {
|
||||||
androidInput.loadSettings(settings);
|
androidInput.loadSettings(settings);
|
||||||
}
|
}
|
||||||
if (androidJoyInput != null) {
|
|
||||||
androidJoyInput.loadSettings(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.getFrameRate() > 0) {
|
if (settings.getFrameRate() > 0) {
|
||||||
minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms
|
minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms
|
||||||
@ -274,12 +270,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JoyInput getJoyInput() {
|
public JoyInput getJoyInput() {
|
||||||
return androidJoyInput;
|
return androidInput.getJoyInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TouchInput getTouchInput() {
|
public TouchInput getTouchInput() {
|
||||||
return androidInput;
|
return androidInput.getTouchInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -529,6 +529,18 @@ extern "C" {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_setSolverNumIterations
|
||||||
|
(JNIEnv *env, jobject object, jlong spaceId, jint value) {
|
||||||
|
jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
|
||||||
|
if (space == NULL) {
|
||||||
|
jclass newExc = env->FindClass("java/lang/NullPointerException");
|
||||||
|
env->ThrowNew(newExc, "The physics space does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
space->getDynamicsWorld()->getSolverInfo().m_numIterations = value;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -174,6 +174,14 @@ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative
|
|||||||
JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_sweepTest_1native
|
JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_sweepTest_1native
|
||||||
(JNIEnv *, jobject, jlong, jobject, jobject, jlong, jobject, jfloat);
|
(JNIEnv *, jobject, jlong, jobject, jobject, jlong, jobject, jfloat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_jme3_bullet_PhysicsSpace
|
||||||
|
* Method: setSolverNumIterations
|
||||||
|
* Signature: (JI)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_setSolverNumIterations
|
||||||
|
(JNIEnv *, jobject, jlong, jint);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -226,19 +226,9 @@ public class BulletAppState implements AppState, PhysicsTickListener {
|
|||||||
if (debugEnabled && debugAppState == null && pSpace != null) {
|
if (debugEnabled && debugAppState == null && pSpace != null) {
|
||||||
debugAppState = new BulletDebugAppState(pSpace);
|
debugAppState = new BulletDebugAppState(pSpace);
|
||||||
stateManager.attach(debugAppState);
|
stateManager.attach(debugAppState);
|
||||||
pSpace.enableDebug(app.getAssetManager());
|
|
||||||
} else if (!debugEnabled && debugAppState != null) {
|
} else if (!debugEnabled && debugAppState != null) {
|
||||||
stateManager.detach(debugAppState);
|
stateManager.detach(debugAppState);
|
||||||
debugAppState = null;
|
debugAppState = null;
|
||||||
if (pSpace != null) {
|
|
||||||
pSpace.enableDebug(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO: remove when deprecation of PhysicsSpace.enableDebug is through
|
|
||||||
if (pSpace.getDebugManager() != null && !debugEnabled) {
|
|
||||||
debugEnabled = true;
|
|
||||||
} else if (pSpace.getDebugManager() == null && debugEnabled) {
|
|
||||||
debugEnabled = false;
|
|
||||||
}
|
}
|
||||||
if (!active) {
|
if (!active) {
|
||||||
return;
|
return;
|
||||||
|
@ -195,7 +195,7 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* When set to true, the physics coordinates will be applied to the local
|
* When set to true, the physics coordinates will be applied to the local
|
||||||
* translation of the Spatial instead of the world traslation.
|
* translation of the Spatial instead of the world translation.
|
||||||
* @param applyPhysicsLocal
|
* @param applyPhysicsLocal
|
||||||
*/
|
*/
|
||||||
public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
|
public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
|
||||||
|
@ -102,7 +102,7 @@ public class PhysicsSpace {
|
|||||||
private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
|
private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
|
||||||
private float accuracy = 1f / 60f;
|
private float accuracy = 1f / 60f;
|
||||||
private int maxSubSteps = 4, rayTestFlags = 1 << 2;
|
private int maxSubSteps = 4, rayTestFlags = 1 << 2;
|
||||||
private AssetManager debugManager;
|
private int solverNumIterations = 10;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// System.loadLibrary("bulletjme");
|
// System.loadLibrary("bulletjme");
|
||||||
@ -960,27 +960,27 @@ public class PhysicsSpace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable debug display for physics.
|
* Set the number of iterations used by the contact solver.
|
||||||
*
|
*
|
||||||
* @deprecated in favor of BulletDebugAppState, use
|
* The default is 10. Use 4 for low quality, 20 for high quality.
|
||||||
* <code>BulletAppState.setDebugEnabled(boolean)</code> to add automatically
|
*
|
||||||
* @param manager AssetManager to use to create debug materials
|
* @param numIterations The number of iterations used by the contact & constraint solver.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
public void setSolverNumIterations(int numIterations) {
|
||||||
public void enableDebug(AssetManager manager) {
|
this.solverNumIterations = numIterations;
|
||||||
debugManager = manager;
|
setSolverNumIterations(physicsSpaceId, numIterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable debug display
|
* Get the number of iterations used by the contact solver.
|
||||||
|
*
|
||||||
|
* @return The number of iterations used by the contact & constraint solver.
|
||||||
*/
|
*/
|
||||||
public void disableDebug() {
|
public int getSolverNumIterations() {
|
||||||
debugManager = null;
|
return solverNumIterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssetManager getDebugManager() {
|
private static native void setSolverNumIterations(long physicsSpaceId, int numIterations);
|
||||||
return debugManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static native void initNativePhysics();
|
public static native void initNativePhysics();
|
||||||
|
|
||||||
|
@ -313,6 +313,26 @@ public final class Bone implements Savable {
|
|||||||
return modelBindInverseScale;
|
return modelBindInverseScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Transform getModelBindInverseTransform() {
|
||||||
|
Transform t = new Transform();
|
||||||
|
t.setTranslation(modelBindInversePos);
|
||||||
|
t.setRotation(modelBindInverseRot);
|
||||||
|
if (modelBindInverseScale != null) {
|
||||||
|
t.setScale(modelBindInverseScale);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform getBindInverseTransform() {
|
||||||
|
Transform t = new Transform();
|
||||||
|
t.setTranslation(bindPos);
|
||||||
|
t.setRotation(bindRot);
|
||||||
|
if (bindScale != null) {
|
||||||
|
t.setScale(bindScale);
|
||||||
|
}
|
||||||
|
return t.invert();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use {@link #getBindPosition()}
|
* @deprecated use {@link #getBindPosition()}
|
||||||
*/
|
*/
|
||||||
|
@ -82,7 +82,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
/**
|
/**
|
||||||
* User wishes to use hardware skinning if available.
|
* User wishes to use hardware skinning if available.
|
||||||
*/
|
*/
|
||||||
private transient boolean hwSkinningDesired = false;
|
private transient boolean hwSkinningDesired = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hardware skinning is currently being used.
|
* Hardware skinning is currently being used.
|
||||||
@ -347,10 +347,21 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
|
|||||||
|
|
||||||
public Control cloneForSpatial(Spatial spatial) {
|
public Control cloneForSpatial(Spatial spatial) {
|
||||||
Node clonedNode = (Node) spatial;
|
Node clonedNode = (Node) spatial;
|
||||||
AnimControl ctrl = spatial.getControl(AnimControl.class);
|
|
||||||
SkeletonControl clone = new SkeletonControl();
|
SkeletonControl clone = new SkeletonControl();
|
||||||
|
|
||||||
|
AnimControl ctrl = spatial.getControl(AnimControl.class);
|
||||||
|
if (ctrl != null) {
|
||||||
|
// AnimControl is responsible for cloning the skeleton, not
|
||||||
|
// SkeletonControl.
|
||||||
clone.skeleton = ctrl.getSkeleton();
|
clone.skeleton = ctrl.getSkeleton();
|
||||||
|
} else {
|
||||||
|
// If there's no AnimControl, create the clone ourselves.
|
||||||
|
clone.skeleton = new Skeleton(skeleton);
|
||||||
|
}
|
||||||
|
clone.hwSkinningDesired = this.hwSkinningDesired;
|
||||||
|
clone.hwSkinningEnabled = this.hwSkinningEnabled;
|
||||||
|
clone.hwSkinningSupported = this.hwSkinningSupported;
|
||||||
|
clone.hwSkinningTested = this.hwSkinningTested;
|
||||||
|
|
||||||
clone.setSpatial(clonedNode);
|
clone.setSpatial(clonedNode);
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ import com.jme3.scene.control.Control;
|
|||||||
*/
|
*/
|
||||||
public class StatsView extends Node implements Control {
|
public class StatsView extends Node implements Control {
|
||||||
|
|
||||||
private BitmapText[] labels;
|
private BitmapText statText;
|
||||||
private Statistics statistics;
|
private Statistics statistics;
|
||||||
|
|
||||||
private String[] statLabels;
|
private String[] statLabels;
|
||||||
@ -81,20 +81,17 @@ public class StatsView extends Node implements Control {
|
|||||||
|
|
||||||
statLabels = statistics.getLabels();
|
statLabels = statistics.getLabels();
|
||||||
statData = new int[statLabels.length];
|
statData = new int[statLabels.length];
|
||||||
labels = new BitmapText[statLabels.length];
|
|
||||||
|
|
||||||
BitmapFont font = manager.loadFont("Interface/Fonts/Console.fnt");
|
BitmapFont font = manager.loadFont("Interface/Fonts/Console.fnt");
|
||||||
for (int i = 0; i < labels.length; i++){
|
statText = new BitmapText(font);
|
||||||
labels[i] = new BitmapText(font);
|
statText.setLocalTranslation(0, statText.getLineHeight() * statLabels.length, 0);
|
||||||
labels[i].setLocalTranslation(0, labels[i].getLineHeight() * (i+1), 0);
|
attachChild(statText);
|
||||||
attachChild(labels[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
addControl(this);
|
addControl(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getHeight() {
|
public float getHeight() {
|
||||||
return labels[0].getLineHeight() * statLabels.length;
|
return statText.getLineHeight() * statLabels.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(float tpf) {
|
public void update(float tpf) {
|
||||||
@ -103,11 +100,14 @@ public class StatsView extends Node implements Control {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
statistics.getData(statData);
|
statistics.getData(statData);
|
||||||
for (int i = 0; i < labels.length; i++) {
|
|
||||||
stringBuilder.setLength(0);
|
stringBuilder.setLength(0);
|
||||||
stringBuilder.append(statLabels[i]).append(" = ").append(statData[i]);
|
|
||||||
labels[i].setText(stringBuilder);
|
// Need to walk through it backwards, as the first label
|
||||||
|
// should appear at the bottom, not the top.
|
||||||
|
for (int i = statLabels.length - 1; i >= 0; i--) {
|
||||||
|
stringBuilder.append(statLabels[i]).append(" = ").append(statData[i]).append('\n');
|
||||||
}
|
}
|
||||||
|
statText.setText(stringBuilder);
|
||||||
|
|
||||||
// Moved to ResetStatsState to make sure it is
|
// Moved to ResetStatsState to make sure it is
|
||||||
// done even if there is no StatsView or the StatsView
|
// done even if there is no StatsView or the StatsView
|
||||||
|
@ -77,7 +77,7 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped;
|
protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped;
|
||||||
protected transient volatile int channel = -1;
|
protected transient volatile int channel = -1;
|
||||||
protected Vector3f velocity = new Vector3f();
|
protected Vector3f velocity = new Vector3f();
|
||||||
protected boolean reverbEnabled = true;
|
protected boolean reverbEnabled = false;
|
||||||
protected float maxDistance = 200; // 200 meters
|
protected float maxDistance = 200; // 200 meters
|
||||||
protected float refDistance = 10; // 10 meters
|
protected float refDistance = 10; // 10 meters
|
||||||
protected Filter reverbFilter;
|
protected Filter reverbFilter;
|
||||||
@ -410,6 +410,14 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getPlaybackTime() {
|
||||||
|
if (channel >= 0)
|
||||||
|
return getRenderer().getSourcePlaybackTime(this);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public Vector3f getPosition() {
|
public Vector3f getPosition() {
|
||||||
return getWorldTranslation();
|
return getWorldTranslation();
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ public interface AudioRenderer {
|
|||||||
|
|
||||||
public void updateSourceParam(AudioSource src, AudioParam param);
|
public void updateSourceParam(AudioSource src, AudioParam param);
|
||||||
public void updateListenerParam(Listener listener, ListenerParam param);
|
public void updateListenerParam(Listener listener, ListenerParam param);
|
||||||
|
public float getSourcePlaybackTime(AudioSource src);
|
||||||
|
|
||||||
public void deleteFilter(Filter filter);
|
public void deleteFilter(Filter filter);
|
||||||
public void deleteAudioData(AudioData ad);
|
public void deleteAudioData(AudioData ad);
|
||||||
|
@ -96,6 +96,11 @@ public interface AudioSource {
|
|||||||
*/
|
*/
|
||||||
public float getTimeOffset();
|
public float getTimeOffset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current playback position of the source in seconds.
|
||||||
|
*/
|
||||||
|
public float getPlaybackTime();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The velocity of the audio source.
|
* @return The velocity of the audio source.
|
||||||
*
|
*
|
||||||
|
@ -54,6 +54,8 @@ public class AudioStream extends AudioData implements Closeable {
|
|||||||
protected boolean eof = false;
|
protected boolean eof = false;
|
||||||
protected int[] ids;
|
protected int[] ids;
|
||||||
|
|
||||||
|
protected int unqueuedBuffersBytes = 0;
|
||||||
|
|
||||||
public AudioStream() {
|
public AudioStream() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -196,10 +198,21 @@ public class AudioStream extends AudioData implements Closeable {
|
|||||||
return in instanceof SeekableStream;
|
return in instanceof SeekableStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getUnqueuedBufferBytes() {
|
||||||
|
return unqueuedBuffersBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnqueuedBufferBytes(int unqueuedBuffers) {
|
||||||
|
this.unqueuedBuffersBytes = unqueuedBuffers;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTime(float time) {
|
public void setTime(float time) {
|
||||||
if (in instanceof SeekableStream) {
|
if (in instanceof SeekableStream) {
|
||||||
((SeekableStream) in).setTime(time);
|
((SeekableStream) in).setTime(time);
|
||||||
eof = false;
|
eof = false;
|
||||||
|
|
||||||
|
// TODO: when we actually support seeking, this will need to be properly set.
|
||||||
|
unqueuedBuffersBytes = 0;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Cannot use setTime on a stream that "
|
"Cannot use setTime on a stream that "
|
||||||
|
@ -301,6 +301,58 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
|
|||||||
f.clearUpdateNeeded();
|
f.clearUpdateNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getSourcePlaybackTime(AudioSource src) {
|
||||||
|
checkDead();
|
||||||
|
synchronized (threadLock) {
|
||||||
|
if (audioDisabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See comment in updateSourceParam().
|
||||||
|
if (src.getChannel() < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = channels[src.getChannel()];
|
||||||
|
AudioData data = src.getAudioData();
|
||||||
|
int playbackOffsetBytes = 0;
|
||||||
|
|
||||||
|
if (data instanceof AudioStream) {
|
||||||
|
// Because audio streams are processed in buffer chunks,
|
||||||
|
// we have to compute the amount of time the stream was already
|
||||||
|
// been playing based on the number of buffers that were processed.
|
||||||
|
AudioStream stream = (AudioStream) data;
|
||||||
|
|
||||||
|
// NOTE: the assumption is that all enqueued buffers are the same size.
|
||||||
|
// this is currently enforced by fillBuffer().
|
||||||
|
|
||||||
|
// The number of unenqueued bytes that the decoder thread
|
||||||
|
// keeps track of.
|
||||||
|
int unqueuedBytes = stream.getUnqueuedBufferBytes();
|
||||||
|
|
||||||
|
// Additional processed buffers that the decoder thread
|
||||||
|
// did not unenqueue yet (it only updates 20 times per second).
|
||||||
|
int unqueuedBytesExtra = al.alGetSourcei(id, AL_BUFFERS_PROCESSED) * BUFFER_SIZE;
|
||||||
|
|
||||||
|
// Total additional bytes that need to be considered.
|
||||||
|
playbackOffsetBytes = unqueuedBytes; // + unqueuedBytesExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add byte offset from source (for both streams and buffers)
|
||||||
|
playbackOffsetBytes += al.alGetSourcei(id, AL_BYTE_OFFSET);
|
||||||
|
|
||||||
|
// Compute time value from bytes
|
||||||
|
// E.g. for 44100 source with 2 channels and 16 bits per sample:
|
||||||
|
// (44100 * 2 * 16 / 8) = 176400
|
||||||
|
int bytesPerSecond = (data.getSampleRate() *
|
||||||
|
data.getChannels() *
|
||||||
|
data.getBitsPerSample() / 8);
|
||||||
|
|
||||||
|
return (float)playbackOffsetBytes / bytesPerSecond;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateSourceParam(AudioSource src, AudioParam param) {
|
public void updateSourceParam(AudioSource src, AudioParam param) {
|
||||||
checkDead();
|
checkDead();
|
||||||
synchronized (threadLock) {
|
synchronized (threadLock) {
|
||||||
@ -648,6 +700,7 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
|
|||||||
private boolean fillStreamingSource(int sourceId, AudioStream stream, boolean looping) {
|
private boolean fillStreamingSource(int sourceId, AudioStream stream, boolean looping) {
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
int processed = al.alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);
|
int processed = al.alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);
|
||||||
|
int unqueuedBufferBytes = 0;
|
||||||
|
|
||||||
for (int i = 0; i < processed; i++) {
|
for (int i = 0; i < processed; i++) {
|
||||||
int buffer;
|
int buffer;
|
||||||
@ -656,6 +709,11 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
|
|||||||
al.alSourceUnqueueBuffers(sourceId, 1, ib);
|
al.alSourceUnqueueBuffers(sourceId, 1, ib);
|
||||||
buffer = ib.get(0);
|
buffer = ib.get(0);
|
||||||
|
|
||||||
|
// XXX: assume that reading from AudioStream always
|
||||||
|
// gives BUFFER_SIZE amount of bytes! This might not always
|
||||||
|
// be the case...
|
||||||
|
unqueuedBufferBytes += BUFFER_SIZE;
|
||||||
|
|
||||||
boolean active = fillBuffer(stream, buffer);
|
boolean active = fillBuffer(stream, buffer);
|
||||||
|
|
||||||
if (!active && !stream.isEOF()) {
|
if (!active && !stream.isEOF()) {
|
||||||
@ -683,6 +741,8 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream.setUnqueuedBufferBytes(stream.getUnqueuedBufferBytes() + unqueuedBufferBytes);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,22 +46,18 @@ public interface JmeExporter {
|
|||||||
*
|
*
|
||||||
* @param object The savable to export
|
* @param object The savable to export
|
||||||
* @param f The output stream
|
* @param f The output stream
|
||||||
* @return Always returns true. If an error occurs during export,
|
|
||||||
* an exception is thrown
|
|
||||||
* @throws IOException If an io exception occurs during export
|
* @throws IOException If an io exception occurs during export
|
||||||
*/
|
*/
|
||||||
public boolean save(Savable object, OutputStream f) throws IOException;
|
public void save(Savable object, OutputStream f) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export the {@link Savable} to a file.
|
* Export the {@link Savable} to a file.
|
||||||
*
|
*
|
||||||
* @param object The savable to export
|
* @param object The savable to export
|
||||||
* @param f The file to export to
|
* @param f The file to export to
|
||||||
* @return Always returns true. If an error occurs during export,
|
|
||||||
* an exception is thrown
|
|
||||||
* @throws IOException If an io exception occurs during export
|
* @throws IOException If an io exception occurs during export
|
||||||
*/
|
*/
|
||||||
public boolean save(Savable object, File f) throws IOException;
|
public void save(Savable object, File f) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link OutputCapsule} for the given savable object.
|
* Returns the {@link OutputCapsule} for the given savable object.
|
||||||
|
@ -607,28 +607,28 @@ public final class ColorRGBA implements Savable, Cloneable, java.io.Serializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the color in sRGB color space as a Vector4f
|
* Get the color in sRGB color space as a <code>ColorRGBA</code>.
|
||||||
*
|
*
|
||||||
* Note that linear values stored in the ColorRGBA will be gamma corrected
|
* Note that linear values stored in the ColorRGBA will be gamma corrected
|
||||||
* and returned as a Vector4f
|
* and returned as a ColorRGBA.
|
||||||
* the x atribute will be fed with the r channel in sRGB space
|
|
||||||
* the y atribute will be fed with the g channel in sRGB space
|
|
||||||
* the z atribute will be fed with the b channel in sRGB space
|
|
||||||
* the w atribute will be fed with the a channel
|
|
||||||
*
|
*
|
||||||
* Note that no correction will be performed on the alpha channel as it's
|
* The x attribute will be fed with the r channel in sRGB space.
|
||||||
* conventionnally doesn't represent a color itself
|
* The y attribute will be fed with the g channel in sRGB space.
|
||||||
|
* The z attribute will be fed with the b channel in sRGB space.
|
||||||
|
* The w attribute will be fed with the a channel.
|
||||||
*
|
*
|
||||||
* @return the color in sRGB color space as a Vector4f
|
* Note that no correction will be performed on the alpha channel as it
|
||||||
|
* conventionally doesn't represent a color itself.
|
||||||
|
*
|
||||||
|
* @return the color in sRGB color space as a ColorRGBA.
|
||||||
*/
|
*/
|
||||||
public Vector4f getAsSrgb(){
|
public ColorRGBA getAsSrgb() {
|
||||||
Vector4f srgb = new Vector4f();
|
ColorRGBA srgb = new ColorRGBA();
|
||||||
float invGama = 1f / GAMMA;
|
float invGama = 1f / GAMMA;
|
||||||
srgb.x = (float)Math.pow(r, invGama);
|
srgb.r = (float) Math.pow(r, invGama);
|
||||||
srgb.y = (float)Math.pow(g, invGama);
|
srgb.g = (float) Math.pow(g, invGama);
|
||||||
srgb.z = (float)Math.pow(b, invGama);
|
srgb.b = (float) Math.pow(b, invGama);
|
||||||
srgb.w = a;
|
srgb.a = a;
|
||||||
|
|
||||||
return srgb;
|
return srgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,6 +902,25 @@ final public class FastMath {
|
|||||||
return clamp(input, 0f, 1f);
|
return clamp(input, 0f, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if two floats are approximately equal.
|
||||||
|
* This takes into account the magnitude of the floats, since
|
||||||
|
* large numbers will have larger differences be close to each other.
|
||||||
|
*
|
||||||
|
* Should return true for a=100000, b=100001, but false for a=10000, b=10001.
|
||||||
|
*
|
||||||
|
* @param a The first float to compare
|
||||||
|
* @param b The second float to compare
|
||||||
|
* @return True if a and b are approximately equal, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean approximateEquals(float a, float b) {
|
||||||
|
if (a == b) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return (abs(a - b) / Math.max(abs(a), abs(b))) <= 0.00001f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a single precision (32 bit) floating point value
|
* Converts a single precision (32 bit) floating point value
|
||||||
* into half precision (16 bit).
|
* into half precision (16 bit).
|
||||||
|
@ -259,6 +259,26 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
|
|||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Matrix4f toTransformMatrix() {
|
||||||
|
Matrix4f trans = new Matrix4f();
|
||||||
|
trans.setTranslation(translation);
|
||||||
|
trans.setRotationQuaternion(rot);
|
||||||
|
trans.setScale(scale);
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fromTransformMatrix(Matrix4f mat) {
|
||||||
|
translation.set(mat.toTranslationVector());
|
||||||
|
rot.set(mat.toRotationQuat());
|
||||||
|
scale.set(mat.toScaleVector());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform invert() {
|
||||||
|
Transform t = new Transform();
|
||||||
|
t.fromTransformMatrix(toTransformMatrix().invertLocal());
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
|
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
|
||||||
*/
|
*/
|
||||||
|
@ -337,7 +337,19 @@ public enum Caps {
|
|||||||
* <p>
|
* <p>
|
||||||
* Improves the quality of environment mapping.
|
* Improves the quality of environment mapping.
|
||||||
*/
|
*/
|
||||||
SeamlessCubemap;
|
SeamlessCubemap,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Running with OpenGL 3.2+ core profile.
|
||||||
|
*
|
||||||
|
* Compatibility features will not be available.
|
||||||
|
*/
|
||||||
|
CoreProfile,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GPU can provide and accept binary shaders.
|
||||||
|
*/
|
||||||
|
BinaryShader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if given the renderer capabilities, the texture
|
* Returns true if given the renderer capabilities, the texture
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
package com.jme3.renderer.opengl;
|
package com.jme3.renderer.opengl;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.DoubleBuffer;
|
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
@ -178,6 +177,8 @@ public interface GL {
|
|||||||
public static final int GL_VERTEX_SHADER = 0x8B31;
|
public static final int GL_VERTEX_SHADER = 0x8B31;
|
||||||
public static final int GL_ZERO = 0x0;
|
public static final int GL_ZERO = 0x0;
|
||||||
|
|
||||||
|
public void resetStats();
|
||||||
|
|
||||||
public void glActiveTexture(int texture);
|
public void glActiveTexture(int texture);
|
||||||
public void glAttachShader(int program, int shader);
|
public void glAttachShader(int program, int shader);
|
||||||
public void glBindBuffer(int target, int buffer);
|
public void glBindBuffer(int target, int buffer);
|
||||||
|
@ -42,7 +42,11 @@ public interface GL3 extends GL2 {
|
|||||||
|
|
||||||
public static final int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A;
|
public static final int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A;
|
||||||
public static final int GL_GEOMETRY_SHADER = 0x8DD9;
|
public static final int GL_GEOMETRY_SHADER = 0x8DD9;
|
||||||
|
public static final int GL_NUM_EXTENSIONS = 0x821D;
|
||||||
|
|
||||||
public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+
|
public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+
|
||||||
public void glBindVertexArray(int param1); /// GL3+
|
public void glBindVertexArray(int param1); /// GL3+
|
||||||
|
public void glDeleteVertexArrays(IntBuffer arrays); /// GL3+
|
||||||
public void glGenVertexArrays(IntBuffer param1); /// GL3+
|
public void glGenVertexArrays(IntBuffer param1); /// GL3+
|
||||||
|
public String glGetString(int param1, int param2); /// GL3+
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,17 @@ package com.jme3.renderer.opengl;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
public class GLDebugDesktop extends GLDebugES implements GL2, GL3 {
|
public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
|
||||||
|
|
||||||
private final GL2 gl2;
|
private final GL2 gl2;
|
||||||
private final GL3 gl3;
|
private final GL3 gl3;
|
||||||
|
private final GL4 gl4;
|
||||||
|
|
||||||
public GLDebugDesktop(GL gl, GLFbo glfbo) {
|
public GLDebugDesktop(GL gl, GLExt glext, GLFbo glfbo) {
|
||||||
super(gl, glfbo);
|
super(gl, glext, glfbo);
|
||||||
this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
|
this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
|
||||||
this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
|
this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
|
||||||
|
this.gl4 = gl instanceof GL4 ? (GL4) gl : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void glAlphaFunc(int func, float ref) {
|
public void glAlphaFunc(int func, float ref) {
|
||||||
@ -74,4 +76,22 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3 {
|
|||||||
checkError();
|
checkError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String glGetString(int param1, int param2) {
|
||||||
|
String result = gl3.glGetString(param1, param2);
|
||||||
|
checkError();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void glDeleteVertexArrays(IntBuffer arrays) {
|
||||||
|
gl3.glDeleteVertexArrays(arrays);
|
||||||
|
checkError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void glPatchParameter(int count) {
|
||||||
|
gl4.glPatchParameter(count);
|
||||||
|
checkError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,14 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
|
|||||||
private final GLFbo glfbo;
|
private final GLFbo glfbo;
|
||||||
private final GLExt glext;
|
private final GLExt glext;
|
||||||
|
|
||||||
public GLDebugES(GL gl, GLFbo glfbo) {
|
public GLDebugES(GL gl, GLExt glext, GLFbo glfbo) {
|
||||||
this.gl = gl;
|
this.gl = gl;
|
||||||
// this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
|
this.glext = glext;
|
||||||
// this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
|
|
||||||
this.glfbo = glfbo;
|
this.glfbo = glfbo;
|
||||||
this.glext = glfbo instanceof GLExt ? (GLExt) glfbo : null;
|
}
|
||||||
|
|
||||||
|
public void resetStats() {
|
||||||
|
gl.resetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void glActiveTexture(int texture) {
|
public void glActiveTexture(int texture) {
|
||||||
@ -478,7 +480,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
||||||
glext.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||||
checkError();
|
checkError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +527,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
|
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
|
||||||
glext.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
|
glfbo.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
|
||||||
checkError();
|
checkError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ import java.nio.IntBuffer;
|
|||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public interface GLExt extends GLFbo {
|
public interface GLExt {
|
||||||
|
|
||||||
public static final int GL_ALREADY_SIGNALED = 0x911A;
|
public static final int GL_ALREADY_SIGNALED = 0x911A;
|
||||||
public static final int GL_COMPRESSED_RGB8_ETC2 = 0x9274;
|
public static final int GL_COMPRESSED_RGB8_ETC2 = 0x9274;
|
||||||
@ -100,7 +100,6 @@ public interface GLExt extends GLFbo {
|
|||||||
public static final int GL_UNSIGNED_INT_5_9_9_9_REV_EXT = 0x8C3E;
|
public static final int GL_UNSIGNED_INT_5_9_9_9_REV_EXT = 0x8C3E;
|
||||||
public static final int GL_WAIT_FAILED = 0x911D;
|
public static final int GL_WAIT_FAILED = 0x911D;
|
||||||
|
|
||||||
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
|
|
||||||
public void glBufferData(int target, IntBuffer data, int usage);
|
public void glBufferData(int target, IntBuffer data, int usage);
|
||||||
public void glBufferSubData(int target, long offset, IntBuffer data);
|
public void glBufferSubData(int target, long offset, IntBuffer data);
|
||||||
public int glClientWaitSync(Object sync, int flags, long timeout);
|
public int glClientWaitSync(Object sync, int flags, long timeout);
|
||||||
@ -110,7 +109,6 @@ public interface GLExt extends GLFbo {
|
|||||||
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount);
|
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount);
|
||||||
public Object glFenceSync(int condition, int flags);
|
public Object glFenceSync(int condition, int flags);
|
||||||
public void glGetMultisample(int pname, int index, FloatBuffer val);
|
public void glGetMultisample(int pname, int index, FloatBuffer val);
|
||||||
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height);
|
|
||||||
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations);
|
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations);
|
||||||
public void glVertexAttribDivisorARB(int index, int divisor);
|
public void glVertexAttribDivisorARB(int index, int divisor);
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ public interface GLFbo {
|
|||||||
|
|
||||||
public void glBindFramebufferEXT(int param1, int param2);
|
public void glBindFramebufferEXT(int param1, int param2);
|
||||||
public void glBindRenderbufferEXT(int param1, int param2);
|
public void glBindRenderbufferEXT(int param1, int param2);
|
||||||
|
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
|
||||||
public int glCheckFramebufferStatusEXT(int param1);
|
public int glCheckFramebufferStatusEXT(int param1);
|
||||||
public void glDeleteFramebuffersEXT(IntBuffer param1);
|
public void glDeleteFramebuffersEXT(IntBuffer param1);
|
||||||
public void glDeleteRenderbuffersEXT(IntBuffer param1);
|
public void glDeleteRenderbuffersEXT(IntBuffer param1);
|
||||||
@ -92,5 +93,5 @@ public interface GLFbo {
|
|||||||
public void glGenRenderbuffersEXT(IntBuffer param1);
|
public void glGenRenderbuffersEXT(IntBuffer param1);
|
||||||
public void glGenerateMipmapEXT(int param1);
|
public void glGenerateMipmapEXT(int param1);
|
||||||
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4);
|
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4);
|
||||||
|
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height);
|
||||||
}
|
}
|
||||||
|
@ -89,9 +89,11 @@ public final class GLImageFormats {
|
|||||||
GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length];
|
GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length];
|
||||||
|
|
||||||
if (caps.contains(Caps.OpenGL20)) {
|
if (caps.contains(Caps.OpenGL20)) {
|
||||||
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.Luminance8, GL2.GL_LUMINANCE8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Luminance8, GL2.GL_LUMINANCE8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
|
}
|
||||||
format(formatToGL, Format.RGB8, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.RGB8, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.RGB565, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
|
format(formatToGL, Format.RGB565, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
|
||||||
@ -108,8 +110,10 @@ public final class GLImageFormats {
|
|||||||
formatSrgb(formatToGL, Format.RGB565, GLExt.GL_SRGB8_EXT, GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
|
formatSrgb(formatToGL, Format.RGB565, GLExt.GL_SRGB8_EXT, GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
|
||||||
formatSrgb(formatToGL, Format.RGB5A1, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
|
formatSrgb(formatToGL, Format.RGB5A1, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
|
||||||
formatSrgb(formatToGL, Format.RGBA8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
formatSrgb(formatToGL, Format.RGBA8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
||||||
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
formatSrgb(formatToGL, Format.Luminance8, GLExt.GL_SLUMINANCE8_EXT, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
formatSrgb(formatToGL, Format.Luminance8, GLExt.GL_SLUMINANCE8_EXT, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
||||||
formatSrgb(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SLUMINANCE8_ALPHA8_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
formatSrgb(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SLUMINANCE8_ALPHA8_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
|
}
|
||||||
formatSrgb(formatToGL, Format.BGR8, GLExt.GL_SRGB8_EXT, GL2.GL_BGR, GL.GL_UNSIGNED_BYTE);
|
formatSrgb(formatToGL, Format.BGR8, GLExt.GL_SRGB8_EXT, GL2.GL_BGR, GL.GL_UNSIGNED_BYTE);
|
||||||
formatSrgb(formatToGL, Format.ABGR8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8);
|
formatSrgb(formatToGL, Format.ABGR8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8);
|
||||||
formatSrgb(formatToGL, Format.ARGB8, GLExt.GL_SRGB8_ALPHA8_EXT, GL2.GL_BGRA, GL2.GL_UNSIGNED_INT_8_8_8_8);
|
formatSrgb(formatToGL, Format.ARGB8, GLExt.GL_SRGB8_ALPHA8_EXT, GL2.GL_BGRA, GL2.GL_UNSIGNED_INT_8_8_8_8);
|
||||||
@ -124,16 +128,20 @@ public final class GLImageFormats {
|
|||||||
}
|
}
|
||||||
} else if (caps.contains(Caps.Rgba8)) {
|
} else if (caps.contains(Caps.Rgba8)) {
|
||||||
// A more limited form of 32-bit RGBA. Only GL_RGBA8 is available.
|
// A more limited form of 32-bit RGBA. Only GL_RGBA8 is available.
|
||||||
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
format(formatToGL, Format.Alpha8, GLExt.GL_RGBA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Alpha8, GLExt.GL_RGBA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.Luminance8, GLExt.GL_RGBA8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Luminance8, GLExt.GL_RGBA8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
|
}
|
||||||
format(formatToGL, Format.RGB8, GLExt.GL_RGBA8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.RGB8, GLExt.GL_RGBA8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
||||||
} else {
|
} else {
|
||||||
// Actually, the internal format isn't used for OpenGL ES 2! This is the same as the above..
|
// Actually, the internal format isn't used for OpenGL ES 2! This is the same as the above..
|
||||||
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
format(formatToGL, Format.Alpha8, GL.GL_RGBA4, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Alpha8, GL.GL_RGBA4, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.Luminance8, GL.GL_RGB565, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Luminance8, GL.GL_RGB565, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.Luminance8Alpha8, GL.GL_RGBA4, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.Luminance8Alpha8, GL.GL_RGBA4, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
|
||||||
|
}
|
||||||
format(formatToGL, Format.RGB8, GL.GL_RGB565, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.RGB8, GL.GL_RGB565, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
|
||||||
format(formatToGL, Format.RGBA8, GL.GL_RGBA4, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
format(formatToGL, Format.RGBA8, GL.GL_RGBA4, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
|
||||||
}
|
}
|
||||||
@ -145,9 +153,11 @@ public final class GLImageFormats {
|
|||||||
format(formatToGL, Format.RGB5A1, GL.GL_RGB5_A1, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
|
format(formatToGL, Format.RGB5A1, GL.GL_RGB5_A1, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
|
||||||
|
|
||||||
if (caps.contains(Caps.FloatTexture)) {
|
if (caps.contains(Caps.FloatTexture)) {
|
||||||
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
format(formatToGL, Format.Luminance16F, GLExt.GL_LUMINANCE16F_ARB, GL.GL_LUMINANCE, GLExt.GL_HALF_FLOAT_ARB);
|
format(formatToGL, Format.Luminance16F, GLExt.GL_LUMINANCE16F_ARB, GL.GL_LUMINANCE, GLExt.GL_HALF_FLOAT_ARB);
|
||||||
format(formatToGL, Format.Luminance32F, GLExt.GL_LUMINANCE32F_ARB, GL.GL_LUMINANCE, GL.GL_FLOAT);
|
format(formatToGL, Format.Luminance32F, GLExt.GL_LUMINANCE32F_ARB, GL.GL_LUMINANCE, GL.GL_FLOAT);
|
||||||
format(formatToGL, Format.Luminance16FAlpha16F, GLExt.GL_LUMINANCE_ALPHA16F_ARB, GL.GL_LUMINANCE_ALPHA, GLExt.GL_HALF_FLOAT_ARB);
|
format(formatToGL, Format.Luminance16FAlpha16F, GLExt.GL_LUMINANCE_ALPHA16F_ARB, GL.GL_LUMINANCE_ALPHA, GLExt.GL_HALF_FLOAT_ARB);
|
||||||
|
}
|
||||||
format(formatToGL, Format.RGB16F, GLExt.GL_RGB16F_ARB, GL.GL_RGB, GLExt.GL_HALF_FLOAT_ARB);
|
format(formatToGL, Format.RGB16F, GLExt.GL_RGB16F_ARB, GL.GL_RGB, GLExt.GL_HALF_FLOAT_ARB);
|
||||||
format(formatToGL, Format.RGB32F, GLExt.GL_RGB32F_ARB, GL.GL_RGB, GL.GL_FLOAT);
|
format(formatToGL, Format.RGB32F, GLExt.GL_RGB32F_ARB, GL.GL_RGB, GL.GL_FLOAT);
|
||||||
format(formatToGL, Format.RGBA16F, GLExt.GL_RGBA16F_ARB, GL.GL_RGBA, GLExt.GL_HALF_FLOAT_ARB);
|
format(formatToGL, Format.RGBA16F, GLExt.GL_RGBA16F_ARB, GL.GL_RGBA, GLExt.GL_HALF_FLOAT_ARB);
|
||||||
|
@ -54,8 +54,10 @@ import com.jme3.texture.Texture;
|
|||||||
import com.jme3.texture.Texture.WrapAxis;
|
import com.jme3.texture.Texture.WrapAxis;
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
import com.jme3.util.ListMap;
|
import com.jme3.util.ListMap;
|
||||||
|
import com.jme3.util.MipMapGenerator;
|
||||||
import com.jme3.util.NativeObjectManager;
|
import com.jme3.util.NativeObjectManager;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -112,13 +114,13 @@ public class GLRenderer implements Renderer {
|
|||||||
private final GLFbo glfbo;
|
private final GLFbo glfbo;
|
||||||
private final TextureUtil texUtil;
|
private final TextureUtil texUtil;
|
||||||
|
|
||||||
public GLRenderer(GL gl, GLFbo glfbo) {
|
public GLRenderer(GL gl, GLExt glext, GLFbo glfbo) {
|
||||||
this.gl = gl;
|
this.gl = gl;
|
||||||
this.gl2 = gl instanceof GL2 ? (GL2)gl : null;
|
this.gl2 = gl instanceof GL2 ? (GL2)gl : null;
|
||||||
this.gl3 = gl instanceof GL3 ? (GL3)gl : null;
|
this.gl3 = gl instanceof GL3 ? (GL3)gl : null;
|
||||||
this.gl4 = gl instanceof GL4 ? (GL4)gl : null;
|
this.gl4 = gl instanceof GL4 ? (GL4)gl : null;
|
||||||
this.glfbo = glfbo;
|
this.glfbo = glfbo;
|
||||||
this.glext = glfbo instanceof GLExt ? (GLExt)glfbo : null;
|
this.glext = glext;
|
||||||
this.texUtil = new TextureUtil(gl, gl2, glext, context);
|
this.texUtil = new TextureUtil(gl, gl2, glext, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,11 +139,20 @@ public class GLRenderer implements Renderer {
|
|||||||
return limits;
|
return limits;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HashSet<String> loadExtensions(String extensions) {
|
private HashSet<String> loadExtensions() {
|
||||||
HashSet<String> extensionSet = new HashSet<String>(64);
|
HashSet<String> extensionSet = new HashSet<String>(64);
|
||||||
for (String extension : extensions.split(" ")) {
|
if (caps.contains(Caps.OpenGL30)) {
|
||||||
|
// If OpenGL3+ is available, use the non-deprecated way
|
||||||
|
// of getting supported extensions.
|
||||||
|
gl3.glGetInteger(GL3.GL_NUM_EXTENSIONS, intBuf16);
|
||||||
|
int extensionCount = intBuf16.get(0);
|
||||||
|
for (int i = 0; i < extensionCount; i++) {
|
||||||
|
String extension = gl3.glGetString(GL.GL_EXTENSIONS, i);
|
||||||
extensionSet.add(extension);
|
extensionSet.add(extension);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
extensionSet.addAll(Arrays.asList(gl.glGetString(GL.GL_EXTENSIONS).split(" ")));
|
||||||
|
}
|
||||||
return extensionSet;
|
return extensionSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,10 +196,12 @@ public class GLRenderer implements Renderer {
|
|||||||
caps.add(Caps.OpenGL31);
|
caps.add(Caps.OpenGL31);
|
||||||
if (oglVer >= 320) {
|
if (oglVer >= 320) {
|
||||||
caps.add(Caps.OpenGL32);
|
caps.add(Caps.OpenGL32);
|
||||||
}if(oglVer>=330){
|
}
|
||||||
|
if (oglVer >= 330) {
|
||||||
caps.add(Caps.OpenGL33);
|
caps.add(Caps.OpenGL33);
|
||||||
caps.add(Caps.GeometryShader);
|
caps.add(Caps.GeometryShader);
|
||||||
}if(oglVer>=400){
|
}
|
||||||
|
if (oglVer >= 400) {
|
||||||
caps.add(Caps.OpenGL40);
|
caps.add(Caps.OpenGL40);
|
||||||
caps.add(Caps.TesselationShader);
|
caps.add(Caps.TesselationShader);
|
||||||
}
|
}
|
||||||
@ -243,7 +256,7 @@ public class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadCapabilitiesCommon() {
|
private void loadCapabilitiesCommon() {
|
||||||
extensions = loadExtensions(gl.glGetString(GL.GL_EXTENSIONS));
|
extensions = loadExtensions();
|
||||||
|
|
||||||
limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
|
limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
|
||||||
if (limits.get(Limits.VertexTextureUnits) > 0) {
|
if (limits.get(Limits.VertexTextureUnits) > 0) {
|
||||||
@ -279,7 +292,7 @@ public class GLRenderer implements Renderer {
|
|||||||
|
|
||||||
// == texture format extensions ==
|
// == texture format extensions ==
|
||||||
|
|
||||||
boolean hasFloatTexture = false;
|
boolean hasFloatTexture;
|
||||||
|
|
||||||
hasFloatTexture = hasExtension("GL_OES_texture_half_float") &&
|
hasFloatTexture = hasExtension("GL_OES_texture_half_float") &&
|
||||||
hasExtension("GL_OES_texture_float");
|
hasExtension("GL_OES_texture_float");
|
||||||
@ -375,11 +388,11 @@ public class GLRenderer implements Renderer {
|
|||||||
caps.add(Caps.TextureFilterAnisotropic);
|
caps.add(Caps.TextureFilterAnisotropic);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasExtension("GL_EXT_framebuffer_object")) {
|
if (hasExtension("GL_EXT_framebuffer_object") || gl3 != null) {
|
||||||
caps.add(Caps.FrameBuffer);
|
caps.add(Caps.FrameBuffer);
|
||||||
|
|
||||||
limits.put(Limits.RenderBufferSize, getInteger(GLExt.GL_MAX_RENDERBUFFER_SIZE_EXT));
|
limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT));
|
||||||
limits.put(Limits.FrameBufferAttachments, getInteger(GLExt.GL_MAX_COLOR_ATTACHMENTS_EXT));
|
limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT));
|
||||||
|
|
||||||
if (hasExtension("GL_EXT_framebuffer_blit")) {
|
if (hasExtension("GL_EXT_framebuffer_blit")) {
|
||||||
caps.add(Caps.FrameBufferBlit);
|
caps.add(Caps.FrameBufferBlit);
|
||||||
@ -434,21 +447,30 @@ public class GLRenderer implements Renderer {
|
|||||||
caps.add(Caps.SeamlessCubemap);
|
caps.add(Caps.SeamlessCubemap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (hasExtension("GL_ARB_get_program_binary")) {
|
if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) {
|
||||||
// int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
|
caps.add(Caps.CoreProfile);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
if (hasExtension("GL_ARB_get_program_binary")) {
|
||||||
|
int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
|
||||||
|
if (binaryFormats > 0) {
|
||||||
|
caps.add(Caps.BinaryShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Print context information
|
// Print context information
|
||||||
logger.log(Level.INFO, "OpenGL Renderer Information\n" +
|
logger.log(Level.INFO, "OpenGL Renderer Information\n" +
|
||||||
" * Vendor: {0}\n" +
|
" * Vendor: {0}\n" +
|
||||||
" * Renderer: {1}\n" +
|
" * Renderer: {1}\n" +
|
||||||
" * OpenGL Version: {2}\n" +
|
" * OpenGL Version: {2}\n" +
|
||||||
" * GLSL Version: {3}",
|
" * GLSL Version: {3}\n" +
|
||||||
|
" * Profile: {4}",
|
||||||
new Object[]{
|
new Object[]{
|
||||||
gl.glGetString(GL.GL_VENDOR),
|
gl.glGetString(GL.GL_VENDOR),
|
||||||
gl.glGetString(GL.GL_RENDERER),
|
gl.glGetString(GL.GL_RENDERER),
|
||||||
gl.glGetString(GL.GL_VERSION),
|
gl.glGetString(GL.GL_VERSION),
|
||||||
gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)
|
gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION),
|
||||||
|
caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Print capabilities (if fine logging is enabled)
|
// Print capabilities (if fine logging is enabled)
|
||||||
@ -491,6 +513,20 @@ public class GLRenderer implements Renderer {
|
|||||||
|
|
||||||
// Initialize default state..
|
// Initialize default state..
|
||||||
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
|
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
if (caps.contains(Caps.CoreProfile)) {
|
||||||
|
// Core Profile requires VAO to be bound.
|
||||||
|
gl3.glGenVertexArrays(intBuf16);
|
||||||
|
int vaoId = intBuf16.get(0);
|
||||||
|
gl3.glBindVertexArray(vaoId);
|
||||||
|
}
|
||||||
|
if (gl2 != null) {
|
||||||
|
gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||||
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
|
gl2.glEnable(GL2.GL_POINT_SPRITE);
|
||||||
|
context.pointSprite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidateState() {
|
public void invalidateState() {
|
||||||
@ -610,31 +646,6 @@ public class GLRenderer implements Renderer {
|
|||||||
context.colorWriteEnabled = false;
|
context.colorWriteEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gl2 != null) {
|
|
||||||
if (state.isPointSprite() && !context.pointSprite) {
|
|
||||||
// Only enable/disable sprite
|
|
||||||
if (context.boundTextures[0] != null) {
|
|
||||||
if (context.boundTextureUnit != 0) {
|
|
||||||
gl.glActiveTexture(GL.GL_TEXTURE0);
|
|
||||||
context.boundTextureUnit = 0;
|
|
||||||
}
|
|
||||||
gl2.glEnable(GL2.GL_POINT_SPRITE);
|
|
||||||
gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
||||||
}
|
|
||||||
context.pointSprite = true;
|
|
||||||
} else if (!state.isPointSprite() && context.pointSprite) {
|
|
||||||
if (context.boundTextures[0] != null) {
|
|
||||||
if (context.boundTextureUnit != 0) {
|
|
||||||
gl.glActiveTexture(GL.GL_TEXTURE0);
|
|
||||||
context.boundTextureUnit = 0;
|
|
||||||
}
|
|
||||||
gl2.glDisable(GL2.GL_POINT_SPRITE);
|
|
||||||
gl2.glDisable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
||||||
context.pointSprite = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.isPolyOffset()) {
|
if (state.isPolyOffset()) {
|
||||||
if (!context.polyOffsetEnabled) {
|
if (!context.polyOffsetEnabled) {
|
||||||
gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
|
gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
|
||||||
@ -704,9 +715,6 @@ public class GLRenderer implements Renderer {
|
|||||||
case AlphaAdditive:
|
case AlphaAdditive:
|
||||||
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
|
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
|
||||||
break;
|
break;
|
||||||
case Color:
|
|
||||||
gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR);
|
|
||||||
break;
|
|
||||||
case Alpha:
|
case Alpha:
|
||||||
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
|
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
|
||||||
break;
|
break;
|
||||||
@ -719,6 +727,7 @@ public class GLRenderer implements Renderer {
|
|||||||
case ModulateX2:
|
case ModulateX2:
|
||||||
gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_SRC_COLOR);
|
gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_SRC_COLOR);
|
||||||
break;
|
break;
|
||||||
|
case Color:
|
||||||
case Screen:
|
case Screen:
|
||||||
gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR);
|
gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR);
|
||||||
break;
|
break;
|
||||||
@ -862,6 +871,7 @@ public class GLRenderer implements Renderer {
|
|||||||
|
|
||||||
public void postFrame() {
|
public void postFrame() {
|
||||||
objManager.deleteUnused(this);
|
objManager.deleteUnused(this);
|
||||||
|
gl.resetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************\
|
/*********************************************************************\
|
||||||
@ -1290,24 +1300,24 @@ public class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_READ_FRAMEBUFFER_EXT, 0);
|
glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, 0);
|
||||||
srcX0 = vpX;
|
srcX0 = vpX;
|
||||||
srcY0 = vpY;
|
srcY0 = vpY;
|
||||||
srcX1 = vpX + vpW;
|
srcX1 = vpX + vpW;
|
||||||
srcY1 = vpY + vpH;
|
srcY1 = vpY + vpH;
|
||||||
} else {
|
} else {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_READ_FRAMEBUFFER_EXT, src.getId());
|
glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, src.getId());
|
||||||
srcX1 = src.getWidth();
|
srcX1 = src.getWidth();
|
||||||
srcY1 = src.getHeight();
|
srcY1 = src.getHeight();
|
||||||
}
|
}
|
||||||
if (dst == null) {
|
if (dst == null) {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_DRAW_FRAMEBUFFER_EXT, 0);
|
glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, 0);
|
||||||
dstX0 = vpX;
|
dstX0 = vpX;
|
||||||
dstY0 = vpY;
|
dstY0 = vpY;
|
||||||
dstX1 = vpX + vpW;
|
dstX1 = vpX + vpW;
|
||||||
dstY1 = vpY + vpH;
|
dstY1 = vpY + vpH;
|
||||||
} else {
|
} else {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
|
glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
|
||||||
dstX1 = dst.getWidth();
|
dstX1 = dst.getWidth();
|
||||||
dstY1 = dst.getHeight();
|
dstY1 = dst.getHeight();
|
||||||
}
|
}
|
||||||
@ -1315,12 +1325,12 @@ public class GLRenderer implements Renderer {
|
|||||||
if (copyDepth) {
|
if (copyDepth) {
|
||||||
mask |= GL.GL_DEPTH_BUFFER_BIT;
|
mask |= GL.GL_DEPTH_BUFFER_BIT;
|
||||||
}
|
}
|
||||||
glext.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
|
glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
|
||||||
dstX0, dstY0, dstX1, dstY1, mask,
|
dstX0, dstY0, dstX1, dstY1, mask,
|
||||||
GL.GL_NEAREST);
|
GL.GL_NEAREST);
|
||||||
|
|
||||||
|
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, prevFBO);
|
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, prevFBO);
|
||||||
} else {
|
} else {
|
||||||
throw new RendererException("Framebuffer blitting not supported by the video hardware");
|
throw new RendererException("Framebuffer blitting not supported by the video hardware");
|
||||||
}
|
}
|
||||||
@ -1366,7 +1376,7 @@ public class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.boundRB != id) {
|
if (context.boundRB != id) {
|
||||||
glfbo.glBindRenderbufferEXT(GLExt.GL_RENDERBUFFER_EXT, id);
|
glfbo.glBindRenderbufferEXT(GLFbo.GL_RENDERBUFFER_EXT, id);
|
||||||
context.boundRB = id;
|
context.boundRB = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1384,13 +1394,13 @@ public class GLRenderer implements Renderer {
|
|||||||
if (maxSamples < samples) {
|
if (maxSamples < samples) {
|
||||||
samples = maxSamples;
|
samples = maxSamples;
|
||||||
}
|
}
|
||||||
glext.glRenderbufferStorageMultisampleEXT(GLExt.GL_RENDERBUFFER_EXT,
|
glfbo.glRenderbufferStorageMultisampleEXT(GLFbo.GL_RENDERBUFFER_EXT,
|
||||||
samples,
|
samples,
|
||||||
glFmt.internalFormat,
|
glFmt.internalFormat,
|
||||||
fb.getWidth(),
|
fb.getWidth(),
|
||||||
fb.getHeight());
|
fb.getHeight());
|
||||||
} else {
|
} else {
|
||||||
glfbo.glRenderbufferStorageEXT(GLExt.GL_RENDERBUFFER_EXT,
|
glfbo.glRenderbufferStorageEXT(GLFbo.GL_RENDERBUFFER_EXT,
|
||||||
glFmt.internalFormat,
|
glFmt.internalFormat,
|
||||||
fb.getWidth(),
|
fb.getWidth(),
|
||||||
fb.getHeight());
|
fb.getHeight());
|
||||||
@ -1400,7 +1410,7 @@ public class GLRenderer implements Renderer {
|
|||||||
private int convertAttachmentSlot(int attachmentSlot) {
|
private int convertAttachmentSlot(int attachmentSlot) {
|
||||||
// can also add support for stencil here
|
// can also add support for stencil here
|
||||||
if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
|
if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
|
||||||
return GLExt.GL_DEPTH_ATTACHMENT_EXT;
|
return GLFbo.GL_DEPTH_ATTACHMENT_EXT;
|
||||||
} else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
|
} else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
|
||||||
// NOTE: Using depth stencil format requires GL3, this is already
|
// NOTE: Using depth stencil format requires GL3, this is already
|
||||||
// checked via render caps.
|
// checked via render caps.
|
||||||
@ -1409,7 +1419,7 @@ public class GLRenderer implements Renderer {
|
|||||||
throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
|
throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GLExt.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
|
return GLFbo.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
|
public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
|
||||||
@ -1419,7 +1429,7 @@ public class GLRenderer implements Renderer {
|
|||||||
// Check NPOT requirements
|
// Check NPOT requirements
|
||||||
checkNonPowerOfTwo(tex);
|
checkNonPowerOfTwo(tex);
|
||||||
|
|
||||||
updateTexImageData(image, tex.getType(), 0);
|
updateTexImageData(image, tex.getType(), 0, false);
|
||||||
|
|
||||||
// NOTE: For depth textures, sets nearest/no-mips mode
|
// NOTE: For depth textures, sets nearest/no-mips mode
|
||||||
// Required to fix "framebuffer unsupported"
|
// Required to fix "framebuffer unsupported"
|
||||||
@ -1427,7 +1437,7 @@ public class GLRenderer implements Renderer {
|
|||||||
setupTextureParams(tex);
|
setupTextureParams(tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
glfbo.glFramebufferTexture2DEXT(GLExt.GL_FRAMEBUFFER_EXT,
|
glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
|
||||||
convertAttachmentSlot(rb.getSlot()),
|
convertAttachmentSlot(rb.getSlot()),
|
||||||
convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()),
|
convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()),
|
||||||
image.getId(),
|
image.getId(),
|
||||||
@ -1445,9 +1455,9 @@ public class GLRenderer implements Renderer {
|
|||||||
updateRenderTexture(fb, rb);
|
updateRenderTexture(fb, rb);
|
||||||
}
|
}
|
||||||
if (needAttach) {
|
if (needAttach) {
|
||||||
glfbo.glFramebufferRenderbufferEXT(GLExt.GL_FRAMEBUFFER_EXT,
|
glfbo.glFramebufferRenderbufferEXT(GLFbo.GL_FRAMEBUFFER_EXT,
|
||||||
convertAttachmentSlot(rb.getSlot()),
|
convertAttachmentSlot(rb.getSlot()),
|
||||||
GLExt.GL_RENDERBUFFER_EXT,
|
GLFbo.GL_RENDERBUFFER_EXT,
|
||||||
rb.getId());
|
rb.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1465,7 +1475,7 @@ public class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.boundFBO != id) {
|
if (context.boundFBO != id) {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, id);
|
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, id);
|
||||||
// binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
|
// binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
|
||||||
context.boundDrawBuf = 0;
|
context.boundDrawBuf = 0;
|
||||||
context.boundFBO = id;
|
context.boundFBO = id;
|
||||||
@ -1545,7 +1555,7 @@ public class GLRenderer implements Renderer {
|
|||||||
if (fb == null) {
|
if (fb == null) {
|
||||||
// unbind any fbos
|
// unbind any fbos
|
||||||
if (context.boundFBO != 0) {
|
if (context.boundFBO != 0) {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, 0);
|
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
|
||||||
statistics.onFrameBufferUse(null, true);
|
statistics.onFrameBufferUse(null, true);
|
||||||
|
|
||||||
context.boundFBO = 0;
|
context.boundFBO = 0;
|
||||||
@ -1577,7 +1587,7 @@ public class GLRenderer implements Renderer {
|
|||||||
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
|
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
|
||||||
|
|
||||||
if (context.boundFBO != fb.getId()) {
|
if (context.boundFBO != fb.getId()) {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, fb.getId());
|
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
|
||||||
statistics.onFrameBufferUse(fb, true);
|
statistics.onFrameBufferUse(fb, true);
|
||||||
|
|
||||||
context.boundFBO = fb.getId();
|
context.boundFBO = fb.getId();
|
||||||
@ -1617,7 +1627,7 @@ public class GLRenderer implements Renderer {
|
|||||||
if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
|
if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
|
||||||
intBuf16.clear();
|
intBuf16.clear();
|
||||||
for (int i = 0; i < fb.getNumColorBuffers(); i++) {
|
for (int i = 0; i < fb.getNumColorBuffers(); i++) {
|
||||||
intBuf16.put(GLExt.GL_COLOR_ATTACHMENT0_EXT + i);
|
intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
intBuf16.flip();
|
intBuf16.flip();
|
||||||
@ -1629,7 +1639,7 @@ public class GLRenderer implements Renderer {
|
|||||||
// select this draw buffer
|
// select this draw buffer
|
||||||
if (gl2 != null) {
|
if (gl2 != null) {
|
||||||
if (context.boundDrawBuf != rb.getSlot()) {
|
if (context.boundDrawBuf != rb.getSlot()) {
|
||||||
gl2.glDrawBuffer(GLExt.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
|
gl2.glDrawBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
|
||||||
context.boundDrawBuf = rb.getSlot();
|
context.boundDrawBuf = rb.getSlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1658,7 +1668,7 @@ public class GLRenderer implements Renderer {
|
|||||||
setFrameBuffer(fb);
|
setFrameBuffer(fb);
|
||||||
if (gl2 != null) {
|
if (gl2 != null) {
|
||||||
if (context.boundReadBuf != rb.getSlot()) {
|
if (context.boundReadBuf != rb.getSlot()) {
|
||||||
gl2.glReadBuffer(GLExt.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
|
gl2.glReadBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
|
||||||
context.boundReadBuf = rb.getSlot();
|
context.boundReadBuf = rb.getSlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1682,7 +1692,7 @@ public class GLRenderer implements Renderer {
|
|||||||
public void deleteFrameBuffer(FrameBuffer fb) {
|
public void deleteFrameBuffer(FrameBuffer fb) {
|
||||||
if (fb.getId() != -1) {
|
if (fb.getId() != -1) {
|
||||||
if (context.boundFBO == fb.getId()) {
|
if (context.boundFBO == fb.getId()) {
|
||||||
glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, 0);
|
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
|
||||||
context.boundFBO = 0;
|
context.boundFBO = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1956,8 +1966,10 @@ public class GLRenderer implements Renderer {
|
|||||||
* @param img The image to upload
|
* @param img The image to upload
|
||||||
* @param type How the data in the image argument should be interpreted.
|
* @param type How the data in the image argument should be interpreted.
|
||||||
* @param unit The texture slot to be used to upload the image, not important
|
* @param unit The texture slot to be used to upload the image, not important
|
||||||
|
* @param scaleToPot If true, the image will be scaled to power-of-2 dimensions
|
||||||
|
* before being uploaded.
|
||||||
*/
|
*/
|
||||||
public void updateTexImageData(Image img, Texture.Type type, int unit) {
|
public void updateTexImageData(Image img, Texture.Type type, int unit, boolean scaleToPot) {
|
||||||
int texId = img.getId();
|
int texId = img.getId();
|
||||||
if (texId == -1) {
|
if (texId == -1) {
|
||||||
// create texture
|
// create texture
|
||||||
@ -2041,33 +2053,39 @@ public class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image imageForUpload;
|
||||||
|
if (scaleToPot) {
|
||||||
|
imageForUpload = MipMapGenerator.resizeToPowerOf2(img);
|
||||||
|
} else {
|
||||||
|
imageForUpload = img;
|
||||||
|
}
|
||||||
if (target == GL.GL_TEXTURE_CUBE_MAP) {
|
if (target == GL.GL_TEXTURE_CUBE_MAP) {
|
||||||
List<ByteBuffer> data = img.getData();
|
List<ByteBuffer> data = imageForUpload.getData();
|
||||||
if (data.size() != 6) {
|
if (data.size() != 6) {
|
||||||
logger.log(Level.WARNING, "Invalid texture: {0}\n"
|
logger.log(Level.WARNING, "Invalid texture: {0}\n"
|
||||||
+ "Cubemap textures must contain 6 data units.", img);
|
+ "Cubemap textures must contain 6 data units.", img);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
texUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, linearizeSrgbImages);
|
texUtil.uploadTexture(imageForUpload, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, linearizeSrgbImages);
|
||||||
}
|
}
|
||||||
} else if (target == GLExt.GL_TEXTURE_2D_ARRAY_EXT) {
|
} else if (target == GLExt.GL_TEXTURE_2D_ARRAY_EXT) {
|
||||||
if (!caps.contains(Caps.TextureArray)) {
|
if (!caps.contains(Caps.TextureArray)) {
|
||||||
throw new RendererException("Texture arrays not supported by graphics hardware");
|
throw new RendererException("Texture arrays not supported by graphics hardware");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ByteBuffer> data = img.getData();
|
List<ByteBuffer> data = imageForUpload.getData();
|
||||||
|
|
||||||
// -1 index specifies prepare data for 2D Array
|
// -1 index specifies prepare data for 2D Array
|
||||||
texUtil.uploadTexture(img, target, -1, linearizeSrgbImages);
|
texUtil.uploadTexture(imageForUpload, target, -1, linearizeSrgbImages);
|
||||||
|
|
||||||
for (int i = 0; i < data.size(); i++) {
|
for (int i = 0; i < data.size(); i++) {
|
||||||
// upload each slice of 2D array in turn
|
// upload each slice of 2D array in turn
|
||||||
// this time with the appropriate index
|
// this time with the appropriate index
|
||||||
texUtil.uploadTexture(img, target, i, linearizeSrgbImages);
|
texUtil.uploadTexture(imageForUpload, target, i, linearizeSrgbImages);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
texUtil.uploadTexture(img, target, 0, linearizeSrgbImages);
|
texUtil.uploadTexture(imageForUpload, target, 0, linearizeSrgbImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (img.getMultiSamples() != imageSamples) {
|
if (img.getMultiSamples() != imageSamples) {
|
||||||
@ -2088,9 +2106,23 @@ public class GLRenderer implements Renderer {
|
|||||||
Image image = tex.getImage();
|
Image image = tex.getImage();
|
||||||
if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
|
if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
|
||||||
// Check NPOT requirements
|
// Check NPOT requirements
|
||||||
checkNonPowerOfTwo(tex);
|
boolean scaleToPot = false;
|
||||||
|
|
||||||
updateTexImageData(image, tex.getType(), unit);
|
try {
|
||||||
|
checkNonPowerOfTwo(tex);
|
||||||
|
} catch (RendererException ex) {
|
||||||
|
if (logger.isLoggable(Level.WARNING)) {
|
||||||
|
int nextWidth = FastMath.nearestPowerOfTwo(tex.getImage().getWidth());
|
||||||
|
int nextHeight = FastMath.nearestPowerOfTwo(tex.getImage().getHeight());
|
||||||
|
logger.log(Level.WARNING,
|
||||||
|
"Non-power-of-2 textures are not supported! Scaling texture '" + tex.getName() +
|
||||||
|
"' of size " + tex.getImage().getWidth() + "x" + tex.getImage().getHeight() +
|
||||||
|
" to " + nextWidth + "x" + nextHeight);
|
||||||
|
}
|
||||||
|
scaleToPot = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTexImageData(image, tex.getType(), unit, scaleToPot);
|
||||||
}
|
}
|
||||||
|
|
||||||
int texId = image.getId();
|
int texId = image.getId();
|
||||||
@ -2620,31 +2652,12 @@ public class GLRenderer implements Renderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.pointSprite && mesh.getMode() != Mode.Points) {
|
|
||||||
// XXX: Hack, disable point sprite mode if mesh not in point mode
|
|
||||||
if (context.boundTextures[0] != null) {
|
|
||||||
if (context.boundTextureUnit != 0) {
|
|
||||||
gl.glActiveTexture(GL.GL_TEXTURE0);
|
|
||||||
context.boundTextureUnit = 0;
|
|
||||||
}
|
|
||||||
if (gl2 != null) {
|
|
||||||
gl2.glDisable(GL2.GL_POINT_SPRITE);
|
|
||||||
gl2.glDisable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
||||||
}
|
|
||||||
context.pointSprite = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gl2 != null) {
|
|
||||||
if (context.pointSize != mesh.getPointSize()) {
|
|
||||||
gl2.glPointSize(mesh.getPointSize());
|
|
||||||
context.pointSize = mesh.getPointSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (context.lineWidth != mesh.getLineWidth()) {
|
if (context.lineWidth != mesh.getLineWidth()) {
|
||||||
gl.glLineWidth(mesh.getLineWidth());
|
gl.glLineWidth(mesh.getLineWidth());
|
||||||
context.lineWidth = mesh.getLineWidth();
|
context.lineWidth = mesh.getLineWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gl4 != null && mesh.getMode().equals(Mode.Patch)) {
|
if (gl4 != null && mesh.getMode().equals(Mode.Patch)) {
|
||||||
gl4.glPatchParameter(mesh.getPatchVertexCount());
|
gl4.glPatchParameter(mesh.getPatchVertexCount());
|
||||||
}
|
}
|
||||||
|
122
jme3-core/src/main/java/com/jme3/renderer/opengl/GLTiming.java
Normal file
122
jme3-core/src/main/java/com/jme3/renderer/opengl/GLTiming.java
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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.renderer.opengl;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GLTiming implements InvocationHandler {
|
||||||
|
|
||||||
|
private final Object obj;
|
||||||
|
private final GLTimingState state;
|
||||||
|
|
||||||
|
public GLTiming(Object obj, GLTimingState state) {
|
||||||
|
this.obj = obj;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object createGLTiming(Object glInterface, GLTimingState state, Class<?> ... glInterfaceClasses) {
|
||||||
|
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
|
||||||
|
glInterfaceClasses,
|
||||||
|
new GLTiming(glInterface, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CallTimingComparator implements Comparator<Map.Entry<String, Long>> {
|
||||||
|
@Override
|
||||||
|
public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) {
|
||||||
|
return (int) (o2.getValue() - o1.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
String methodName = method.getName();
|
||||||
|
if (methodName.equals("resetStats")) {
|
||||||
|
if (state.lastPrintOutTime + 1000000000 <= System.nanoTime() && state.sampleCount > 0) {
|
||||||
|
state.timeSpentInGL /= state.sampleCount;
|
||||||
|
System.out.println("--- TOTAL TIME SPENT IN GL CALLS: " + (state.timeSpentInGL/1000) + "us");
|
||||||
|
|
||||||
|
Map.Entry<String, Long>[] callTimes = new Map.Entry[state.callTiming.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (Map.Entry<String, Long> callTime : state.callTiming.entrySet()) {
|
||||||
|
callTimes[i++] = callTime;
|
||||||
|
}
|
||||||
|
Arrays.sort(callTimes, new CallTimingComparator());
|
||||||
|
int limit = 10;
|
||||||
|
for (Map.Entry<String, Long> callTime : callTimes) {
|
||||||
|
long val = callTime.getValue() / state.sampleCount;
|
||||||
|
String name = callTime.getKey();
|
||||||
|
String pad = " ".substring(0, 30 - name.length());
|
||||||
|
System.out.println("\t" + callTime.getKey() + pad + (val/1000) + "us");
|
||||||
|
if (limit-- == 0) break;
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Long> callTime : callTimes) {
|
||||||
|
state.callTiming.put(callTime.getKey(), Long.valueOf(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.sampleCount = 0;
|
||||||
|
state.timeSpentInGL = 0;
|
||||||
|
state.lastPrintOutTime = System.nanoTime();
|
||||||
|
} else {
|
||||||
|
state.sampleCount++;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
Long currentTimeObj = state.callTiming.get(methodName);
|
||||||
|
long currentTime = 0;
|
||||||
|
if (currentTimeObj != null) currentTime = currentTimeObj;
|
||||||
|
|
||||||
|
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
Object result = method.invoke(obj, args);
|
||||||
|
long delta = System.nanoTime() - startTime;
|
||||||
|
|
||||||
|
currentTime += delta;
|
||||||
|
state.timeSpentInGL += delta;
|
||||||
|
|
||||||
|
state.callTiming.put(methodName, currentTime);
|
||||||
|
|
||||||
|
if (delta > 1000000 && !methodName.equals("glClear")) {
|
||||||
|
// More than 1ms
|
||||||
|
// Ignore glClear as it cannot be avoided.
|
||||||
|
System.out.println("GL call " + methodName + " took " + (delta/1000) + "us to execute!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.renderer.opengl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class GLTimingState {
|
||||||
|
long timeSpentInGL = 0;
|
||||||
|
int sampleCount = 0;
|
||||||
|
long lastPrintOutTime = 0;
|
||||||
|
final HashMap<String, Long> callTiming = new HashMap<String, Long>();
|
||||||
|
}
|
@ -64,6 +64,7 @@ public final class GLTracer implements InvocationHandler {
|
|||||||
noEnumArgs("glScissor", 0, 1, 2, 3);
|
noEnumArgs("glScissor", 0, 1, 2, 3);
|
||||||
noEnumArgs("glClear", 0);
|
noEnumArgs("glClear", 0);
|
||||||
noEnumArgs("glGetInteger", 1);
|
noEnumArgs("glGetInteger", 1);
|
||||||
|
noEnumArgs("glGetString", 1);
|
||||||
|
|
||||||
noEnumArgs("glBindTexture", 1);
|
noEnumArgs("glBindTexture", 1);
|
||||||
noEnumArgs("glPixelStorei", 1);
|
noEnumArgs("glPixelStorei", 1);
|
||||||
@ -95,8 +96,6 @@ public final class GLTracer implements InvocationHandler {
|
|||||||
noEnumArgs("glFramebufferTexture2DEXT", 3, 4);
|
noEnumArgs("glFramebufferTexture2DEXT", 3, 4);
|
||||||
noEnumArgs("glBlitFramebufferEXT", 0, 1, 2, 3, 4, 5, 6, 7, 8);
|
noEnumArgs("glBlitFramebufferEXT", 0, 1, 2, 3, 4, 5, 6, 7, 8);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
noEnumArgs("glCreateProgram", -1);
|
noEnumArgs("glCreateProgram", -1);
|
||||||
noEnumArgs("glCreateShader", -1);
|
noEnumArgs("glCreateShader", -1);
|
||||||
noEnumArgs("glShaderSource", 0);
|
noEnumArgs("glShaderSource", 0);
|
||||||
@ -155,7 +154,7 @@ public final class GLTracer implements InvocationHandler {
|
|||||||
* @return A tracer that implements the given interface
|
* @return A tracer that implements the given interface
|
||||||
*/
|
*/
|
||||||
public static Object createGlesTracer(Object glInterface, Class<?> glInterfaceClass) {
|
public static Object createGlesTracer(Object glInterface, Class<?> glInterfaceClass) {
|
||||||
IntMap<String> constMap = generateConstantMap(GL.class, GLExt.class);
|
IntMap<String> constMap = generateConstantMap(GL.class, GLFbo.class, GLExt.class);
|
||||||
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
|
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
|
||||||
new Class<?>[] { glInterfaceClass },
|
new Class<?>[] { glInterfaceClass },
|
||||||
new GLTracer(glInterface, constMap));
|
new GLTracer(glInterface, constMap));
|
||||||
@ -169,7 +168,7 @@ public final class GLTracer implements InvocationHandler {
|
|||||||
* @return A tracer that implements the given interface
|
* @return A tracer that implements the given interface
|
||||||
*/
|
*/
|
||||||
public static Object createDesktopGlTracer(Object glInterface, Class<?> ... glInterfaceClasses) {
|
public static Object createDesktopGlTracer(Object glInterface, Class<?> ... glInterfaceClasses) {
|
||||||
IntMap<String> constMap = generateConstantMap(GL2.class, GLExt.class);
|
IntMap<String> constMap = generateConstantMap(GL2.class, GL3.class, GL4.class, GLFbo.class, GLExt.class);
|
||||||
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
|
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
|
||||||
glInterfaceClasses,
|
glInterfaceClasses,
|
||||||
new GLTracer(glInterface, constMap));
|
new GLTracer(glInterface, constMap));
|
||||||
|
@ -582,12 +582,14 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
if (VertexBuffer.Type.Tangent.ordinal() == bufType) {
|
if (VertexBuffer.Type.Tangent.ordinal() == bufType) {
|
||||||
useTangents = true;
|
useTangents = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (inBuf == null) {
|
||||||
|
throw new IllegalArgumentException("Geometry " + geom.getName() + " has no " + outBuf.getBufferType() + " buffer whereas other geoms have. all geometries should have the same types of buffers.\n Try to use GeometryBatchFactory.alignBuffer() on the BatchNode before batching");
|
||||||
|
} else if (outBuf == null) {
|
||||||
|
throw new IllegalArgumentException("Geometry " + geom.getName() + " has a " + outBuf.getBufferType() + " buffer whereas other geoms don't. all geometries should have the same types of buffers.\n Try to use GeometryBatchFactory.alignBuffer() on the BatchNode before batching");
|
||||||
} else {
|
} else {
|
||||||
inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
|
inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
|
||||||
// for (int vert = 0; vert < geomVertCount; vert++) {
|
}
|
||||||
// int curGlobalVertIndex = globalVertIndex + vert;
|
|
||||||
// inBuf.copyElement(vert, outBuf, curGlobalVertIndex);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +149,7 @@ public abstract class AbstractBox extends Mesh {
|
|||||||
duUpdateGeometryNormals();
|
duUpdateGeometryNormals();
|
||||||
duUpdateGeometryTextures();
|
duUpdateGeometryTextures();
|
||||||
duUpdateGeometryIndices();
|
duUpdateGeometryIndices();
|
||||||
|
setStatic();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,6 +127,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
unIndent();
|
unIndent();
|
||||||
startCondition(shaderNode.getCondition(), source);
|
startCondition(shaderNode.getCondition(), source);
|
||||||
source.append(nodeSource);
|
source.append(nodeSource);
|
||||||
|
source.append("\n");
|
||||||
endCondition(shaderNode.getCondition(), source);
|
endCondition(shaderNode.getCondition(), source);
|
||||||
indent();
|
indent();
|
||||||
}
|
}
|
||||||
|
@ -172,15 +172,6 @@ public class JmeSystem {
|
|||||||
return systemDelegate.getPlatformAssetConfigURL();
|
return systemDelegate.getPlatformAssetConfigURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Directly create an image raster via {@link DefaultImageRaster}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static ImageRaster createImageRaster(Image image, int slice) {
|
|
||||||
checkDelegate();
|
|
||||||
return systemDelegate.createImageRaster(image, slice);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays an error message to the user in whichever way the context
|
* Displays an error message to the user in whichever way the context
|
||||||
* feels is appropriate. If this is a headless or an offscreen surface
|
* feels is appropriate. If this is a headless or an offscreen surface
|
||||||
|
@ -132,11 +132,6 @@ public abstract class JmeSystemDelegate {
|
|||||||
return new DesktopAssetManager(null);
|
return new DesktopAssetManager(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public final ImageRaster createImageRaster(Image image, int slice) {
|
|
||||||
return new DefaultImageRaster(image, slice);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException;
|
public abstract void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException;
|
||||||
|
|
||||||
public abstract void showErrorDialog(String message);
|
public abstract void showErrorDialog(String message);
|
||||||
|
@ -367,7 +367,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
protected int width, height, depth;
|
protected int width, height, depth;
|
||||||
protected int[] mipMapSizes;
|
protected int[] mipMapSizes;
|
||||||
protected ArrayList<ByteBuffer> data;
|
protected ArrayList<ByteBuffer> data;
|
||||||
protected transient Object efficientData;
|
|
||||||
protected int multiSamples = 1;
|
protected int multiSamples = 1;
|
||||||
protected ColorSpace colorSpace = null;
|
protected ColorSpace colorSpace = null;
|
||||||
// protected int mipOffset = 0;
|
// protected int mipOffset = 0;
|
||||||
@ -375,7 +374,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
// attributes relating to GL object
|
// attributes relating to GL object
|
||||||
protected boolean mipsWereGenerated = false;
|
protected boolean mipsWereGenerated = false;
|
||||||
protected boolean needGeneratedMips = false;
|
protected boolean needGeneratedMips = false;
|
||||||
protected final LastTextureState lastTextureState = new LastTextureState();
|
protected LastTextureState lastTextureState = new LastTextureState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal use only.
|
* Internal use only.
|
||||||
@ -421,7 +420,8 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if the image needs to have mipmaps generated
|
* @return True if the image needs to have mipmaps generated
|
||||||
* for it (as requested by the texture).
|
* for it (as requested by the texture). This stays true even
|
||||||
|
* after mipmaps have been generated.
|
||||||
*/
|
*/
|
||||||
public boolean isGeneratedMipmapsRequired() {
|
public boolean isGeneratedMipmapsRequired() {
|
||||||
return needGeneratedMips;
|
return needGeneratedMips;
|
||||||
@ -434,8 +434,9 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
@Override
|
@Override
|
||||||
public void setUpdateNeeded() {
|
public void setUpdateNeeded() {
|
||||||
super.setUpdateNeeded();
|
super.setUpdateNeeded();
|
||||||
if (!isGeneratedMipmapsRequired() && !hasMipmaps()) {
|
if (isGeneratedMipmapsRequired() && !hasMipmaps()) {
|
||||||
setNeedGeneratedMipmaps();
|
// Mipmaps are no longer valid, since the image was changed.
|
||||||
|
setMipmapsGenerated(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,6 +489,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
Image clone = (Image) super.clone();
|
Image clone = (Image) super.clone();
|
||||||
clone.mipMapSizes = mipMapSizes != null ? mipMapSizes.clone() : null;
|
clone.mipMapSizes = mipMapSizes != null ? mipMapSizes.clone() : null;
|
||||||
clone.data = data != null ? new ArrayList<ByteBuffer>(data) : null;
|
clone.data = data != null ? new ArrayList<ByteBuffer>(data) : null;
|
||||||
|
clone.lastTextureState = new LastTextureState();
|
||||||
clone.setUpdateNeeded();
|
clone.setUpdateNeeded();
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
@ -527,12 +529,14 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
|
|
||||||
this();
|
this();
|
||||||
|
|
||||||
if (mipMapSizes != null && mipMapSizes.length <= 1) {
|
if (mipMapSizes != null) {
|
||||||
|
if (mipMapSizes.length <= 1) {
|
||||||
mipMapSizes = null;
|
mipMapSizes = null;
|
||||||
} else {
|
} else {
|
||||||
needGeneratedMips = false;
|
needGeneratedMips = false;
|
||||||
mipsWereGenerated = true;
|
mipsWereGenerated = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setFormat(format);
|
setFormat(format);
|
||||||
this.width = width;
|
this.width = width;
|
||||||
@ -756,8 +760,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setEfficentData(Object efficientData){
|
public void setEfficentData(Object efficientData){
|
||||||
this.efficientData = efficientData;
|
|
||||||
setUpdateNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -765,7 +767,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Object getEfficentData(){
|
public Object getEfficentData(){
|
||||||
return efficientData;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -787,8 +789,8 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
needGeneratedMips = false;
|
needGeneratedMips = false;
|
||||||
mipsWereGenerated = false;
|
mipsWereGenerated = false;
|
||||||
} else {
|
} else {
|
||||||
needGeneratedMips = false;
|
needGeneratedMips = true;
|
||||||
mipsWereGenerated = true;
|
mipsWereGenerated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpdateNeeded();
|
setUpdateNeeded();
|
||||||
|
@ -44,7 +44,9 @@ public class DefaultImageRaster extends ImageRaster {
|
|||||||
private final ImageCodec codec;
|
private final ImageCodec codec;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
|
private final int offset;
|
||||||
private final byte[] temp;
|
private final byte[] temp;
|
||||||
|
private final boolean convertToLinear;
|
||||||
private int slice;
|
private int slice;
|
||||||
|
|
||||||
private void rangeCheck(int x, int y) {
|
private void rangeCheck(int x, int y) {
|
||||||
@ -53,13 +55,40 @@ public class DefaultImageRaster extends ImageRaster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultImageRaster(Image image, int slice) {
|
public DefaultImageRaster(Image image, int slice, int mipMapLevel, boolean convertToLinear) {
|
||||||
this.image = image;
|
int[] mipMapSizes = image.getMipMapSizes();
|
||||||
this.slice = slice;
|
int availableMips = mipMapSizes != null ? mipMapSizes.length : 1;
|
||||||
this.buffer = image.getData(slice);
|
|
||||||
this.codec = ImageCodec.lookup(image.getFormat());
|
if (mipMapLevel >= availableMips) {
|
||||||
|
throw new IllegalStateException("Cannot create image raster for mipmap level #" + mipMapLevel + ". "
|
||||||
|
+ "Image only has " + availableMips + " mipmap levels.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.hasMipmaps()) {
|
||||||
|
this.width = Math.max(1, image.getWidth() >> mipMapLevel);
|
||||||
|
this.height = Math.max(1, image.getHeight() >> mipMapLevel);
|
||||||
|
|
||||||
|
int mipOffset = 0;
|
||||||
|
for (int i = 0; i < mipMapLevel; i++) {
|
||||||
|
mipOffset += mipMapSizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.offset = mipOffset;
|
||||||
|
} else {
|
||||||
this.width = image.getWidth();
|
this.width = image.getWidth();
|
||||||
this.height = image.getHeight();
|
this.height = image.getHeight();
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.image = image;
|
||||||
|
this.slice = slice;
|
||||||
|
|
||||||
|
// Conversion to linear only needed if image's color space is sRGB.
|
||||||
|
this.convertToLinear = convertToLinear && image.getColorSpace() == ColorSpace.sRGB;
|
||||||
|
|
||||||
|
this.buffer = image.getData(slice);
|
||||||
|
this.codec = ImageCodec.lookup(image.getFormat());
|
||||||
|
|
||||||
if (codec instanceof ByteAlignedImageCodec || codec instanceof ByteOffsetImageCodec) {
|
if (codec instanceof ByteAlignedImageCodec || codec instanceof ByteOffsetImageCodec) {
|
||||||
this.temp = new byte[codec.bpp];
|
this.temp = new byte[codec.bpp];
|
||||||
} else {
|
} else {
|
||||||
@ -86,6 +115,12 @@ public class DefaultImageRaster extends ImageRaster {
|
|||||||
public void setPixel(int x, int y, ColorRGBA color) {
|
public void setPixel(int x, int y, ColorRGBA color) {
|
||||||
rangeCheck(x, y);
|
rangeCheck(x, y);
|
||||||
|
|
||||||
|
if (convertToLinear) {
|
||||||
|
// Input is linear, needs to be converted to sRGB before writing
|
||||||
|
// into image.
|
||||||
|
color = color.getAsSrgb();
|
||||||
|
}
|
||||||
|
|
||||||
// Check flags for grayscale
|
// Check flags for grayscale
|
||||||
if (codec.isGray) {
|
if (codec.isGray) {
|
||||||
float gray = color.r * 0.27f + color.g * 0.67f + color.b * 0.06f;
|
float gray = color.r * 0.27f + color.g * 0.67f + color.b * 0.06f;
|
||||||
@ -113,7 +148,7 @@ public class DefaultImageRaster extends ImageRaster {
|
|||||||
components[3] = Math.min( (int) (color.b * codec.maxBlue + 0.5f), codec.maxBlue);
|
components[3] = Math.min( (int) (color.b * codec.maxBlue + 0.5f), codec.maxBlue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
codec.writeComponents(getBuffer(), x, y, width, 0, components, temp);
|
codec.writeComponents(getBuffer(), x, y, width, offset, components, temp);
|
||||||
image.setUpdateNeeded();
|
image.setUpdateNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +163,7 @@ public class DefaultImageRaster extends ImageRaster {
|
|||||||
public ColorRGBA getPixel(int x, int y, ColorRGBA store) {
|
public ColorRGBA getPixel(int x, int y, ColorRGBA store) {
|
||||||
rangeCheck(x, y);
|
rangeCheck(x, y);
|
||||||
|
|
||||||
codec.readComponents(getBuffer(), x, y, width, 0, components, temp);
|
codec.readComponents(getBuffer(), x, y, width, offset, components, temp);
|
||||||
if (store == null) {
|
if (store == null) {
|
||||||
store = new ColorRGBA();
|
store = new ColorRGBA();
|
||||||
}
|
}
|
||||||
@ -169,6 +204,12 @@ public class DefaultImageRaster extends ImageRaster {
|
|||||||
store.a = 1;
|
store.a = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (convertToLinear) {
|
||||||
|
// Input image is sRGB, need to convert to linear.
|
||||||
|
store.setAsSrgb(store.r, store.g, store.b, store.a);
|
||||||
|
}
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,21 +71,42 @@ public abstract class ImageRaster {
|
|||||||
* @param image The image to read / write to.
|
* @param image The image to read / write to.
|
||||||
* @param slice Which slice to use. Only applies to 3D images, 2D image
|
* @param slice Which slice to use. Only applies to 3D images, 2D image
|
||||||
* arrays or cubemaps.
|
* arrays or cubemaps.
|
||||||
|
* @param mipMapLevel The mipmap level to read / write to. To access levels
|
||||||
|
* other than 0, the image must have
|
||||||
|
* {@link Image#setMipMapSizes(int[]) mipmap sizes} set.
|
||||||
|
* @param convertToLinear If true, the application expects read or written
|
||||||
|
* colors to be in linear color space (<code>ImageRaster</code> will
|
||||||
|
* automatically perform a conversion as needed). If false, the application expects
|
||||||
|
* colors to be in the image's native {@link Image#getColorSpace() color space}.
|
||||||
|
* @return An ImageRaster to read / write to the image.
|
||||||
|
*/
|
||||||
|
public static ImageRaster create(Image image, int slice, int mipMapLevel, boolean convertToLinear) {
|
||||||
|
return new DefaultImageRaster(image, slice, mipMapLevel, convertToLinear);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new image reader / writer.
|
||||||
|
*
|
||||||
|
* @param image The image to read / write to.
|
||||||
|
* @param slice Which slice to use. Only applies to 3D images, 2D image
|
||||||
|
* arrays or cubemaps.
|
||||||
|
* @return An ImageRaster to read / write to the image.
|
||||||
*/
|
*/
|
||||||
public static ImageRaster create(Image image, int slice) {
|
public static ImageRaster create(Image image, int slice) {
|
||||||
return JmeSystem.createImageRaster(image, slice);
|
return create(image, slice, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new image reader / writer for 2D images.
|
* Create new image reader / writer for 2D images.
|
||||||
*
|
*
|
||||||
* @param image The image to read / write to.
|
* @param image The image to read / write to.
|
||||||
|
* @return An ImageRaster to read / write to the image.
|
||||||
*/
|
*/
|
||||||
public static ImageRaster create(Image image) {
|
public static ImageRaster create(Image image) {
|
||||||
if (image.getData().size() > 1) {
|
if (image.getData().size() > 1) {
|
||||||
throw new IllegalStateException("Use constructor that takes slices argument to read from multislice image");
|
throw new IllegalStateException("Use constructor that takes slices argument to read from multislice image");
|
||||||
}
|
}
|
||||||
return JmeSystem.createImageRaster(image, 0);
|
return create(image, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageRaster() {
|
public ImageRaster() {
|
||||||
|
140
jme3-core/src/main/java/com/jme3/util/MipMapGenerator.java
Normal file
140
jme3-core/src/main/java/com/jme3/util/MipMapGenerator.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.texture.Image;
|
||||||
|
import com.jme3.texture.Image.Format;
|
||||||
|
import com.jme3.texture.image.ImageRaster;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class MipMapGenerator {
|
||||||
|
|
||||||
|
private MipMapGenerator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image scaleImage(Image inputImage, int outputWidth, int outputHeight) {
|
||||||
|
int size = outputWidth * outputHeight * inputImage.getFormat().getBitsPerPixel() / 8;
|
||||||
|
ByteBuffer buffer = BufferUtils.createByteBuffer(size);
|
||||||
|
Image outputImage = new Image(inputImage.getFormat(),
|
||||||
|
outputWidth,
|
||||||
|
outputHeight,
|
||||||
|
buffer,
|
||||||
|
inputImage.getColorSpace());
|
||||||
|
|
||||||
|
ImageRaster input = ImageRaster.create(inputImage, 0, 0, false);
|
||||||
|
ImageRaster output = ImageRaster.create(outputImage, 0, 0, false);
|
||||||
|
|
||||||
|
float xRatio = ((float)(input.getWidth() - 1)) / output.getWidth();
|
||||||
|
float yRatio = ((float)(input.getHeight() - 1)) / output.getHeight();
|
||||||
|
|
||||||
|
ColorRGBA outputColor = new ColorRGBA();
|
||||||
|
ColorRGBA bottomLeft = new ColorRGBA();
|
||||||
|
ColorRGBA bottomRight = new ColorRGBA();
|
||||||
|
ColorRGBA topLeft = new ColorRGBA();
|
||||||
|
ColorRGBA topRight = new ColorRGBA();
|
||||||
|
|
||||||
|
for (int y = 0; y < outputHeight; y++) {
|
||||||
|
for (int x = 0; x < outputWidth; x++) {
|
||||||
|
float x2f = x * xRatio;
|
||||||
|
float y2f = y * yRatio;
|
||||||
|
|
||||||
|
int x2 = (int)x2f;
|
||||||
|
int y2 = (int)y2f;
|
||||||
|
|
||||||
|
float xDiff = x2f - x2;
|
||||||
|
float yDiff = y2f - y2;
|
||||||
|
|
||||||
|
input.getPixel(x2, y2, bottomLeft);
|
||||||
|
input.getPixel(x2 + 1, y2, bottomRight);
|
||||||
|
input.getPixel(x2, y2 + 1, topLeft);
|
||||||
|
input.getPixel(x2 + 1, y2 + 1, topRight);
|
||||||
|
|
||||||
|
bottomLeft.multLocal( (1f - xDiff) * (1f - yDiff) );
|
||||||
|
bottomRight.multLocal( (xDiff) * (1f - yDiff) );
|
||||||
|
topLeft.multLocal( (1f - xDiff) * (yDiff) );
|
||||||
|
topRight.multLocal( (xDiff) * (yDiff) );
|
||||||
|
|
||||||
|
outputColor.set(bottomLeft).addLocal(bottomRight)
|
||||||
|
.addLocal(topLeft).addLocal(topRight);
|
||||||
|
|
||||||
|
output.setPixel(x, y, outputColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image resizeToPowerOf2(Image original){
|
||||||
|
int potWidth = FastMath.nearestPowerOfTwo(original.getWidth());
|
||||||
|
int potHeight = FastMath.nearestPowerOfTwo(original.getHeight());
|
||||||
|
return scaleImage(original, potWidth, potHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void generateMipMaps(Image image){
|
||||||
|
int width = image.getWidth();
|
||||||
|
int height = image.getHeight();
|
||||||
|
|
||||||
|
Image current = image;
|
||||||
|
ArrayList<ByteBuffer> output = new ArrayList<ByteBuffer>();
|
||||||
|
int totalSize = 0;
|
||||||
|
|
||||||
|
while (height >= 1 || width >= 1){
|
||||||
|
output.add(current.getData(0));
|
||||||
|
totalSize += current.getData(0).capacity();
|
||||||
|
|
||||||
|
if (height == 1 || width == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
height /= 2;
|
||||||
|
width /= 2;
|
||||||
|
|
||||||
|
current = scaleImage(current, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer combinedData = BufferUtils.createByteBuffer(totalSize);
|
||||||
|
int[] mipSizes = new int[output.size()];
|
||||||
|
for (int i = 0; i < output.size(); i++){
|
||||||
|
ByteBuffer data = output.get(i);
|
||||||
|
data.clear();
|
||||||
|
combinedData.put(data);
|
||||||
|
mipSizes[i] = data.capacity();
|
||||||
|
}
|
||||||
|
combinedData.flip();
|
||||||
|
|
||||||
|
// insert mip data into image
|
||||||
|
image.setData(0, combinedData);
|
||||||
|
image.setMipMapSizes(mipSizes);
|
||||||
|
}
|
||||||
|
}
|
@ -18,9 +18,6 @@ varying vec3 SpecularSum;
|
|||||||
uniform mat4 g_ViewMatrix;
|
uniform mat4 g_ViewMatrix;
|
||||||
uniform vec4 g_LightData[NB_LIGHTS];
|
uniform vec4 g_LightData[NB_LIGHTS];
|
||||||
varying vec3 vPos;
|
varying vec3 vPos;
|
||||||
#else
|
|
||||||
varying vec3 specularAccum;
|
|
||||||
varying vec4 diffuseAccum;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DIFFUSEMAP
|
#ifdef DIFFUSEMAP
|
||||||
@ -72,7 +69,8 @@ uniform float m_Shininess;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
#ifdef NORMALMAP
|
#if !defined(VERTEX_LIGHTING)
|
||||||
|
#if defined(NORMALMAP)
|
||||||
mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
|
mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
|
||||||
|
|
||||||
if (!gl_FrontFacing)
|
if (!gl_FrontFacing)
|
||||||
@ -84,6 +82,7 @@ void main(){
|
|||||||
#else
|
#else
|
||||||
vec3 viewDir = normalize(-vPos.xyz);
|
vec3 viewDir = normalize(-vPos.xyz);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
vec2 newTexCoord;
|
vec2 newTexCoord;
|
||||||
|
|
||||||
@ -165,10 +164,9 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_LIGHTING
|
#ifdef VERTEX_LIGHTING
|
||||||
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb
|
gl_FragColor.rgb = AmbientSum.rgb * diffuseColor.rgb
|
||||||
+diffuseAccum.rgb *diffuseColor.rgb
|
+ DiffuseSum.rgb * diffuseColor.rgb
|
||||||
+specularAccum.rgb * specularColor.rgb;
|
+ SpecularSum.rgb * specularColor.rgb;
|
||||||
gl_FragColor.a=1.0;
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -43,8 +43,9 @@ attribute vec3 inNormal;
|
|||||||
varying vec3 vBinormal;
|
varying vec3 vBinormal;
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
varying vec3 specularAccum;
|
#ifdef COLORRAMP
|
||||||
varying vec4 diffuseAccum;
|
uniform sampler2D m_ColorRamp;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_REFLECTION
|
#ifdef USE_REFLECTION
|
||||||
@ -128,14 +129,13 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
#ifdef VERTEX_LIGHTING
|
#ifdef VERTEX_LIGHTING
|
||||||
int i = 0;
|
int i = 0;
|
||||||
diffuseAccum = vec4(0.0);
|
vec3 diffuseAccum = vec3(0.0);
|
||||||
specularAccum = vec3(0.0);
|
vec3 specularAccum = vec3(0.0);
|
||||||
vec4 diffuseColor;
|
vec4 diffuseColor;
|
||||||
vec3 specularColor;
|
vec3 specularColor;
|
||||||
for (int i =0;i < NB_LIGHTS; i+=3){
|
for (int i =0;i < NB_LIGHTS; i+=3){
|
||||||
vec4 lightColor = g_LightData[i];
|
vec4 lightColor = g_LightData[i];
|
||||||
vec4 lightData1 = g_LightData[i+1];
|
vec4 lightData1 = g_LightData[i+1];
|
||||||
DiffuseSum = vec4(1.0);
|
|
||||||
#ifdef MATERIAL_COLORS
|
#ifdef MATERIAL_COLORS
|
||||||
diffuseColor = m_Diffuse * vec4(lightColor.rgb, 1.0);
|
diffuseColor = m_Diffuse * vec4(lightColor.rgb, 1.0);
|
||||||
specularColor = m_Specular.rgb * lightColor.rgb;
|
specularColor = m_Specular.rgb * lightColor.rgb;
|
||||||
@ -160,16 +160,19 @@ void main(){
|
|||||||
#if __VERSION__ >= 110
|
#if __VERSION__ >= 110
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
vec2 v = computeLighting(wvNormal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess);
|
vec2 light = computeLighting(wvNormal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess);
|
||||||
|
|
||||||
#ifdef COLORRAMP
|
#ifdef COLORRAMP
|
||||||
diffuseAccum += texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb * diffuseColor;
|
diffuseAccum += texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb * diffuseColor.rgb;
|
||||||
specularAccum += texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb * specularColor;
|
specularAccum += texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb * specularColor;
|
||||||
#else
|
#else
|
||||||
diffuseAccum += v.x * diffuseColor;
|
diffuseAccum += light.x * diffuseColor.rgb;
|
||||||
specularAccum += v.y * specularColor;
|
specularAccum += light.y * specularColor;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiffuseSum.rgb *= diffuseAccum.rgb;
|
||||||
|
SpecularSum.rgb *= specularAccum.rgb;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,26 +2,4 @@ INCLUDE com/jme3/asset/General.cfg
|
|||||||
|
|
||||||
# Desktop-specific loaders
|
# Desktop-specific loaders
|
||||||
LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg
|
LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg
|
||||||
LOADER com.jme3.audio.plugins.OGGLoader : oggLOADER com.jme3.audio.plugins.WAVLoader : wav
|
|
||||||
LOADER com.jme3.audio.plugins.OGGLoader : ogg
|
LOADER com.jme3.audio.plugins.OGGLoader : ogg
|
||||||
LOADER com.jme3.cursors.plugins.CursorLoader : ani, cur, ico
|
|
||||||
LOADER com.jme3.material.plugins.J3MLoader : j3m
|
|
||||||
LOADER com.jme3.material.plugins.J3MLoader : j3md
|
|
||||||
LOADER com.jme3.material.plugins.ShaderNodeDefinitionLoader : j3sn
|
|
||||||
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
|
|
||||||
LOADER com.jme3.texture.plugins.DDSLoader : dds
|
|
||||||
LOADER com.jme3.texture.plugins.PFMLoader : pfm
|
|
||||||
LOADER com.jme3.texture.plugins.HDRLoader : hdr
|
|
||||||
LOADER com.jme3.texture.plugins.TGALoader : tga
|
|
||||||
LOADER com.jme3.export.binary.BinaryImporter : j3o
|
|
||||||
LOADER com.jme3.export.binary.BinaryImporter : j3f
|
|
||||||
LOADER com.jme3.scene.plugins.OBJLoader : obj
|
|
||||||
LOADER com.jme3.scene.plugins.MTLLoader : mtl
|
|
||||||
LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml
|
|
||||||
LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml
|
|
||||||
LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
|
|
||||||
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
|
|
||||||
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
|
|
||||||
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag,geom,tsctrl,tseval, glsl, glsllib
|
|
||||||
LOADER com.jme3.scene.plugins.fbx.SceneLoader : fbx
|
|
||||||
LOADER com.jme3.scene.plugins.fbx.SceneWithAnimationLoader : fba
|
|
||||||
|
@ -21,6 +21,6 @@ LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml
|
|||||||
LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
|
LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
|
||||||
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
|
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
|
||||||
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
|
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
|
||||||
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
|
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, geom, tsctrl, tseval, glsl, glsllib
|
||||||
LOADER com.jme3.scene.plugins.fbx.SceneLoader : fbx
|
LOADER com.jme3.scene.plugins.fbx.SceneLoader : fbx
|
||||||
LOADER com.jme3.scene.plugins.fbx.SceneWithAnimationLoader : fba
|
LOADER com.jme3.scene.plugins.fbx.SceneWithAnimationLoader : fba
|
||||||
|
@ -56,3 +56,10 @@ Controller\ (XBOX\ 360\ For\ Windows).ry=rz
|
|||||||
# requires custom code to support trigger buttons but this
|
# requires custom code to support trigger buttons but this
|
||||||
# keeps it from confusing the .rx mapping.
|
# keeps it from confusing the .rx mapping.
|
||||||
Controller\ (XBOX\ 360\ For\ Windows).z=trigger
|
Controller\ (XBOX\ 360\ For\ Windows).z=trigger
|
||||||
|
|
||||||
|
# XBOX 360 Controller connected to Android using
|
||||||
|
# the USB dongle
|
||||||
|
Xbox\ 360\ Wireless\ Receiver.AXIS_RX=z
|
||||||
|
Xbox\ 360\ Wireless\ Receiver.AXIS_RY=rz
|
||||||
|
Xbox\ 360\ Wireless\ Receiver.z=AXIS_RX
|
||||||
|
Xbox\ 360\ Wireless\ Receiver.rz=AXIS_RY
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.export.binary;
|
package com.jme3.export.binary;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.export.FormatVersion;
|
import com.jme3.export.FormatVersion;
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
@ -168,7 +169,32 @@ public class BinaryExporter implements JmeExporter {
|
|||||||
return new BinaryExporter();
|
return new BinaryExporter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean save(Savable object, OutputStream os) throws IOException {
|
/**
|
||||||
|
* Saves the object into memory then loads it from memory.
|
||||||
|
*
|
||||||
|
* Used by tests to check if the persistence system is working.
|
||||||
|
*
|
||||||
|
* @param <T> The type of savable.
|
||||||
|
* @param assetManager AssetManager to load assets from.
|
||||||
|
* @param object The object to save and then load.
|
||||||
|
* @return A new instance that has been saved and loaded from the
|
||||||
|
* original object.
|
||||||
|
*/
|
||||||
|
public static <T extends Savable> T saveAndLoad(AssetManager assetManager, T object) {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
BinaryExporter exporter = new BinaryExporter();
|
||||||
|
exporter.save(object, baos);
|
||||||
|
BinaryImporter importer = new BinaryImporter();
|
||||||
|
importer.setAssetManager(assetManager);
|
||||||
|
return (T) importer.load(baos.toByteArray());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// Should never happen.
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Savable object, OutputStream os) throws IOException {
|
||||||
// reset some vars
|
// reset some vars
|
||||||
aliasCount = 1;
|
aliasCount = 1;
|
||||||
idCount = 1;
|
idCount = 1;
|
||||||
@ -294,8 +320,6 @@ public class BinaryExporter implements JmeExporter {
|
|||||||
logger.log(Level.FINE, "location table: {0} bytes", locationTableSize);
|
logger.log(Level.FINE, "location table: {0} bytes", locationTableSize);
|
||||||
logger.log(Level.FINE, "data: {0} bytes", location);
|
logger.log(Level.FINE, "data: {0} bytes", location);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getChunk(BinaryIdContentPair pair) {
|
protected String getChunk(BinaryIdContentPair pair) {
|
||||||
@ -325,7 +349,7 @@ public class BinaryExporter implements JmeExporter {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean save(Savable object, File f) throws IOException {
|
public void save(Savable object, File f) throws IOException {
|
||||||
File parentDirectory = f.getParentFile();
|
File parentDirectory = f.getParentFile();
|
||||||
if (parentDirectory != null && !parentDirectory.exists()) {
|
if (parentDirectory != null && !parentDirectory.exists()) {
|
||||||
parentDirectory.mkdirs();
|
parentDirectory.mkdirs();
|
||||||
@ -333,13 +357,11 @@ public class BinaryExporter implements JmeExporter {
|
|||||||
|
|
||||||
FileOutputStream fos = new FileOutputStream(f);
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
try {
|
try {
|
||||||
return save(object, fos);
|
save(object, fos);
|
||||||
} finally {
|
} finally {
|
||||||
if (fos != null) {
|
|
||||||
fos.close();
|
fos.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public BinaryOutputCapsule getCapsule(Savable object) {
|
public BinaryOutputCapsule getCapsule(Savable object) {
|
||||||
return contentTable.get(object).getContent();
|
return contentTable.get(object).getContent();
|
||||||
|
@ -586,6 +586,11 @@ public class J3MLoader implements AssetLoader {
|
|||||||
InputStream in = info.openStream();
|
InputStream in = info.openStream();
|
||||||
try {
|
try {
|
||||||
key = info.getKey();
|
key = info.getKey();
|
||||||
|
if (key.getExtension().equals("j3m") && !(key instanceof MaterialKey)) {
|
||||||
|
throw new IOException("Material instances must be loaded via MaterialKey");
|
||||||
|
} else if (key.getExtension().equals("j3md") && key instanceof MaterialKey) {
|
||||||
|
throw new IOException("Material definitions must be loaded via AssetKey");
|
||||||
|
}
|
||||||
loadFromRoot(BlockLanguageParser.parse(in));
|
loadFromRoot(BlockLanguageParser.parse(in));
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null){
|
if (in != null){
|
||||||
@ -594,9 +599,6 @@ public class J3MLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (material != null){
|
if (material != null){
|
||||||
if (!(info.getKey() instanceof MaterialKey)){
|
|
||||||
throw new IOException("Material instances must be loaded via MaterialKey");
|
|
||||||
}
|
|
||||||
// material implementation
|
// material implementation
|
||||||
return material;
|
return material;
|
||||||
}else{
|
}else{
|
||||||
|
@ -242,6 +242,20 @@ public class DXTFlipper {
|
|||||||
img.position(blockByteOffset);
|
img.position(blockByteOffset);
|
||||||
img.limit(blockByteOffset + bpb);
|
img.limit(blockByteOffset + bpb);
|
||||||
|
|
||||||
|
if (alphaBlock != null){
|
||||||
|
img.get(alphaBlock);
|
||||||
|
switch (type){
|
||||||
|
case 2:
|
||||||
|
flipDXT3Block(alphaBlock, h);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
flipDXT5Block(alphaBlock, h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retImg.put(alphaBlock);
|
||||||
|
}
|
||||||
|
|
||||||
img.get(colorBlock);
|
img.get(colorBlock);
|
||||||
if (type == 4 || type == 5)
|
if (type == 4 || type == 5)
|
||||||
flipDXT5Block(colorBlock, h);
|
flipDXT5Block(colorBlock, h);
|
||||||
@ -251,19 +265,6 @@ public class DXTFlipper {
|
|||||||
// write block (no need to flip block indexes, only pixels
|
// write block (no need to flip block indexes, only pixels
|
||||||
// inside block
|
// inside block
|
||||||
retImg.put(colorBlock);
|
retImg.put(colorBlock);
|
||||||
|
|
||||||
if (alphaBlock != null){
|
|
||||||
img.get(alphaBlock);
|
|
||||||
switch (type){
|
|
||||||
case 2:
|
|
||||||
flipDXT3Block(alphaBlock, h); break;
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
flipDXT5Block(alphaBlock, h);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
retImg.put(alphaBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
retImg.rewind();
|
retImg.rewind();
|
||||||
}else if (h >= 4){
|
}else if (h >= 4){
|
||||||
|
@ -16,6 +16,7 @@ import java.nio.FloatBuffer;
|
|||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class GeometryBatchFactory {
|
public class GeometryBatchFactory {
|
||||||
@ -453,4 +454,93 @@ public class GeometryBatchFactory {
|
|||||||
mergeGeometries(geoms, outMesh);
|
mergeGeometries(geoms, outMesh);
|
||||||
printMesh(outMesh);
|
printMesh(outMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to align the buffers of geometries' meshes of a sub graph
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static enum AlignOption {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will remove the buffers of a type that is not on all the geometries
|
||||||
|
*/
|
||||||
|
RemoveUnalignedBuffers,
|
||||||
|
/**
|
||||||
|
* Will create missing buffers and pad with dummy data
|
||||||
|
*/
|
||||||
|
CreateMissingBuffers
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will ensure that all the geometries' meshes of the n sub graph have the
|
||||||
|
* same types of buffers
|
||||||
|
* @param n the node to gather geometries from
|
||||||
|
* @param option the align options
|
||||||
|
* @see AlignOption
|
||||||
|
*
|
||||||
|
* Very experimental for now.
|
||||||
|
*/
|
||||||
|
public static void alignBuffers(Node n, AlignOption option) {
|
||||||
|
List<Geometry> geoms = new ArrayList<Geometry>();
|
||||||
|
gatherGeoms(n, geoms);
|
||||||
|
|
||||||
|
//gather buffer types
|
||||||
|
Map<VertexBuffer.Type, VertexBuffer> types = new EnumMap<VertexBuffer.Type, VertexBuffer>(VertexBuffer.Type.class);
|
||||||
|
Map<VertexBuffer.Type, Integer> typesCount = new EnumMap<VertexBuffer.Type, Integer>(VertexBuffer.Type.class);
|
||||||
|
for (Geometry geom : geoms) {
|
||||||
|
for (VertexBuffer buffer : geom.getMesh().getBufferList()) {
|
||||||
|
if (types.get(buffer.getBufferType()) == null) {
|
||||||
|
types.put(buffer.getBufferType(), buffer);
|
||||||
|
logger.log(Level.FINE, buffer.getBufferType().toString());
|
||||||
|
}
|
||||||
|
Integer count = typesCount.get(buffer.getBufferType());
|
||||||
|
if (count == null) {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
typesCount.put(buffer.getBufferType(), count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case RemoveUnalignedBuffers:
|
||||||
|
for (Geometry geom : geoms) {
|
||||||
|
|
||||||
|
for (VertexBuffer buffer : geom.getMesh().getBufferList()) {
|
||||||
|
Integer count = typesCount.get(buffer.getBufferType());
|
||||||
|
if (count != null && count < geoms.size()) {
|
||||||
|
geom.getMesh().clearBuffer(buffer.getBufferType());
|
||||||
|
logger.log(Level.FINE, "removing {0} from {1}", new Object[]{buffer.getBufferType(), geom.getName()});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CreateMissingBuffers:
|
||||||
|
for (Geometry geom : geoms) {
|
||||||
|
for (VertexBuffer.Type type : types.keySet()) {
|
||||||
|
if (geom.getMesh().getBuffer(type) == null) {
|
||||||
|
VertexBuffer vb = new VertexBuffer(type);
|
||||||
|
Buffer b;
|
||||||
|
switch (type) {
|
||||||
|
case Index:
|
||||||
|
case BoneIndex:
|
||||||
|
case HWBoneIndex:
|
||||||
|
b = BufferUtils.createIntBuffer(geom.getMesh().getVertexCount() * types.get(type).getNumComponents());
|
||||||
|
break;
|
||||||
|
case InterleavedData:
|
||||||
|
b = BufferUtils.createByteBuffer(geom.getMesh().getVertexCount() * types.get(type).getNumComponents());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
b = BufferUtils.createFloatBuffer(geom.getMesh().getVertexCount() * types.get(type).getNumComponents());
|
||||||
|
}
|
||||||
|
vb.setupData(types.get(type).getUsage(), types.get(type).getNumComponents(), types.get(type).getFormat(), b);
|
||||||
|
geom.getMesh().setBuffer(vb);
|
||||||
|
logger.log(Level.FINE, "geom {0} misses buffer {1}. Creating", new Object[]{geom.getName(), type});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,8 +162,8 @@ public class SSAOFilter extends Filter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ssaoPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth, 1, ssaoMat);
|
ssaoPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth, 1, ssaoMat);
|
||||||
ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
|
// ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
|
||||||
ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
|
// ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
postRenderPasses.add(ssaoPass);
|
postRenderPasses.add(ssaoPass);
|
||||||
material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
|
material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
|
||||||
material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
|
material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
|
||||||
|
@ -79,7 +79,7 @@ public class TestAttachDriver extends SimpleApplication implements ActionListene
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
setupKeys();
|
setupKeys();
|
||||||
setupFloor();
|
setupFloor();
|
||||||
buildPlayer();
|
buildPlayer();
|
||||||
|
@ -85,7 +85,7 @@ public class TestAttachGhostObject extends SimpleApplication implements AnalogLi
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
setupKeys();
|
setupKeys();
|
||||||
setupJoint();
|
setupJoint();
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ public class TestCcd extends SimpleApplication implements ActionListener {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
bullet = new Sphere(32, 32, 0.4f, true, false);
|
bullet = new Sphere(32, 32, 0.4f, true, false);
|
||||||
bullet.setTextureMode(TextureMode.Projected);
|
bullet.setTextureMode(TextureMode.Projected);
|
||||||
bulletCollisionShape = new SphereCollisionShape(0.1f);
|
bulletCollisionShape = new SphereCollisionShape(0.1f);
|
||||||
|
@ -61,7 +61,7 @@ public class TestCollisionGroups extends SimpleApplication {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
|
|
||||||
// Add a physics sphere to the world
|
// Add a physics sphere to the world
|
||||||
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
||||||
|
@ -61,7 +61,7 @@ public class TestCollisionListener extends SimpleApplication implements PhysicsC
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
bullet = new Sphere(32, 32, 0.4f, true, false);
|
bullet = new Sphere(32, 32, 0.4f, true, false);
|
||||||
bullet.setTextureMode(TextureMode.Projected);
|
bullet.setTextureMode(TextureMode.Projected);
|
||||||
bulletCollisionShape = new SphereCollisionShape(0.4f);
|
bulletCollisionShape = new SphereCollisionShape(0.4f);
|
||||||
|
@ -67,7 +67,7 @@ public class TestCollisionShapeFactory extends SimpleApplication {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
createMaterial();
|
createMaterial();
|
||||||
|
|
||||||
Node node = new Node("node1");
|
Node node = new Node("node1");
|
||||||
|
@ -62,7 +62,7 @@ public class TestGhostObject extends SimpleApplication {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
|
|
||||||
// Mesh to be shared across several boxes.
|
// Mesh to be shared across several boxes.
|
||||||
Box boxGeom = new Box(Vector3f.ZERO, 1f, 1f, 1f);
|
Box boxGeom = new Box(Vector3f.ZERO, 1f, 1f, 1f);
|
||||||
|
@ -60,7 +60,7 @@ public class TestKinematicAddToPhysicsSpaceIssue extends SimpleApplication {
|
|||||||
|
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
// Add a physics sphere to the world
|
// Add a physics sphere to the world
|
||||||
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
||||||
physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
|
physicsSphere.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(3, 6, 0));
|
||||||
|
@ -59,7 +59,7 @@ public class TestLocalPhysics extends SimpleApplication {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
|
|
||||||
// Add a physics sphere to the world
|
// Add a physics sphere to the world
|
||||||
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
||||||
|
@ -69,7 +69,7 @@ public class TestPhysicsCar extends SimpleApplication implements ActionListener
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
|
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
|
||||||
setupKeys();
|
setupKeys();
|
||||||
buildPlayer();
|
buildPlayer();
|
||||||
|
@ -76,7 +76,7 @@ public class TestPhysicsHingeJoint extends SimpleApplication implements AnalogLi
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
setupKeys();
|
setupKeys();
|
||||||
setupJoint();
|
setupJoint();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public class TestPhysicsRayCast extends SimpleApplication {
|
|||||||
n.getControl(RigidBodyControl.class).setKinematic(true);
|
n.getControl(RigidBodyControl.class).setKinematic(true);
|
||||||
bulletAppState.getPhysicsSpace().add(n);
|
bulletAppState.getPhysicsSpace().add(n);
|
||||||
rootNode.attachChild(n);
|
rootNode.attachChild(n);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,7 +69,7 @@ public class TestPhysicsReadWrite extends SimpleApplication{
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
physicsRootNode=new Node("PhysicsRootNode");
|
physicsRootNode=new Node("PhysicsRootNode");
|
||||||
rootNode.attachChild(physicsRootNode);
|
rootNode.attachChild(physicsRootNode);
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class TestRagDoll extends SimpleApplication implements ActionListener {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
inputManager.addMapping("Pull ragdoll up", new MouseButtonTrigger(0));
|
inputManager.addMapping("Pull ragdoll up", new MouseButtonTrigger(0));
|
||||||
inputManager.addListener(this, "Pull ragdoll up");
|
inputManager.addListener(this, "Pull ragdoll up");
|
||||||
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
|
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
|
||||||
|
@ -59,7 +59,7 @@ public class TestSimplePhysics extends SimpleApplication {
|
|||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
stateManager.attach(bulletAppState);
|
stateManager.attach(bulletAppState);
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
|
|
||||||
// Add a physics sphere to the world
|
// Add a physics sphere to the world
|
||||||
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
Node physicsSphere = PhysicsTestHelper.createPhysicsTestNode(assetManager, new SphereCollisionShape(1), 1);
|
||||||
|
@ -52,7 +52,7 @@ public class TestSweepTest extends SimpleApplication {
|
|||||||
bulletAppState.getPhysicsSpace().add(obstacle);
|
bulletAppState.getPhysicsSpace().add(obstacle);
|
||||||
rootNode.attachChild(obstacle);
|
rootNode.attachChild(obstacle);
|
||||||
|
|
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
bulletAppState.setDebugEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,26 +69,9 @@ public class TestParticleExportingCloning extends SimpleApplication {
|
|||||||
rootNode.attachChild(emit);
|
rootNode.attachChild(emit);
|
||||||
rootNode.attachChild(emit2);
|
rootNode.attachChild(emit2);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ParticleEmitter emit3 = BinaryExporter.saveAndLoad(assetManager, emit);
|
||||||
try {
|
|
||||||
BinaryExporter.getInstance().save(emit, out);
|
|
||||||
|
|
||||||
BinaryImporter imp = new BinaryImporter();
|
|
||||||
imp.setAssetManager(assetManager);
|
|
||||||
ParticleEmitter emit3 = (ParticleEmitter) imp.load(out.toByteArray());
|
|
||||||
|
|
||||||
emit3.move(-3, 0, 0);
|
emit3.move(-3, 0, 0);
|
||||||
rootNode.attachChild(emit3);
|
rootNode.attachChild(emit3);
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Camera cam2 = cam.clone();
|
|
||||||
// cam.setViewPortTop(0.5f);
|
|
||||||
// cam2.setViewPortBottom(0.5f);
|
|
||||||
// ViewPort vp = renderManager.createMainView("SecondView", cam2);
|
|
||||||
// viewPort.setClearEnabled(false);
|
|
||||||
// vp.attachScene(rootNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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 jme3test.model.anim;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimChannel;
|
||||||
|
import com.jme3.animation.AnimControl;
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.export.binary.BinaryExporter;
|
||||||
|
import com.jme3.light.DirectionalLight;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
|
||||||
|
public class TestModelExportingCloning extends SimpleApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestModelExportingCloning app = new TestModelExportingCloning();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
cam.setLocation(new Vector3f(10f, 3f, 40f));
|
||||||
|
cam.lookAtDirection(Vector3f.UNIT_Z.negate(), Vector3f.UNIT_Y);
|
||||||
|
|
||||||
|
DirectionalLight dl = new DirectionalLight();
|
||||||
|
dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
|
||||||
|
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
|
||||||
|
rootNode.addLight(dl);
|
||||||
|
|
||||||
|
AnimControl control;
|
||||||
|
AnimChannel channel;
|
||||||
|
|
||||||
|
Spatial originalModel = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
|
||||||
|
control = originalModel.getControl(AnimControl.class);
|
||||||
|
channel = control.createChannel();
|
||||||
|
channel.setAnim("Walk");
|
||||||
|
rootNode.attachChild(originalModel);
|
||||||
|
|
||||||
|
Spatial clonedModel = originalModel.clone();
|
||||||
|
clonedModel.move(10, 0, 0);
|
||||||
|
control = clonedModel.getControl(AnimControl.class);
|
||||||
|
channel = control.createChannel();
|
||||||
|
channel.setAnim("push");
|
||||||
|
rootNode.attachChild(clonedModel);
|
||||||
|
|
||||||
|
Spatial exportedModel = BinaryExporter.saveAndLoad(assetManager, originalModel);
|
||||||
|
exportedModel.move(20, 0, 0);
|
||||||
|
control = exportedModel.getControl(AnimControl.class);
|
||||||
|
channel = control.createChannel();
|
||||||
|
channel.setAnim("pull");
|
||||||
|
rootNode.attachChild(exportedModel);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
#extension GL_EXT_texture_array : enable
|
#extension GL_EXT_texture_array : enable
|
||||||
#extension GL_EXT_gpu_shader4 : enable
|
// #extension GL_EXT_gpu_shader4 : enable
|
||||||
|
|
||||||
uniform vec4 m_Color;
|
uniform vec4 m_Color;
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ uniform vec4 m_Color;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_COLORMAP
|
#ifdef HAS_COLORMAP
|
||||||
#if !defined(GL_EXT_texture_array) && !defined(GL_EXT_gpu_shader4)
|
#if !defined(GL_EXT_texture_array)
|
||||||
#error Texture arrays are not supported, but required for this shader.
|
#error Texture arrays are not supported, but required for this shader.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -2,3 +2,9 @@ INCLUDE com/jme3/asset/General.cfg
|
|||||||
|
|
||||||
# IOS specific loaders
|
# IOS specific loaders
|
||||||
LOADER com.jme3.system.ios.IosImageLoader : jpg, bmp, gif, png, jpeg
|
LOADER com.jme3.system.ios.IosImageLoader : jpg, bmp, gif, png, jpeg
|
||||||
|
LOADER com.jme3.audio.plugins.OGGLoader : ogg
|
||||||
|
LOADER com.jme3.material.plugins.J3MLoader : j3m
|
||||||
|
LOADER com.jme3.material.plugins.J3MLoader : j3md
|
||||||
|
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
|
||||||
|
LOADER com.jme3.export.binary.BinaryImporter : j3o
|
||||||
|
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@
|
|||||||
package com.jme3.audio.android;
|
|
||||||
|
|
||||||
import com.jme3.asset.AssetKey;
|
|
||||||
import com.jme3.audio.AudioData;
|
|
||||||
import com.jme3.audio.AudioRenderer;
|
|
||||||
import com.jme3.util.NativeObject;
|
|
||||||
|
|
||||||
public class AndroidAudioData extends AudioData {
|
|
||||||
|
|
||||||
protected AssetKey<?> assetKey;
|
|
||||||
protected float currentVolume = 0f;
|
|
||||||
|
|
||||||
public AndroidAudioData(){
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AndroidAudioData(int id){
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssetKey<?> getAssetKey() {
|
|
||||||
return assetKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAssetKey(AssetKey<?> assetKey) {
|
|
||||||
this.assetKey = assetKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataType getDataType() {
|
|
||||||
return DataType.Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getDuration() {
|
|
||||||
return 0; // TODO: ???
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetObject() {
|
|
||||||
this.id = -1;
|
|
||||||
setUpdateNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteObject(Object rendererObject) {
|
|
||||||
((AudioRenderer)rendererObject).deleteAudioData(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getCurrentVolume() {
|
|
||||||
return currentVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentVolume(float currentVolume) {
|
|
||||||
this.currentVolume = currentVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NativeObject createDestructableClone() {
|
|
||||||
return new AndroidAudioData(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getUniqueId() {
|
|
||||||
return ((long)OBJTYPE_AUDIOBUFFER << 32) | ((long)id);
|
|
||||||
}
|
|
||||||
}
|
|
53
jme3-ios/src/main/java/com/jme3/audio/ios/IosAL.java
Normal file
53
jme3-ios/src/main/java/com/jme3/audio/ios/IosAL.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package com.jme3.audio.ios;
|
||||||
|
|
||||||
|
import com.jme3.audio.openal.AL;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
public final class IosAL implements AL {
|
||||||
|
|
||||||
|
public IosAL() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public native String alGetString(int parameter);
|
||||||
|
|
||||||
|
public native int alGenSources();
|
||||||
|
|
||||||
|
public native int alGetError();
|
||||||
|
|
||||||
|
public native void alDeleteSources(int numSources, IntBuffer sources);
|
||||||
|
|
||||||
|
public native void alGenBuffers(int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alDeleteBuffers(int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alSourceStop(int source);
|
||||||
|
|
||||||
|
public native void alSourcei(int source, int param, int value);
|
||||||
|
|
||||||
|
public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);
|
||||||
|
|
||||||
|
public native void alSourcePlay(int source);
|
||||||
|
|
||||||
|
public native void alSourcePause(int source);
|
||||||
|
|
||||||
|
public native void alSourcef(int source, int param, float value);
|
||||||
|
|
||||||
|
public native void alSource3f(int source, int param, float value1, float value2, float value3);
|
||||||
|
|
||||||
|
public native int alGetSourcei(int source, int param);
|
||||||
|
|
||||||
|
public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alListener(int param, FloatBuffer data);
|
||||||
|
|
||||||
|
public native void alListenerf(int param, float value);
|
||||||
|
|
||||||
|
public native void alListener3f(int param, float value1, float value2, float value3);
|
||||||
|
|
||||||
|
public native void alSource3i(int source, int param, int value1, int value2, int value3);
|
||||||
|
|
||||||
|
}
|
26
jme3-ios/src/main/java/com/jme3/audio/ios/IosALC.java
Normal file
26
jme3-ios/src/main/java/com/jme3/audio/ios/IosALC.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package com.jme3.audio.ios;
|
||||||
|
|
||||||
|
import com.jme3.audio.openal.ALC;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
public final class IosALC implements ALC {
|
||||||
|
|
||||||
|
public IosALC() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public native void createALC();
|
||||||
|
|
||||||
|
public native void destroyALC();
|
||||||
|
|
||||||
|
public native boolean isCreated();
|
||||||
|
|
||||||
|
public native String alcGetString(int parameter);
|
||||||
|
|
||||||
|
public native boolean alcIsExtensionPresent(String extension);
|
||||||
|
|
||||||
|
public native void alcGetInteger(int param, IntBuffer buffer, int size);
|
||||||
|
|
||||||
|
public native void alcDevicePauseSOFT();
|
||||||
|
|
||||||
|
public native void alcDeviceResumeSOFT();
|
||||||
|
}
|
32
jme3-ios/src/main/java/com/jme3/audio/ios/IosEFX.java
Normal file
32
jme3-ios/src/main/java/com/jme3/audio/ios/IosEFX.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package com.jme3.audio.ios;
|
||||||
|
|
||||||
|
import com.jme3.audio.openal.EFX;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
public class IosEFX implements EFX {
|
||||||
|
|
||||||
|
public IosEFX() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alGenEffects(int numEffects, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alEffecti(int effect, int param, int value);
|
||||||
|
|
||||||
|
public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value);
|
||||||
|
|
||||||
|
public native void alDeleteEffects(int numEffects, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alGenFilters(int numFilters, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alFilteri(int filter, int param, int value);
|
||||||
|
|
||||||
|
public native void alFilterf(int filter, int param, float value);
|
||||||
|
|
||||||
|
public native void alDeleteFilters(int numFilters, IntBuffer buffers);
|
||||||
|
|
||||||
|
public native void alEffectf(int effect, int param, float value);
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package com.jme3.audio.plugins;
|
|
||||||
|
|
||||||
import com.jme3.asset.AssetInfo;
|
|
||||||
import com.jme3.asset.AssetLoader;
|
|
||||||
import com.jme3.audio.android.AndroidAudioData;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>AndroidAudioLoader</code> will create an
|
|
||||||
* {@link AndroidAudioData} object with the specified asset key.
|
|
||||||
*/
|
|
||||||
public class AndroidAudioLoader implements AssetLoader {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(AssetInfo assetInfo) throws IOException {
|
|
||||||
AndroidAudioData result = new AndroidAudioData();
|
|
||||||
result.setAssetKey(assetInfo.getKey());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,7 @@ package com.jme3.renderer.ios;
|
|||||||
import com.jme3.renderer.RendererException;
|
import com.jme3.renderer.RendererException;
|
||||||
import com.jme3.renderer.opengl.GL;
|
import com.jme3.renderer.opengl.GL;
|
||||||
import com.jme3.renderer.opengl.GLExt;
|
import com.jme3.renderer.opengl.GLExt;
|
||||||
|
import com.jme3.renderer.opengl.GLFbo;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.BufferOverflowException;
|
import java.nio.BufferOverflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -46,10 +47,13 @@ import java.nio.ShortBuffer;
|
|||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public class IosGL implements GL, GLExt {
|
public class IosGL implements GL, GLExt, GLFbo {
|
||||||
|
|
||||||
private final int[] temp_array = new int[16];
|
private final int[] temp_array = new int[16];
|
||||||
|
|
||||||
|
public void resetStats() {
|
||||||
|
}
|
||||||
|
|
||||||
private static int getLimitBytes(ByteBuffer buffer) {
|
private static int getLimitBytes(ByteBuffer buffer) {
|
||||||
checkLimit(buffer);
|
checkLimit(buffer);
|
||||||
return buffer.limit();
|
return buffer.limit();
|
||||||
@ -90,7 +94,9 @@ public class IosGL implements GL, GLExt {
|
|||||||
if (buffer.remaining() < n) {
|
if (buffer.remaining() < n) {
|
||||||
throw new BufferOverflowException();
|
throw new BufferOverflowException();
|
||||||
}
|
}
|
||||||
|
int pos = buffer.position();
|
||||||
buffer.put(array, 0, n);
|
buffer.put(array, 0, n);
|
||||||
|
buffer.position(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkLimit(Buffer buffer) {
|
private static void checkLimit(Buffer buffer) {
|
||||||
|
@ -1,576 +0,0 @@
|
|||||||
package com.jme3.renderer.ios;
|
|
||||||
|
|
||||||
//import android.graphics.Bitmap;
|
|
||||||
//import android.opengl.ETC1;
|
|
||||||
//import android.opengl.ETC1Util.ETC1Texture;
|
|
||||||
//import android.opengl.JmeIosGLES;
|
|
||||||
//import android.opengl.GLUtils;
|
|
||||||
//import com.jme3.asset.AndroidImageInfo;
|
|
||||||
import com.jme3.renderer.ios.JmeIosGLES;
|
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.renderer.RendererException;
|
|
||||||
import com.jme3.texture.Image;
|
|
||||||
import com.jme3.texture.Image.Format;
|
|
||||||
import com.jme3.util.BufferUtils;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class TextureUtil {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
|
|
||||||
//TODO Make this configurable through appSettings
|
|
||||||
public static boolean ENABLE_COMPRESSION = true;
|
|
||||||
private static boolean NPOT = false;
|
|
||||||
private static boolean ETC1support = false;
|
|
||||||
private static boolean DXT1 = false;
|
|
||||||
private static boolean PVRTC = false;
|
|
||||||
private static boolean DEPTH24_STENCIL8 = false;
|
|
||||||
private static boolean DEPTH_TEXTURE = false;
|
|
||||||
private static boolean RGBA8 = false;
|
|
||||||
|
|
||||||
// Same constant used by both GL_ARM_rgba8 and GL_OES_rgb8_rgba8.
|
|
||||||
private static final int GL_RGBA8 = 0x8058;
|
|
||||||
|
|
||||||
private static final int GL_DXT1 = 0x83F0;
|
|
||||||
private static final int GL_DXT1A = 0x83F1;
|
|
||||||
|
|
||||||
private static final int GL_DEPTH_STENCIL_OES = 0x84F9;
|
|
||||||
private static final int GL_UNSIGNED_INT_24_8_OES = 0x84FA;
|
|
||||||
private static final int GL_DEPTH24_STENCIL8_OES = 0x88F0;
|
|
||||||
|
|
||||||
public static void loadTextureFeatures(String extensionString) {
|
|
||||||
ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture");
|
|
||||||
DEPTH24_STENCIL8 = extensionString.contains("GL_OES_packed_depth_stencil");
|
|
||||||
NPOT = extensionString.contains("GL_IMG_texture_npot")
|
|
||||||
|| extensionString.contains("GL_OES_texture_npot")
|
|
||||||
|| extensionString.contains("GL_NV_texture_npot_2D_mipmap");
|
|
||||||
|
|
||||||
PVRTC = extensionString.contains("GL_IMG_texture_compression_pvrtc");
|
|
||||||
|
|
||||||
DXT1 = extensionString.contains("GL_EXT_texture_compression_dxt1");
|
|
||||||
DEPTH_TEXTURE = extensionString.contains("GL_OES_depth_texture");
|
|
||||||
|
|
||||||
RGBA8 = extensionString.contains("GL_ARM_rgba8") ||
|
|
||||||
extensionString.contains("GL_OES_rgb8_rgba8");
|
|
||||||
|
|
||||||
logger.log(Level.FINE, "Supports ETC1? {0}", ETC1support);
|
|
||||||
logger.log(Level.FINE, "Supports DEPTH24_STENCIL8? {0}", DEPTH24_STENCIL8);
|
|
||||||
logger.log(Level.FINE, "Supports NPOT? {0}", NPOT);
|
|
||||||
logger.log(Level.FINE, "Supports PVRTC? {0}", PVRTC);
|
|
||||||
logger.log(Level.FINE, "Supports DXT1? {0}", DXT1);
|
|
||||||
logger.log(Level.FINE, "Supports DEPTH_TEXTURE? {0}", DEPTH_TEXTURE);
|
|
||||||
logger.log(Level.FINE, "Supports RGBA8? {0}", RGBA8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private static void buildMipmap(Bitmap bitmap, boolean compress) {
|
|
||||||
int level = 0;
|
|
||||||
int height = bitmap.getHeight();
|
|
||||||
int width = bitmap.getWidth();
|
|
||||||
|
|
||||||
logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE");
|
|
||||||
|
|
||||||
JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
|
|
||||||
while (height >= 1 || width >= 1) {
|
|
||||||
//First of all, generate the texture from our bitmap and set it to the according level
|
|
||||||
if (compress) {
|
|
||||||
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height});
|
|
||||||
uploadBitmapAsCompressed(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, false, 0, 0);
|
|
||||||
} else {
|
|
||||||
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height});
|
|
||||||
GLUtils.texImage2D(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height == 1 || width == 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Increase the mipmap level
|
|
||||||
height /= 2;
|
|
||||||
width /= 2;
|
|
||||||
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
|
|
||||||
|
|
||||||
// Recycle any bitmaps created as a result of scaling the bitmap.
|
|
||||||
// Do not recycle the original image (mipmap level 0)
|
|
||||||
if (level != 0) {
|
|
||||||
bitmap.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap = bitmap2;
|
|
||||||
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) {
|
|
||||||
if (bitmap.hasAlpha()) {
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present.");
|
|
||||||
if (subTexture) {
|
|
||||||
GLUtils.texSubImage2D(target, level, x, y, bitmap);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
} else {
|
|
||||||
GLUtils.texImage2D(target, level, bitmap, 0);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Convert to RGB565
|
|
||||||
int bytesPerPixel = 2;
|
|
||||||
Bitmap rgb565 = bitmap.copy(Bitmap.Config.RGB_565, true);
|
|
||||||
|
|
||||||
// Put texture data into ByteBuffer
|
|
||||||
ByteBuffer inputImage = BufferUtils.createByteBuffer(bitmap.getRowBytes() * bitmap.getHeight());
|
|
||||||
rgb565.copyPixelsToBuffer(inputImage);
|
|
||||||
inputImage.position(0);
|
|
||||||
|
|
||||||
// Delete the copied RGB565 image
|
|
||||||
rgb565.recycle();
|
|
||||||
|
|
||||||
// Encode the image into the output bytebuffer
|
|
||||||
int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight());
|
|
||||||
ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize);
|
|
||||||
ETC1.encodeImage(inputImage, bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
bytesPerPixel,
|
|
||||||
bytesPerPixel * bitmap.getWidth(),
|
|
||||||
compressedImage);
|
|
||||||
|
|
||||||
// Delete the input image buffer
|
|
||||||
BufferUtils.destroyDirectBuffer(inputImage);
|
|
||||||
|
|
||||||
// Create an ETC1Texture from the compressed image data
|
|
||||||
ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage);
|
|
||||||
|
|
||||||
// Upload the ETC1Texture
|
|
||||||
if (bytesPerPixel == 2) {
|
|
||||||
int oldSize = (bitmap.getRowBytes() * bitmap.getHeight());
|
|
||||||
int newSize = compressedImage.capacity();
|
|
||||||
logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize});
|
|
||||||
if (subTexture) {
|
|
||||||
JmeIosGLES.glCompressedTexSubImage2D(target,
|
|
||||||
level,
|
|
||||||
x, y,
|
|
||||||
bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
ETC1.ETC1_RGB8_OES,
|
|
||||||
etc1tex.getData().capacity(),
|
|
||||||
etc1tex.getData());
|
|
||||||
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
} else {
|
|
||||||
JmeIosGLES.glCompressedTexImage2D(target,
|
|
||||||
level,
|
|
||||||
ETC1.ETC1_RGB8_OES,
|
|
||||||
bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
0,
|
|
||||||
etc1tex.getData().capacity(),
|
|
||||||
etc1tex.getData());
|
|
||||||
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
|
|
||||||
// JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
|
|
||||||
// } else if (bytesPerPixel == 3) {
|
|
||||||
// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
|
|
||||||
// JmeIosGLES.GL_UNSIGNED_BYTE, etc1Texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferUtils.destroyDirectBuffer(compressedImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>uploadTextureBitmap</code> uploads a native android bitmap
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) {
|
|
||||||
uploadTextureBitmap(target, bitmap, needMips, false, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>uploadTextureBitmap</code> uploads a native android bitmap
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) {
|
|
||||||
boolean recycleBitmap = false;
|
|
||||||
//TODO, maybe this should raise an exception when NPOT is not supported
|
|
||||||
|
|
||||||
boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha();
|
|
||||||
if (needMips && willCompress) {
|
|
||||||
// Image is compressed and mipmaps are desired, generate them
|
|
||||||
// using software.
|
|
||||||
buildMipmap(bitmap, willCompress);
|
|
||||||
} else {
|
|
||||||
if (willCompress) {
|
|
||||||
// Image is compressed but mipmaps are not desired, upload directly.
|
|
||||||
logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated.");
|
|
||||||
uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Image is not compressed, mipmaps may or may not be desired.
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
|
|
||||||
(needMips
|
|
||||||
? " Mipmaps will be generated in HARDWARE"
|
|
||||||
: " Mipmaps are not generated."));
|
|
||||||
if (subTexture) {
|
|
||||||
System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight());
|
|
||||||
GLUtils.texSubImage2D(target, 0, x, y, bitmap);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
} else {
|
|
||||||
GLUtils.texImage2D(target, 0, bitmap, 0);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needMips) {
|
|
||||||
// No pregenerated mips available,
|
|
||||||
// generate from base level if required
|
|
||||||
JmeIosGLES.glGenerateMipmap(target);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recycleBitmap) {
|
|
||||||
bitmap.recycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static void uploadTextureAny(Image img, int target, int index, boolean needMips) {
|
|
||||||
/*
|
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) {
|
|
||||||
logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img);
|
|
||||||
// If image was loaded from asset manager, use fast path
|
|
||||||
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
|
|
||||||
uploadTextureBitmap(target, imageInfo.getBitmap(), needMips);
|
|
||||||
} else {
|
|
||||||
*/
|
|
||||||
logger.log(Level.FINEST, " === Uploading image {0}. Using BUFFER PATH === ", img);
|
|
||||||
boolean wantGeneratedMips = needMips && !img.hasMipmaps();
|
|
||||||
if (wantGeneratedMips && img.getFormat().isCompressed()) {
|
|
||||||
logger.log(Level.WARNING, "Generating mipmaps is only"
|
|
||||||
+ " supported for Bitmap based or non-compressed images!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload using slower path
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
|
|
||||||
(wantGeneratedMips
|
|
||||||
? " Mipmaps will be generated in HARDWARE"
|
|
||||||
: " Mipmaps are not generated."));
|
|
||||||
|
|
||||||
uploadTexture(img, target, index);
|
|
||||||
|
|
||||||
// Image was uploaded using slower path, since it is not compressed,
|
|
||||||
// then compress it
|
|
||||||
if (wantGeneratedMips) {
|
|
||||||
// No pregenerated mips available,
|
|
||||||
// generate from base level if required
|
|
||||||
JmeIosGLES.glGenerateMipmap(target);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void unsupportedFormat(Format fmt) {
|
|
||||||
throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IosGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException {
|
|
||||||
IosGLImageFormat imageFormat = new IosGLImageFormat();
|
|
||||||
switch (fmt) {
|
|
||||||
case Depth32:
|
|
||||||
case Depth32F:
|
|
||||||
throw new UnsupportedOperationException("The image format '"
|
|
||||||
+ fmt + "' is not supported by OpenGL ES 2.0 specification.");
|
|
||||||
case Alpha8:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_ALPHA;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
// Highest precision alpha supported by vanilla OGLES2
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Luminance8:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_LUMINANCE;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
// Highest precision luminance supported by vanilla OGLES2
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Luminance8Alpha8:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_LUMINANCE_ALPHA;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RGB565:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_RGB;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5;
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
|
|
||||||
break;
|
|
||||||
case RGB5A1:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_RGBA;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_5_5_1;
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB5_A1;
|
|
||||||
break;
|
|
||||||
case RGB8:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_RGB;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
// Fallback: Use RGB565 if RGBA8 is not available.
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BGR8:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_RGB;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RGBA8:
|
|
||||||
imageFormat.format = JmeIosGLES.GL_RGBA;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Depth:
|
|
||||||
case Depth16:
|
|
||||||
if (!DEPTH_TEXTURE) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT;
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16;
|
|
||||||
break;
|
|
||||||
case Depth24:
|
|
||||||
case Depth24Stencil8:
|
|
||||||
if (!DEPTH_TEXTURE) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
if (DEPTH24_STENCIL8) {
|
|
||||||
// NEW: True Depth24 + Stencil8 format.
|
|
||||||
imageFormat.format = GL_DEPTH_STENCIL_OES;
|
|
||||||
imageFormat.dataType = GL_UNSIGNED_INT_24_8_OES;
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_DEPTH24_STENCIL8_OES;
|
|
||||||
} else {
|
|
||||||
// Vanilla OGLES2, only Depth16 available.
|
|
||||||
imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT;
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DXT1:
|
|
||||||
if (!DXT1) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
imageFormat.format = GL_DXT1;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
imageFormat.compress = true;
|
|
||||||
break;
|
|
||||||
case DXT1A:
|
|
||||||
if (!DXT1) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
imageFormat.format = GL_DXT1A;
|
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
|
|
||||||
imageFormat.compress = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException("Unrecognized format: " + fmt);
|
|
||||||
}
|
|
||||||
return imageFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class IosGLImageFormat {
|
|
||||||
|
|
||||||
boolean compress = false;
|
|
||||||
int format = -1;
|
|
||||||
int renderBufferStorageFormat = -1;
|
|
||||||
int dataType = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void uploadTexture(Image img,
|
|
||||||
int target,
|
|
||||||
int index) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) {
|
|
||||||
throw new RendererException("This image uses efficient data. "
|
|
||||||
+ "Use uploadTextureBitmap instead.");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Otherwise upload image directly.
|
|
||||||
// Prefer to only use power of 2 textures here to avoid errors.
|
|
||||||
Image.Format fmt = img.getFormat();
|
|
||||||
ByteBuffer data;
|
|
||||||
if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
|
|
||||||
data = img.getData(index);
|
|
||||||
} else {
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = img.getWidth();
|
|
||||||
int height = img.getHeight();
|
|
||||||
|
|
||||||
if (!NPOT && img.isNPOT()) {
|
|
||||||
// Check if texture is POT
|
|
||||||
throw new RendererException("Non-power-of-2 textures "
|
|
||||||
+ "are not supported by the video hardware "
|
|
||||||
+ "and no scaling path available for image: " + img);
|
|
||||||
}
|
|
||||||
IosGLImageFormat imageFormat = getImageFormat(fmt);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] mipSizes = img.getMipMapSizes();
|
|
||||||
int pos = 0;
|
|
||||||
if (mipSizes == null) {
|
|
||||||
if (data != null) {
|
|
||||||
mipSizes = new int[]{data.capacity()};
|
|
||||||
} else {
|
|
||||||
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++) {
|
|
||||||
int mipWidth = Math.max(1, width >> i);
|
|
||||||
int mipHeight = Math.max(1, height >> i);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
data.position(pos);
|
|
||||||
data.limit(pos + mipSizes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageFormat.compress && data != null) {
|
|
||||||
JmeIosGLES.glCompressedTexImage2D(target,
|
|
||||||
i,
|
|
||||||
imageFormat.format,
|
|
||||||
mipWidth,
|
|
||||||
mipHeight,
|
|
||||||
0,
|
|
||||||
data.remaining(),
|
|
||||||
data);
|
|
||||||
} else {
|
|
||||||
JmeIosGLES.glTexImage2D(target,
|
|
||||||
i,
|
|
||||||
imageFormat.format,
|
|
||||||
mipWidth,
|
|
||||||
mipHeight,
|
|
||||||
0,
|
|
||||||
imageFormat.format,
|
|
||||||
imageFormat.dataType,
|
|
||||||
data);
|
|
||||||
}
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
|
|
||||||
pos += mipSizes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the texture currently bound to target at with data from the given
|
|
||||||
* Image at position x and y. The parameter index is used as the zoffset in
|
|
||||||
* case a 3d texture or texture 2d array is being updated.
|
|
||||||
*
|
|
||||||
* @param image Image with the source data (this data will be put into the
|
|
||||||
* texture)
|
|
||||||
* @param target the target texture
|
|
||||||
* @param index the mipmap level to update
|
|
||||||
* @param x the x position where to put the image in the texture
|
|
||||||
* @param y the y position where to put the image in the texture
|
|
||||||
*/
|
|
||||||
public static void uploadSubTexture(
|
|
||||||
Image img,
|
|
||||||
int target,
|
|
||||||
int index,
|
|
||||||
int x,
|
|
||||||
int y) {
|
|
||||||
//TODO:
|
|
||||||
/*
|
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) {
|
|
||||||
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
|
|
||||||
uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Otherwise upload image directly.
|
|
||||||
// Prefer to only use power of 2 textures here to avoid errors.
|
|
||||||
Image.Format fmt = img.getFormat();
|
|
||||||
ByteBuffer data;
|
|
||||||
if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
|
|
||||||
data = img.getData(index);
|
|
||||||
} else {
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = img.getWidth();
|
|
||||||
int height = img.getHeight();
|
|
||||||
|
|
||||||
if (!NPOT && img.isNPOT()) {
|
|
||||||
// Check if texture is POT
|
|
||||||
throw new RendererException("Non-power-of-2 textures "
|
|
||||||
+ "are not supported by the video hardware "
|
|
||||||
+ "and no scaling path available for image: " + img);
|
|
||||||
}
|
|
||||||
IosGLImageFormat imageFormat = getImageFormat(fmt);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] mipSizes = img.getMipMapSizes();
|
|
||||||
int pos = 0;
|
|
||||||
if (mipSizes == null) {
|
|
||||||
if (data != null) {
|
|
||||||
mipSizes = new int[]{data.capacity()};
|
|
||||||
} else {
|
|
||||||
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++) {
|
|
||||||
int mipWidth = Math.max(1, width >> i);
|
|
||||||
int mipHeight = Math.max(1, height >> i);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
data.position(pos);
|
|
||||||
data.limit(pos + mipSizes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageFormat.compress && data != null) {
|
|
||||||
JmeIosGLES.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
} else {
|
|
||||||
JmeIosGLES.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data);
|
|
||||||
JmeIosGLES.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += mipSizes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,6 +40,7 @@ import com.jme3.renderer.ios.IosGL;
|
|||||||
import com.jme3.renderer.opengl.GL;
|
import com.jme3.renderer.opengl.GL;
|
||||||
import com.jme3.renderer.opengl.GLDebugES;
|
import com.jme3.renderer.opengl.GLDebugES;
|
||||||
import com.jme3.renderer.opengl.GLExt;
|
import com.jme3.renderer.opengl.GLExt;
|
||||||
|
import com.jme3.renderer.opengl.GLFbo;
|
||||||
import com.jme3.renderer.opengl.GLRenderer;
|
import com.jme3.renderer.opengl.GLRenderer;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -158,11 +159,11 @@ public class IGLESContext implements JmeContext {
|
|||||||
GLExt glext = (GLExt) gl;
|
GLExt glext = (GLExt) gl;
|
||||||
|
|
||||||
// if (settings.getBoolean("GraphicsDebug")) {
|
// if (settings.getBoolean("GraphicsDebug")) {
|
||||||
gl = new GLDebugES(gl, glext);
|
gl = new GLDebugES(gl, glext, (GLFbo) glext);
|
||||||
glext = (GLExt) gl;
|
glext = (GLExt) gl;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
renderer = new GLRenderer(gl, glext);
|
renderer = new GLRenderer(gl, glext, (GLFbo) glext);
|
||||||
renderer.initialize();
|
renderer.initialize();
|
||||||
|
|
||||||
input = new IosInputHandler();
|
input = new IosInputHandler();
|
||||||
|
@ -33,6 +33,7 @@ package com.jme3.system.ios;
|
|||||||
|
|
||||||
import com.jme3.asset.AssetInfo;
|
import com.jme3.asset.AssetInfo;
|
||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
|
import com.jme3.asset.TextureKey;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Image.Format;
|
import com.jme3.texture.Image.Format;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -45,15 +46,17 @@ import java.io.InputStream;
|
|||||||
public class IosImageLoader implements AssetLoader {
|
public class IosImageLoader implements AssetLoader {
|
||||||
|
|
||||||
public Object load(AssetInfo info) throws IOException {
|
public Object load(AssetInfo info) throws IOException {
|
||||||
InputStream in = info.openStream();
|
boolean flip = ((TextureKey) info.getKey()).isFlipY();
|
||||||
Image img = null;
|
Image img = null;
|
||||||
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
img = loadImageData(Image.Format.RGBA8, in);
|
in = info.openStream();
|
||||||
} catch (Exception e) {
|
img = loadImageData(Format.RGBA8, flip, in);
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
} finally {
|
||||||
|
if (in != null) {
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,5 +67,5 @@ public class IosImageLoader implements AssetLoader {
|
|||||||
* @param inputStream the InputStream to load the image data from
|
* @param inputStream the InputStream to load the image data from
|
||||||
* @return the loaded Image
|
* @return the loaded Image
|
||||||
*/
|
*/
|
||||||
private static native Image loadImageData(Format format, InputStream inputStream);
|
private static native Image loadImageData(Format format, boolean flipY, InputStream inputStream);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,14 @@ import com.jme3.system.AppSettings;
|
|||||||
import com.jme3.system.JmeContext;
|
import com.jme3.system.JmeContext;
|
||||||
import com.jme3.system.JmeSystemDelegate;
|
import com.jme3.system.JmeSystemDelegate;
|
||||||
import com.jme3.system.NullContext;
|
import com.jme3.system.NullContext;
|
||||||
|
import com.jme3.audio.AudioRenderer;
|
||||||
|
import com.jme3.audio.ios.IosAL;
|
||||||
|
import com.jme3.audio.ios.IosALC;
|
||||||
|
//import com.jme3.audio.ios.IosEFX;
|
||||||
|
import com.jme3.audio.openal.AL;
|
||||||
|
import com.jme3.audio.openal.ALAudioRenderer;
|
||||||
|
import com.jme3.audio.openal.ALC;
|
||||||
|
import com.jme3.audio.openal.EFX;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@ -89,7 +97,10 @@ public class JmeIosSystem extends JmeSystemDelegate {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AudioRenderer newAudioRenderer(AppSettings settings) {
|
public AudioRenderer newAudioRenderer(AppSettings settings) {
|
||||||
return null;
|
ALC alc = new IosALC();
|
||||||
|
AL al = new IosAL();
|
||||||
|
//EFX efx = new IosEFX();
|
||||||
|
return new ALAudioRenderer(al, alc, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user