diff --git a/engine/src/android/com/jme3/app/android/AndroidApplication.java b/engine/src/android/com/jme3/app/android/AndroidApplication.java new file mode 100644 index 000000000..973c5ccd7 --- /dev/null +++ b/engine/src/android/com/jme3/app/android/AndroidApplication.java @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2009-2010 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.app.android; + +import java.nio.CharBuffer; +import java.util.concurrent.atomic.AtomicBoolean; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import com.jme3.app.Application; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.input.android.AndroidInput; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial.CullHint; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeSystem; +import com.jme3.util.FastInteger; + + +/** + * AndroidApplication extends the {@link com.jme3.app.Application} + * class to provide default functionality like a first-person camera, + * and an accessible root node that is updated and rendered regularly. + * It will display the current frames-per-second value on-screen. + * + * + */ +public abstract class AndroidApplication extends Application implements DialogInterface.OnClickListener +{ + + protected Node rootNode = new Node("Root Node"); + protected Node guiNode = new Node("Gui Node"); + protected float secondCounter = 0.0f; + protected BitmapText fpsText; + protected CharBuffer textBuf = CharBuffer.allocate(50); + protected char[] fpsBuf = new char[16]; + protected BitmapFont guiFont; + + protected Activity activity; + protected AndroidInput input; + protected final AtomicBoolean loadingFinished; + + public AndroidApplication() + { + this(null, null); + } + + public AndroidApplication(Activity activity, AndroidInput input) + { + super(); + this.activity = activity; + this.input = input; + + loadingFinished = new AtomicBoolean(false); + } + + @Override + public void start() + { + // Set the correct xml parser driver for android + System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver"); + + if (settings == null) + { + settings = new AppSettings(true); + } + + // Use vertex arrays for rendering + settings.putBoolean("USE_VA", true); + // Verbose logging off + settings.putBoolean("VERBOSE_LOGGING", false); + + //re-setting settings they can have been merged from the registry. + setSettings(settings); + super.start(); + } + + + /** + * Retrieves guiNode + * @return guiNode Node object + * + */ + public Node getGuiNode() { + return guiNode; + } + + /** + * Retrieves rootNode + * @return rootNode Node object + * + */ + public Node getRootNode() { + return rootNode; + } + + + /** + * Attaches FPS statistics to guiNode and displays it on the screen. + * + */ + public void loadFPSText() { + guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); + fpsText = new BitmapText(guiFont, false); + fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0); + fpsText.setText("Frames per second"); + guiNode.attachChild(fpsText); + } + + @Override + public void initialize() + { + // Create a default Android assetmanager before Application can create one in super.initialize(); + assetManager = JmeSystem.newAssetManager(null); + super.initialize(); + + guiNode.setQueueBucket(Bucket.Gui); + guiNode.setCullHint(CullHint.Never); + loadFPSText(); + viewPort.attachScene(rootNode); + guiViewPort.attachScene(guiNode); + + // call user code + init(); + + // Start thread for async load + Thread t = new Thread(new Runnable() + { + @Override + public void run () + { + try + { + // call user code + asyncload(); + } + catch (Exception e) + { + handleError("AsyncLoad failed", e); + } + loadingFinished.set(true); + } + }); + t.setDaemon(true); + t.start(); + } + + @Override + public void update() { + super.update(); // makes sure to execute AppTasks + if (speed == 0 || paused) { + return; + } + + float tpf = timer.getTimePerFrame() * speed; + + secondCounter += timer.getTimePerFrame(); + int fps = (int) timer.getFrameRate(); + if (secondCounter >= 5.0f) { + textBuf.clear(); + textBuf.put("Frames per second: "); + FastInteger.toCharArray(fps, fpsBuf); + textBuf.put(fpsBuf); + textBuf.flip(); + fpsText.setText(textBuf); + secondCounter = 0.0f; + } + + // update states + stateManager.update(tpf); + + // simple update and root node + update(tpf); + rootNode.updateLogicalState(tpf); + guiNode.updateLogicalState(tpf); + rootNode.updateGeometricState(); + guiNode.updateGeometricState(); + + // render states + stateManager.render(renderManager); + renderManager.render(tpf); + render(renderManager); + stateManager.postRender(); + } + + public abstract void init(); + + public void update(float tpf) { + } + + public void render(RenderManager rm) { + } + + + /** + * Gets called by a different thread to allow + * async loading of assets. + * + * This means that update and rendering can already + * happen while some assets are still loading. + */ + public void asyncload() + { + + } + + /** + * Called when an error has occured. This is typically + * invoked when an uncought exception is thrown in the render thread. + * @param errorMsg The error message, if any, or null. + * @param t Throwable object, or null. + */ + @Override + public void handleError(final String errorMsg, final Throwable t) + { + + String s = ""; + if (t != null && t.getStackTrace() != null) + { + for (StackTraceElement ste: t.getStackTrace()) + { + s += ste.getClassName() + "." + ste.getMethodName() + "(" + + ste.getLineNumber() + ") "; + } + } + final String sTrace = s; + activity.runOnUiThread(new Runnable() { + @Override + public void run() + { + AlertDialog dialog = new AlertDialog.Builder(activity) + // .setIcon(R.drawable.alert_dialog_icon) + .setTitle(t != null ? t.toString() : "Failed") + .setPositiveButton("Kill", AndroidApplication.this) + .setMessage((errorMsg != null ? errorMsg + ": " : "") + sTrace) + .create(); + dialog.show(); + } + }); + + } + + + /** + * Called by the android alert dialog, terminate the activity and OpenGL rendering + * @param dialog + * @param whichButton + */ + public void onClick(DialogInterface dialog, int whichButton) + { + this.stop(); + activity.finish(); + } + +} diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java index ee2908e87..2c1c9be3d 100644 --- a/engine/src/android/com/jme3/asset/AndroidAssetManager.java +++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java @@ -1,269 +1,118 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package com.jme3.asset; -import com.jme3.asset.plugins.AndroidLocator; -import com.jme3.audio.AudioData; -import com.jme3.audio.AudioKey; -import com.jme3.export.binary.BinaryExporter; -import com.jme3.export.binary.BinaryImporter; -import com.jme3.font.BitmapFont; -import com.jme3.font.plugins.BitmapFontLoader; -import com.jme3.material.Material; -import com.jme3.material.plugins.J3MLoader; -import com.jme3.scene.Spatial; -import com.jme3.shader.Shader; -import com.jme3.shader.ShaderKey; -import com.jme3.shader.plugins.GLSLLoader; -import com.jme3.texture.Image; import com.jme3.texture.Texture; import com.jme3.texture.plugins.AndroidImageLoader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.logging.Level; +import java.net.URL; import java.util.logging.Logger; +import com.jme3.asset.plugins.AndroidLocator; +import com.jme3.asset.plugins.ClasspathLocator; + /** - * AssetManager for Android + * AndroidAssetManager is an implementation of DesktopAssetManager for Android * - * @author Kirill Vainer + * @author larynx */ -public final class AndroidAssetManager implements AssetManager { +public class AndroidAssetManager extends DesktopAssetManager { private static final Logger logger = Logger.getLogger(AndroidAssetManager.class.getName()); - private final AndroidLocator locator = new AndroidLocator(); - private final AndroidImageLoader imageLoader = new AndroidImageLoader(); - private final BinaryImporter modelLoader = new BinaryImporter(); - private final BitmapFontLoader fontLoader = new BitmapFontLoader(); - private final J3MLoader j3mLoader = new J3MLoader(); - private final J3MLoader j3mdLoader = new J3MLoader(); - private final GLSLLoader glslLoader = new GLSLLoader(); - - private final BinaryExporter exporter = new BinaryExporter(); - private final HashMap cache = new HashMap(); - public AndroidAssetManager(){ - this(false); - } - - public AndroidAssetManager(boolean loadDefaults){ - if (loadDefaults){ -// AssetConfig cfg = new AssetConfig(this); -// InputStream stream = AssetManager.class.getResourceAsStream("Desktop.cfg"); -// try{ -// cfg.loadText(stream); -// }catch (IOException ex){ -// logger.log(Level.SEVERE, "Failed to load asset config", ex); -// }finally{ -// if (stream != null) -// try{ -// stream.close(); -// }catch (IOException ex){ -// } -// } + this(null); + } + + @Deprecated + public AndroidAssetManager(boolean loadDefaults){ + //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg")); + this(null); + } + + /** + * AndroidAssetManager constructor + * If URL == null then a default list of locators and loaders for android is set + * @param configFile + */ + public AndroidAssetManager(URL configFile) + { + super(configFile); + System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver"); + + if (configFile == null) + { + // Set Default + this.registerLocator("", AndroidLocator.class); + this.registerLocator("", ClasspathLocator.class); + this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg"); + this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m"); + this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md"); + this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt"); + this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds"); + this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm"); + this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr"); + this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga"); + this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o"); + this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj"); + this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl"); + this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml"); + this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml"); + this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material"); + this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene"); + this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib"); } + logger.info("AndroidAssetManager created."); } - public void registerLoader(String loaderClass, String ... extensions){ - } - - public void registerLocator(String rootPath, String locatorClass, String ... extensions){ - } - - private Object tryLoadFromHD(AssetKey key){ - if (!key.getExtension().equals("fnt")) - return null; - - File f = new File("/sdcard/" + key.getName() + ".opt"); - if (!f.exists()) - return null; - - try { - InputStream stream = new FileInputStream(f); - BitmapFont font = (BitmapFont) modelLoader.load(stream, null, null); - stream.close(); - return font; - } catch (IOException ex){ - } - - return null; - } - - private void tryPutToHD(AssetKey key, Object data){ - if (!key.getExtension().equals("fnt")) - return; - - File f = new File("/sdcard/" + key.getName() + ".opt"); - - try { - BitmapFont font = (BitmapFont) data; - OutputStream stream = new FileOutputStream(f); - exporter.save(font, stream); - stream.close(); - } catch (IOException ex){ - } - } - - public Object loadAsset(AssetKey key){ - logger.info("loadAsset(" + key + ")"); - Object asset; -// Object asset = tryLoadFromHD(key); -// if (asset != null) -// return asset; - - if (key.shouldCache()){ - asset = cache.get(key); - if (asset != null) - return key.createClonedInstance(asset); - } - // find resource - AssetInfo info = locator.locate(this, key); - if (info == null){ - logger.log(Level.WARNING, "Cannot locate resource: "+key.getName()); - return null; - } - - String ex = key.getExtension(); - logger.log(Level.INFO, "Loading asset: "+key.getName()); - try{ - if (ex.equals("png") || ex.equals("jpg") - || ex.equals("jpeg") || ex.equals("j3i")){ - Image image; - if (ex.equals("j3i")){ - image = (Image) modelLoader.load(info); - }else{ - image = (Image) imageLoader.load(info); - } - TextureKey tkey = (TextureKey) key; - asset = image; - Texture tex = (Texture) tkey.postProcess(asset); - tex.setMagFilter(Texture.MagFilter.Nearest); - tex.setAnisotropicFilter(0); - if (tex.getMinFilter().usesMipMapLevels()){ - tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap); - }else{ - tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps); - } - asset = tex; - }else if (ex.equals("j3o")){ - asset = modelLoader.load(info); - }else if (ex.equals("fnt")){ - asset = fontLoader.load(info); - }else if (ex.equals("j3md")){ - asset = j3mdLoader.load(info); - }else if (ex.equals("j3m")){ - asset = j3mLoader.load(info); - }else{ - logger.info("loading asset as glsl shader ..."); - asset = glslLoader.load(info); - // logger.log(Level.WARNING, "No loader registered for type: "+ex); - // return null; - } - - if (key.shouldCache()) - cache.put(key, asset); - -// tryPutToHD(key, asset); - - return key.createClonedInstance(asset); - } catch (Exception e){ - logger.log(Level.WARNING, "Failed to load resource: "+key.getName(), e); - } - return null; - } - - public AssetInfo locateAsset(AssetKey key){ - AssetInfo info = locator.locate(this, key); - if (info == null){ - logger.log(Level.WARNING, "Cannot locate resource: "+key.getName()); - return null; - } - return info; - } - - - - - public Object loadAsset(String name) { - return loadAsset(new AssetKey(name)); - } - - public Spatial loadModel(String name) { - return (Spatial) loadAsset(name); - } - - public Material loadMaterial(String name) { - return (Material) loadAsset(name); - } - - public BitmapFont loadFont(String name){ - return (BitmapFont) loadAsset(name); - } - + /** + * Loads a texture. + * + * @return + */ + @Override public Texture loadTexture(TextureKey key){ - return (Texture) loadAsset(key); - } - - public Texture loadTexture(String name){ - return loadTexture(new TextureKey(name, false)); - } - - public Shader loadShader(ShaderKey key){ - logger.info("loadShader(" + key + ")"); - - String vertName = key.getVertName(); - String fragName = key.getFragName(); - - String vertSource = (String) loadAsset(new AssetKey(vertName)); - String fragSource = (String) loadAsset(new AssetKey(fragName)); - - Shader s = new Shader(key.getLanguage()); - s.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled()); - s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled()); - - logger.info("returing shader: [" + s + "]"); - return s; - } - - - public void registerLocator(String rootPath, String locatorClassName) { - throw new UnsupportedOperationException("Not supported yet."); - } - - public AudioData loadAudio(AudioKey key) { - throw new UnsupportedOperationException("Not supported yet."); - } - - public AudioData loadAudio(String name) { - throw new UnsupportedOperationException("Not supported yet."); - } - - public Spatial loadModel(ModelKey key) { - throw new UnsupportedOperationException("Not supported yet."); - } - - /* new */ - - private AssetEventListener eventListener = null; - - public void setAssetEventListener(AssetEventListener listener){ - eventListener = listener; - } - - public void registerLocator(String rootPath, Class locatorClass){ - logger.warning("not implemented."); - } - - public void registerLoader(Class loader, String ... extensions){ - logger.warning("not implemented."); + Texture tex = (Texture) loadAsset(key); + + // Needed for Android + tex.setMagFilter(Texture.MagFilter.Nearest); + tex.setAnisotropicFilter(0); + if (tex.getMinFilter().usesMipMapLevels()){ + tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap); + }else{ + tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps); + } + return tex; } - - - - + } diff --git a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java index adbe7603d..378d7d4a6 100644 --- a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java +++ b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java @@ -1,6 +1,5 @@ package com.jme3.asset.plugins; -import android.content.res.AssetManager; import android.content.res.Resources; import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetKey; @@ -8,20 +7,22 @@ import com.jme3.asset.AssetLocator; import com.jme3.system.JmeSystem; import java.io.IOException; import java.io.InputStream; +import java.util.logging.Level; import java.util.logging.Logger; public class AndroidLocator implements AssetLocator { private static final Logger logger = Logger.getLogger(AndroidLocator.class.getName()); private Resources resources; - private AssetManager androidManager; + private android.content.res.AssetManager androidManager; + private String rootPath = ""; private class AndroidAssetInfo extends AssetInfo { private final InputStream in; - public AndroidAssetInfo(com.jme3.asset.AssetManager manager, AssetKey key, - InputStream in){ + public AndroidAssetInfo(com.jme3.asset.AssetManager manager, AssetKey key, InputStream in) + { super(manager, key); this.in = in; } @@ -33,28 +34,40 @@ public class AndroidLocator implements AssetLocator { } - public AndroidLocator(){ + public AndroidLocator() + { resources = JmeSystem.getResources(); androidManager = resources.getAssets(); } - public void setRootPath(String rootPath) { + public void setRootPath(String rootPath) + { + this.rootPath = rootPath; } - public AssetInfo locate(com.jme3.asset.AssetManager manager, AssetKey key) { + @SuppressWarnings("rawtypes") + @Override + public AssetInfo locate(com.jme3.asset.AssetManager manager, AssetKey key) + { InputStream in = null; - try { - in = androidManager.open(key.getName()); + String sAssetPath = rootPath + key.getName(); + // Fix path issues + if (sAssetPath.startsWith("/")) + { + // Remove leading / + sAssetPath = sAssetPath.substring(1); + } + sAssetPath = sAssetPath.replace("//", "/"); + try { + in = androidManager.open(sAssetPath); if (in == null) return null; return new AndroidAssetInfo(manager, key, in); - } catch (IOException ex) { - if (in != null) - try { - in.close(); - } catch (IOException ex1) { - } + } + catch (IOException ex) + { + logger.log(Level.WARNING, "Failed to locate {0} ", sAssetPath); } return null; } diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java index 3be23f644..9fa50fec0 100644 --- a/engine/src/android/com/jme3/input/android/AndroidInput.java +++ b/engine/src/android/com/jme3/input/android/AndroidInput.java @@ -2,124 +2,45 @@ package com.jme3.input.android; import java.util.List; import java.util.ArrayList; +import java.util.logging.Logger; import android.content.Context; import android.opengl.GLSurfaceView; import android.util.AttributeSet; +import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import com.jme3.input.android.TouchEvent; import com.jme3.input.KeyInput; import com.jme3.input.MouseInput; import com.jme3.input.RawInputListener; import com.jme3.input.event.KeyInputEvent; import com.jme3.input.event.MouseButtonEvent; import com.jme3.input.event.MouseMotionEvent; +import com.jme3.math.Vector2f; -public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput { + +public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput, + GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener +{ + private final static Logger logger = Logger.getLogger(AndroidInput.class.getName()); - private RawInputListener listener; - private int lastX = -1, lastY = -1; - - private static final char[] ANDROID_TO_JME_CHR = { - 0x0,// unknown - 0x0,// soft left - 0x0,// soft right - 0x0,// home - 0x0,// back - 0x0,// call - 0x0,// endcall - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '*', - '#', - 0x0,//dpad_up - 0x0,//dpad_down - 0x0,//dpad_left - 0x0,//dpad_right - 0x0,//dpad_center - 0x0,//volume up - 0x0,//volume down - 0x0,//power - 0x0,//camera - 0x0,//clear - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - 'g', - 'h', - 'i', - 'j', - 'k', - 'l', - 'm', - 'n', - 'o', - 'p', - 'q', - 'r', - 's', - 't', - 'u', - 'v', - 'w', - 'x', - 'y', - 'z', - ',', - '.', - - 0x0,//left alt - 0x0,//right alt - 0x0,//left ctrl - 0x0,//right ctrl - -// 0x0,//fn -// 0x0,//cap - - '\t', - ' ', - 0x0,//sym(bol) - 0x0,//explorer - 0x0,//envelope - '\n',//newline - 0x0,//delete - '`', - '-', - '=', - '[', - ']', - '\\',//backslash - ';', - '\'',//apostrophe - '/',//slash - '@',//at - 0x0,//num - 0x0,//headset hook - 0x0,//focus - 0x0, - 0x0,//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 - }; + private RawInputListener listenerRaw = null; + private AndroidTouchInputListener listenerTouch = null; + private ScaleGestureDetector scaledetector; + private GestureDetector detector; + private Vector2f lastPos = new Vector2f(); + private boolean dragging = false; + + private List currentEvents = new ArrayList(); + private final static int MAX_EVENTS = 1024; + + + private boolean FIRE_MOUSE_EVENTS = true; + + private static final int[] ANDROID_TO_JME = { 0x0, // unknown 0x0, // key code soft left @@ -223,170 +144,170 @@ public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput 0x0,//mute }; -// private int[] keyMap = { -// 0x0, -// KeyEvent.KEYCODE_BACK, // ESC key -// -// KeyEvent.KEYCODE_1, -// KeyEvent.KEYCODE_2, -// KeyEvent.KEYCODE_3, -// KeyEvent.KEYCODE_4, -// KeyEvent.KEYCODE_5, -// KeyEvent.KEYCODE_6, -// KeyEvent.KEYCODE_7, -// KeyEvent.KEYCODE_8, -// KeyEvent.KEYCODE_9, -// KeyEvent.KEYCODE_0, -// KeyEvent.KEYCODE_MINUS, -// KeyEvent.KEYCODE_EQUALS, -// KeyEvent.KEYCODE_BACK, -// KeyEvent.KEYCODE_TAB, -// KeyEvent.KEYCODE_Q, -// KeyEvent.KEYCODE_W, -// KeyEvent.KEYCODE_E, -// KeyEvent.KEYCODE_R, -// KeyEvent.KEYCODE_T, -// KeyEvent.KEYCODE_Y, -// KeyEvent.KEYCODE_U, -// KeyEvent.KEYCODE_I, -// KeyEvent.KEYCODE_O, -// KeyEvent.KEYCODE_P, -// KeyEvent.KEYCODE_LEFT_BRACKET, -// KeyEvent.KEYCODE_RIGHT_BRACKET, -// KeyEvent.KEYCODE_ENTER, -// KeyEvent.KEYCODE_SOFT_LEFT, // Left Ctrl -// KeyEvent.KEYCODE_A, -// KeyEvent.KEYCODE_S, -// KeyEvent.KEYCODE_D, -// KeyEvent.KEYCODE_F, -// KeyEvent.KEYCODE_G, -// KeyEvent.KEYCODE_H, -// KeyEvent.KEYCODE_J, -// KeyEvent.KEYCODE_K, -// KeyEvent.KEYCODE_L, -// KeyEvent.KEYCODE_SEMICOLON, -// KeyEvent.KEYCODE_APOSTROPHE, -// KeyEvent.KEYCODE_GRAVE, -// KeyEvent.KEYCODE_SHIFT_LEFT, -// KeyEvent.KEYCODE_BACKSLASH, -// KeyEvent.KEYCODE_Z, -// KeyEvent.KEYCODE_X, -// KeyEvent.KEYCODE_C, -// KeyEvent.KEYCODE_V, -// KeyEvent.KEYCODE_B, -// KeyEvent.KEYCODE_N, -// KeyEvent.KEYCODE_M, -// -// KeyEvent.KEYCODE_COMMA, -// KeyEvent.KEYCODE_PERIOD, -// KeyEvent.KEYCODE_SLASH, -// KeyEvent.KEYCODE_SHIFT_RIGHT, -// KeyEvent.KEYCODE_STAR, -// -// KeyEvent.KEYCODE_ALT_LEFT, -// KeyEvent.KEYCODE_SPACE, -// -// 0x0, // no caps lock -// -// 0x0, // F1 -// 0x0, // F2 -// 0x0, // F3 -// 0x0, // F4 -// 0x0, // F5 -// 0x0, // F6 -// 0x0, // F7 -// 0x0, // F8 -// 0x0, // F9 -// 0x0, // F10 -// -// KeyEvent.KEYCODE_NUM, -// 0x0, // scroll lock -// -// 0x0, // numpad7 -// 0x0, // numpad8 -// 0x0, // numpad9 -// -// KeyEvent. -// } - - public AndroidInput(Context ctx, AttributeSet attribs){ + public AndroidInput(Context ctx, AttributeSet attribs) + { super(ctx, attribs); + detector=new GestureDetector(this); + scaledetector=new ScaleGestureDetector(ctx, this); } - public AndroidInput(Context ctx){ + public AndroidInput(Context ctx) + { super(ctx); + detector=new GestureDetector(this); + scaledetector=new ScaleGestureDetector(ctx, this); } + /** + * onTouchEvent gets called from android thread on touchpad events + */ @Override - public boolean onTouchEvent(MotionEvent motionEvent){ - int newX = getWidth() - (int) motionEvent.getX(); - int newY = (int) motionEvent.getY(); - - - switch (motionEvent.getAction()){ + public boolean onTouchEvent(MotionEvent event) + { + boolean bWasHandled = false; + MouseButtonEvent btn; + TouchEvent touch; + + // Send the raw event + processEvent(event); + + // Try to detect gestures + this.detector.onTouchEvent(event); + this.scaledetector.onTouchEvent(event); + + + switch (event.getAction()) + { case MotionEvent.ACTION_DOWN: - MouseButtonEvent btn = new MouseButtonEvent(0, true, newX, newY); - btn.setTime(motionEvent.getEventTime()); - processEvent(btn); - // listener.onMouseButtonEvent(btn); - lastX = -1; - lastY = -1; - return true; + + // Store current pos + lastPos.set(event.getX(),event.getY()); + + if (FIRE_MOUSE_EVENTS) + { + // Handle mouse events + btn = new MouseButtonEvent(0, true, (int)lastPos.getX(), (int)lastPos.getY()); + btn.setTime(event.getEventTime()); + processEvent(btn); + } + + // Handle gesture events + touch = new TouchEvent(TouchEvent.Type.GRABBED, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + + bWasHandled = true; + break; + case MotionEvent.ACTION_UP: - MouseButtonEvent btn2 = new MouseButtonEvent(0, false, newX, newY); - btn2.setTime(motionEvent.getEventTime()); - processEvent(btn2); - // listener.onMouseButtonEvent(btn2); - lastX = -1; - lastY = -1; - return true; + + if (FIRE_MOUSE_EVENTS) + { + // Handle mouse events + btn = new MouseButtonEvent(0, false, (int)event.getX(), (int)event.getY()); + btn.setTime(event.getEventTime()); + processEvent(btn); + } + // Handle gesture events + if(dragging) + { + touch = new TouchEvent(TouchEvent.Type.DRAGGED, TouchEvent.Operation.STOPPED,event.getX(),event.getY(),event.getX()-lastPos.getX(),event.getY()-lastPos.getY(),null); + processEvent(touch); + } + touch = new TouchEvent(TouchEvent.Type.RELEASED, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + dragging=false; + bWasHandled = true; + break; case MotionEvent.ACTION_MOVE: - // int newX = getWidth() - (int) motionEvent.getX(); - // int newY = (int) motionEvent.getY(); - int dx; - int dy; - if (lastX != -1){ - dx = newX - lastX; - dy = newY - lastY; - }else{ - dx = 0; - dy = 0; + if(!scaledetector.isInProgress()) + { + if(!dragging) + touch = new TouchEvent(TouchEvent.Type.DRAGGED, TouchEvent.Operation.STARTED,event.getX(),event.getY(),event.getX()-lastPos.getX(),event.getY()-lastPos.getY(),null); + else + touch = new TouchEvent(TouchEvent.Type.DRAGGED, TouchEvent.Operation.RUNNING,event.getX(),event.getY(),event.getX()-lastPos.getX(),event.getY()-lastPos.getY(),null); + + processEvent(touch); + dragging=true; } - lastX = newX; - lastY = newY; - MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0); - mot.setTime(motionEvent.getEventTime()); - processEvent(mot); - //listener.onMouseMotionEvent(mot); - try{ - Thread.sleep(15); - } catch (InterruptedException ex) { + if (FIRE_MOUSE_EVENTS) + { + int newX = getWidth() - (int) event.getX(); + int newY = (int) event.getY(); + int dx; + int dy; + if (lastPos.getX() != -1){ + dx = newX - (int)lastPos.getX(); + dy = newY - (int)lastPos.getY(); + }else{ + dx = 0; + dy = 0; + } + MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0); + mot.setTime(event.getEventTime()); + processEvent(mot); } - return true; + bWasHandled = true; + break; + + // TODO: implement motion events + case MotionEvent.ACTION_POINTER_UP: + break; + + case MotionEvent.ACTION_POINTER_DOWN: + break; + + case MotionEvent.ACTION_OUTSIDE: + break; + + case MotionEvent.ACTION_CANCEL: + break; } - return false; + return bWasHandled; } @Override public boolean onKeyDown (int keyCode, KeyEvent event) { + + // Send the raw event + processEvent(event); + int jmeCode = ANDROID_TO_JME[keyCode]; - String str = event.getCharacters(); - char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0; - KeyInputEvent evt = new KeyInputEvent(jmeCode, c, true, false); - processEvent(evt); - // listener.onKeyEvent(evt); - return false; + if (jmeCode != 0) + { + String str = event.getCharacters(); + char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0; + KeyInputEvent evt = new KeyInputEvent(jmeCode, c, true, false); + logger.info("onKeyDown " + evt); + processEvent(evt); + } + // Handle all keys ourself, except the back button (4) + if (keyCode == 4) + return false; + else + return true; } @Override public boolean onKeyUp (int keyCode, KeyEvent event) { + + // Send the raw event + processEvent(event); + int jmeCode = ANDROID_TO_JME[keyCode]; - String str = event.getCharacters(); - char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0; - KeyInputEvent evt = new KeyInputEvent(jmeCode, c, false, false); - processEvent(evt); - //listener.onKeyEvent(evt); - return false; + if (jmeCode != 0) + { + String str = event.getCharacters(); + char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0; + KeyInputEvent evt = new KeyInputEvent(jmeCode, c, false, false); + logger.info("onKeyUp " + evt); + processEvent(evt); + } + + // Handle all keys ourself, except the back button (4) + if (keyCode == 4) + return false; + else + return true; } public void setCursorVisible(boolean visible){ @@ -410,39 +331,130 @@ public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput return true; } - // XXX: android does not have an Event interface? - private List currentEvents = new ArrayList(); - private final static int MAX_EVENTS = 1024; - private void processEvent(Object event) { + private void processEvent(Object event) + { synchronized (currentEvents) { if (currentEvents.size() < MAX_EVENTS) currentEvents.add(event); } } + Object event; private void generateEvents() { - synchronized (currentEvents) { - for (Object event: currentEvents) { - if (event instanceof MouseButtonEvent) { - listener.onMouseButtonEvent((MouseButtonEvent) event); - } else if (event instanceof MouseMotionEvent) { - listener.onMouseMotionEvent((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - listener.onKeyEvent((KeyInputEvent) event); - } - } - currentEvents.clear(); - } + if (listenerRaw != null) + { + synchronized (currentEvents) { + //for (Object event: currentEvents) { + for (int i = 0; i < currentEvents.size(); i++) { + event = currentEvents.get(i); + if (event instanceof MouseButtonEvent) { + listenerRaw.onMouseButtonEvent((MouseButtonEvent) event); + } else if (event instanceof MouseMotionEvent) { + listenerRaw.onMouseMotionEvent((MouseMotionEvent) event); + } else if (event instanceof KeyInputEvent) { + listenerRaw.onKeyEvent((KeyInputEvent) event); + } else if (event instanceof TouchEvent) { + if (listenerTouch != null) + listenerTouch.onTouchEvent((TouchEvent) event); + } else if (event instanceof MotionEvent) { + if (listenerTouch != null) + listenerTouch.onMotionEvent((MotionEvent) event); + } else if (event instanceof KeyEvent) { + if (listenerTouch != null) + listenerTouch.onAndroidKeyEvent((KeyEvent) event); + } + } + currentEvents.clear(); + } + } } public void setInputListener(RawInputListener listener) { - this.listener = listener; + this.listenerRaw = listener; + } + + public void setInputListener(AndroidTouchInputListener listener) { + this.listenerRaw = listener; + this.listenerTouch = listener; } public long getInputTimeNanos() { return System.nanoTime(); } + + // --------------- Gesture detected callback events ---------------------------------- + + public boolean onDown(MotionEvent event) + { + return false; + } + + public void onLongPress(MotionEvent event) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.LONGPRESSED, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + } + + public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.FLING, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + return true; + } + + public boolean onSingleTapConfirmed(MotionEvent event) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.TAP, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + return true; + } + + public boolean onDoubleTap(MotionEvent event) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.DOUBLETAP, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + return true; + } + + public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.SCALE, TouchEvent.Operation.STARTED,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),0,0,new float[]{scaleGestureDetector.getCurrentSpan(),scaleGestureDetector.getScaleFactor()}); + processEvent(touch); + return true; + } + + public boolean onScale(ScaleGestureDetector scaleGestureDetector) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.SCALE, TouchEvent.Operation.RUNNING,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),0,0,new float[]{scaleGestureDetector.getCurrentSpan(),scaleGestureDetector.getScaleFactor()}); + processEvent(touch); + return false; + } + + public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.SCALE, TouchEvent.Operation.STOPPED,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),0,0,new float[]{scaleGestureDetector.getCurrentSpan(),scaleGestureDetector.getScaleFactor()}); + processEvent(touch); + } + + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + // TODO Auto-generated method stub + return false; + } + + public void onShowPress(MotionEvent e) { + // TODO Auto-generated method stub + + } + + public boolean onSingleTapUp(MotionEvent event) + { + TouchEvent touch = new TouchEvent(TouchEvent.Type.TAP, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null); + processEvent(touch); + return true; + } + } diff --git a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java new file mode 100644 index 000000000..003a028e9 --- /dev/null +++ b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java @@ -0,0 +1,19 @@ +package com.jme3.input.android; + +import com.jme3.input.RawInputListener; + +import android.view.KeyEvent; +import android.view.MotionEvent; + +/** + * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens + * For use with class AndroidInput + * @author larynx + * + */ +public interface AndroidTouchInputListener extends RawInputListener +{ + public void onTouchEvent(TouchEvent evt); + public void onMotionEvent(MotionEvent evt); + public void onAndroidKeyEvent(KeyEvent evt); +} diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java index 9562c1775..4c30650e5 100644 --- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java @@ -76,53 +76,10 @@ import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; - -/* -//import org.lwjgl.opengl.ARBGeometryShader4; -//import org.lwjgl.opengl.ARBHalfFloatVertex; -//import org.lwjgl.opengl.ARBVertexArrayObject; -//import org.lwjgl.opengl.ARBHalfFloatVertex; -//import org.lwjgl.opengl.ARBVertexArrayObject; -import org.lwjgl.opengl.ARBDrawBuffers; -//import org.lwjgl.opengl.ARBDrawInstanced; -import org.lwjgl.opengl.ARBDrawInstanced; -import org.lwjgl.opengl.ARBMultisample; -import org.lwjgl.opengl.ContextCapabilities; -import org.lwjgl.opengl.EXTTextureArray; -import org.lwjgl.opengl.EXTTextureFilterAnisotropic; -import org.lwjgl.opengl.GLContext; -import org.lwjgl.opengl.NVHalfFloat; - -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL12.*; -import static org.lwjgl.opengl.GL13.*; -import static org.lwjgl.opengl.GL14.*; -import static org.lwjgl.opengl.GL15.*; -import static org.lwjgl.opengl.GL20.*; - -import static org.lwjgl.opengl.EXTFramebufferObject.*; -import static org.lwjgl.opengl.EXTFramebufferMultisample.*; -import static org.lwjgl.opengl.EXTFramebufferBlit.*; -import org.lwjgl.opengl.ARBShaderObjects.*; -import org.lwjgl.opengl.ARBVertexArrayObject; -//import static org.lwjgl.opengl.ARBDrawInstanced.*; -*/ - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.opengles.GL10; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.opengl.GLES10; import android.opengl.GLES11; import android.opengl.GLES20; -import android.opengl.GLSurfaceView; -import android.opengl.GLUtils; -import android.opengl.Matrix; -import android.os.SystemClock; -import android.util.Log; @@ -168,14 +125,14 @@ public class OGLESShaderRenderer implements Renderer { private int clipX, clipY, clipW, clipH; - private final GL10 gl; + //private final GL10 gl; private boolean powerOf2 = false; private boolean verboseLogging = false; - private boolean useVBO = true; + private boolean useVBO = false; - public OGLESShaderRenderer(GL10 gl) { - this.gl = gl; + public OGLESShaderRenderer() { + } public void setUseVA(boolean value) { @@ -1869,7 +1826,7 @@ public class OGLESShaderRenderer implements Renderer { return; } for (int i = 0; i < 6; i++){ - TextureUtil.uploadTexture(gl, img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, true, powerOf2); + TextureUtil.uploadTexture(img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, true, powerOf2); } }/*else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT){ List data = img.getData(); @@ -1881,7 +1838,7 @@ public class OGLESShaderRenderer implements Renderer { TextureUtil.uploadTexture(img, target, i, 0, tdc); } }*/else{ - TextureUtil.uploadTexture(gl, img, target, 0, 0, tdc, true, powerOf2); + TextureUtil.uploadTexture(img, target, 0, 0, tdc, true, powerOf2); if (verboseLogging) logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)"); @@ -2843,11 +2800,18 @@ public class OGLESShaderRenderer implements Renderer { public void setAlphaToCoverage(boolean value) { - // TODO Auto-generated method stub + if (value) { + GLES20.glEnable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + GLES20.glDisable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE); + } } - public void invalidateState(){ - // TODO invalidateState + @Override + public void invalidateState() + { + context.reset(); + boundShader = null; + lastFb = null; } - } diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java index 08ca07051..f1eea426d 100644 --- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java +++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java @@ -1,6 +1,7 @@ package com.jme3.renderer.android; import android.graphics.Bitmap; +import android.opengl.GLES20; import android.opengl.GLUtils; import com.jme3.math.FastMath; import com.jme3.texture.Image; @@ -36,7 +37,7 @@ public class TextureUtil { } } - private static void buildMipmap(GL10 gl, Bitmap bitmap) { + private static void buildMipmap(Bitmap bitmap) { int level = 0; int height = bitmap.getHeight(); int width = bitmap.getWidth(); @@ -61,7 +62,7 @@ public class TextureUtil { } } - private static void uploadTextureBitmap(GL10 gl, Bitmap bitmap, boolean generateMips, boolean powerOf2){ + private static void uploadTextureBitmap(Bitmap bitmap, boolean generateMips, boolean powerOf2){ if (!powerOf2){ int width = bitmap.getWidth(); int height = bitmap.getHeight(); @@ -76,7 +77,7 @@ public class TextureUtil { } if (generateMips){ - buildMipmap(gl, bitmap); + buildMipmap(bitmap); }else{ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); @@ -84,18 +85,17 @@ public class TextureUtil { } public static void uploadTexture( - GL10 gl, Image img, int target, int index, int border, - boolean tdc, + boolean tdc, boolean generateMips, boolean powerOf2){ if (img.getEfficentData() instanceof Bitmap){ Bitmap bitmap = (Bitmap) img.getEfficentData(); - uploadTextureBitmap(gl, bitmap, generateMips, powerOf2); + uploadTextureBitmap(bitmap, generateMips, powerOf2); // img.setEfficentData(null); return; } @@ -118,71 +118,71 @@ public class TextureUtil { switch (fmt){ case Alpha16: - format = gl.GL_ALPHA; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_ALPHA; + dataType = GL10.GL_UNSIGNED_BYTE; break; case Alpha8: - format = gl.GL_ALPHA; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_ALPHA; + dataType = GL10.GL_UNSIGNED_BYTE; break; case Luminance8: - format = gl.GL_LUMINANCE; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_LUMINANCE; + dataType = GL10.GL_UNSIGNED_BYTE; break; case Luminance8Alpha8: - format = gl.GL_LUMINANCE_ALPHA; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_LUMINANCE_ALPHA; + dataType = GL10.GL_UNSIGNED_BYTE; break; case Luminance16Alpha16: - format = gl.GL_LUMINANCE_ALPHA; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_LUMINANCE_ALPHA; + dataType = GL10.GL_UNSIGNED_BYTE; break; case Luminance16: - format = gl.GL_LUMINANCE; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_LUMINANCE; + dataType = GL10.GL_UNSIGNED_BYTE; break; case RGB565: - format = gl.GL_RGB; - dataType = gl.GL_UNSIGNED_SHORT_5_6_5; + format = GL10.GL_RGB; + dataType = GL10.GL_UNSIGNED_SHORT_5_6_5; break; case ARGB4444: - format = gl.GL_RGBA; - dataType = gl.GL_UNSIGNED_SHORT_4_4_4_4; + format = GL10.GL_RGBA; + dataType = GL10.GL_UNSIGNED_SHORT_4_4_4_4; break; case RGB10: - format = gl.GL_RGB; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_RGB; + dataType = GL10.GL_UNSIGNED_BYTE; break; case RGB16: - format = gl.GL_RGB; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_RGB; + dataType = GL10.GL_UNSIGNED_BYTE; break; case RGB5A1: - format = gl.GL_RGBA; - dataType = gl.GL_UNSIGNED_SHORT_5_5_5_1; + format = GL10.GL_RGBA; + dataType = GL10.GL_UNSIGNED_SHORT_5_5_5_1; break; case RGB8: - format = gl.GL_RGB; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_RGB; + dataType = GL10.GL_UNSIGNED_BYTE; break; case BGR8: - format = gl.GL_RGB; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_RGB; + dataType = GL10.GL_UNSIGNED_BYTE; break; case RGBA16: - format = gl.GL_RGBA; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_RGBA; + dataType = GL10.GL_UNSIGNED_BYTE; break; case RGBA8: - format = gl.GL_RGBA; - dataType = gl.GL_UNSIGNED_BYTE; + format = GL10.GL_RGBA; + dataType = GL10.GL_UNSIGNED_BYTE; break; default: throw new UnsupportedOperationException("Unrecognized format: "+fmt); } if (data != null) - gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1); + GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); int[] mipSizes = img.getMipMapSizes(); int pos = 0; @@ -197,7 +197,7 @@ public class TextureUtil { // of more than paletted compressions is added.. if (compress){ data.clear(); - gl.glCompressedTexImage2D(gl.GL_TEXTURE_2D, + GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 1 - mipSizes.length, format, width, @@ -219,7 +219,7 @@ public class TextureUtil { } if (compress && data != null){ - gl.glCompressedTexImage2D(gl.GL_TEXTURE_2D, + GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, i, format, mipWidth, @@ -228,7 +228,7 @@ public class TextureUtil { data.remaining(), data); }else{ - gl.glTexImage2D(gl.GL_TEXTURE_2D, + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, i, format, mipWidth, diff --git a/engine/src/android/com/jme3/system/JmeSystem.java b/engine/src/android/com/jme3/system/JmeSystem.java index f7e07d6cb..c3fb96c2e 100644 --- a/engine/src/android/com/jme3/system/JmeSystem.java +++ b/engine/src/android/com/jme3/system/JmeSystem.java @@ -6,8 +6,8 @@ import com.jme3.asset.AndroidAssetManager; import com.jme3.asset.AssetManager; import com.jme3.audio.AudioNode; import com.jme3.audio.AudioData; -import com.jme3.audio.AudioRenderer; import com.jme3.audio.AudioParam; +import com.jme3.audio.AudioRenderer; import com.jme3.audio.Environment; import com.jme3.audio.Listener; import com.jme3.audio.ListenerParam; @@ -28,6 +28,7 @@ public class JmeSystem { private static final Logger logger = Logger.getLogger(JmeSystem.class.getName()); private static boolean initialized = false; + private static boolean lowPermissions = false; private static Resources res; public static void initialize(AppSettings settings){ @@ -51,7 +52,15 @@ public class JmeSystem { } public static String getFullName(){ - return "jMonkey Engine 3 ALPHA 0.50"; + return "jMonkey Engine 3 ALPHA 0.50 Android"; + } + + public static void setLowPermissions(boolean lowPerm){ + lowPermissions = lowPerm; + } + + public static boolean isLowPermissions() { + return lowPermissions; } public static JmeContext newContext(AppSettings settings, Type contextType) { @@ -71,8 +80,15 @@ public class JmeSystem { public void initialize() {} public void update(float tpf) {} public void cleanup() {} - public void updateListenerParam(Listener listener, ListenerParam parameter) {} - public void updateSourceParam(AudioNode node, AudioParam parameter) {} + public void updateListenerParam(Listener listener, + ListenerParam param) { + // TODO Auto-generated method stub + + } + public void updateSourceParam(AudioNode src, AudioParam param) { + // TODO Auto-generated method stub + + } }; } @@ -85,16 +101,16 @@ public class JmeSystem { } public static AssetManager newAssetManager(){ - logger.info("newAssetManager()"); - return new AndroidAssetManager(true); + logger.info("newAssetManager()"); + return new AndroidAssetManager(null); } public static AssetManager newAssetManager(URL url){ - logger.info("newAssetManager(" + url + ")"); - return new AndroidAssetManager(true); + logger.info("newAssetManager(" + url + ")"); + return new AndroidAssetManager(url); } - public static boolean showSettingsDialog(AppSettings settings) { + public static boolean showSettingsDialog(AppSettings settings, boolean loadSettings) { return true; } diff --git a/engine/src/android/com/jme3/system/android/AndroidTimer.java b/engine/src/android/com/jme3/system/android/AndroidTimer.java index ef0b76898..4cabc3330 100644 --- a/engine/src/android/com/jme3/system/android/AndroidTimer.java +++ b/engine/src/android/com/jme3/system/android/AndroidTimer.java @@ -35,15 +35,14 @@ package com.jme3.system.android; import com.jme3.system.Timer; /** - * NanoTimer is a System.nanoTime implementation of Timer. - * This is primarily useful for headless applications running on a server. - * - * @author Matthew D. Hicks + * AndroidTimer is a System.nanoTime implementation of Timer. */ public class AndroidTimer extends Timer { - private static final long TIMER_RESOLUTION = 1000L; - private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L; + //private static final long TIMER_RESOLUTION = 1000L; + //private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L; + private static final long TIMER_RESOLUTION = 1000000000L; + private static final float INVERSE_TIMER_RESOLUTION = 1f/1000000000L; private long startTime; private long previousTime; @@ -51,7 +50,8 @@ public class AndroidTimer extends Timer { private float fps; public AndroidTimer() { - startTime = System.currentTimeMillis(); + //startTime = System.currentTimeMillis(); + startTime = System.nanoTime(); } /** @@ -66,7 +66,8 @@ public class AndroidTimer extends Timer { } public long getTime() { - return System.currentTimeMillis() - startTime; + //return System.currentTimeMillis() - startTime; + return System.nanoTime() - startTime; } public long getResolution() { @@ -88,7 +89,8 @@ public class AndroidTimer extends Timer { } public void reset() { - startTime = System.currentTimeMillis(); + //startTime = System.currentTimeMillis(); + startTime = System.nanoTime(); previousTime = getTime(); } } diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java index 26ac788e1..353bd56ca 100644 --- a/engine/src/android/com/jme3/system/android/OGLESContext.java +++ b/engine/src/android/com/jme3/system/android/OGLESContext.java @@ -39,7 +39,6 @@ import com.jme3.input.JoyInput; import com.jme3.input.KeyInput; import com.jme3.input.MouseInput; import com.jme3.input.android.AndroidInput; -//import com.jme3.renderer.android.OGLESRenderer; import com.jme3.renderer.android.OGLESShaderRenderer; import com.jme3.system.AppSettings; import com.jme3.system.JmeContext; @@ -51,48 +50,60 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { +public class OGLESContext implements JmeContext, GLSurfaceView.Renderer +{ private static final Logger logger = Logger.getLogger(OGLESContext.class.getName()); - protected AtomicBoolean created = new AtomicBoolean(false); - protected AppSettings settings = new AppSettings(true); + protected final AtomicBoolean created = new AtomicBoolean(false); + protected final AtomicBoolean renderable = new AtomicBoolean(false); + protected final AtomicBoolean needClose = new AtomicBoolean(false); + protected final Object createdLock = new Object(); + protected final AppSettings settings = new AppSettings(true); - /* < OpenGL ES 2.0 * */ - //protected OGLESRenderer renderer; /* >= OpenGL ES 2.0 (Android 2.2+) */ protected OGLESShaderRenderer renderer; protected Timer timer; protected SystemListener listener; - protected AtomicBoolean needClose = new AtomicBoolean(false); + protected boolean wasActive = false; - protected int frameRate = 0; protected boolean autoFlush = true; protected AndroidInput view; + + private long milliStart; + private long milliDelta; + protected int frameRate = 33; + //protected int minFrameDuration = 1000 / frameRate; // Set a max FPS of 33 + protected int minFrameDuration = 0; // No FPS cap - public OGLESContext(){ - } + public OGLESContext() { } - public Type getType() { + @Override + public Type getType() + { return Type.Display; } - - public GLSurfaceView createView(Activity activity){ - view = new AndroidInput(activity); - - /* - * Requesting client version from GLSurfaceView which is extended by - * AndroidInput. - * This is required to get OpenGL ES 2.0 - */ - - logger.info("setEGLContextClientVersion(2)"); - view.setEGLContextClientVersion(2); - logger.info("setEGLContextClientVersion(2) ... done."); - + + public GLSurfaceView createView(Activity activity) + { + return createView(new AndroidInput(activity)); + } + + + public GLSurfaceView createView(AndroidInput view) + { + this.view = view; + + /* + * Requesting client version from GLSurfaceView which is extended by + * AndroidInput. + * This is required to get OpenGL ES 2.0 + */ + view.setEGLContextClientVersion(2); + //RGB565, Depth16 view.setEGLConfigChooser(5, 6, 5, 0, 16, 0); view.setFocusableInTouchMode(true); @@ -104,12 +115,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { return view; } + - protected void applySettings(AppSettings setting){ - } - - protected void initInThread(GL10 gl){ - logger.info("Display created."); + protected void initInThread() + { + logger.info("OGLESContext create"); logger.fine("Running on thread: "+Thread.currentThread().getName()); Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @@ -117,175 +127,220 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown); } }); - - created.set(true); - + timer = new AndroidTimer(); - renderer = new OGLESShaderRenderer(gl); - applySettingsToRenderer(renderer, settings); - + renderer = new OGLESShaderRenderer(); + + renderer.setUseVA(true); + renderer.setVerboseLogging(false); + renderer.initialize(); - listener.initialize(); - - // OGLESShaderRenderer does not support guiView yet - // forcefully remove all gui nodes - - if (listener instanceof com.jme3.app.SimpleApplication) { - ((com.jme3.app.SimpleApplication) listener).getGuiNode().detachAllChildren(); - } + listener.initialize(); + created.set(true); + + needClose.set(false); } /** * De-initialize in the OpenGL thread. */ - protected void deinitInThread(){ + protected void deinitInThread() + { + if (renderer != null) + renderer.cleanup(); + listener.destroy(); - if (renderer != null) { - renderer.cleanup(); - // do android specific cleaning here - - logger.info("Display destroyed."); + + // do android specific cleaning here + logger.info("Display destroyed."); + renderable.set(false); created.set(false); renderer = null; timer = null; - } + } + + protected void applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) + { + logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]"); + logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]"); + renderer.setUseVA(settings.getBoolean("USE_VA")); + renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING")); + } + + protected void applySettings(AppSettings setting) + { + if (renderer != null) + applySettingsToRenderer(renderer, settings); } - - protected void applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) { - logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]"); - logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]"); - renderer.setUseVA(settings.getBoolean("USE_VA")); - renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING")); - } - - @Override - public void setSettings(AppSettings settings) { - this.settings.copyFrom(settings); - - // XXX This code should be somewhere else - if (renderer != null) - applySettingsToRenderer(renderer, this.settings); + @Override + public void setSettings(AppSettings settings) + { + this.settings.copyFrom(settings); } + @Override public void setSystemListener(SystemListener listener){ this.listener = listener; } + @Override public AppSettings getSettings() { return settings; } + @Override public com.jme3.renderer.Renderer getRenderer() { return renderer; } + @Override public MouseInput getMouseInput() { return view; } + @Override public KeyInput getKeyInput() { return view; } - + + @Override public JoyInput getJoyInput() { return null; } - - public Timer getTimer() { + + @Override + public Timer getTimer() + { return timer; } - public void setTitle(String title) { + @Override + public void setTitle(String title) + { } - - public boolean isCreated(){ + + @Override + public boolean isCreated() + { return created.get(); } - - public void setAutoFlushFrames(boolean enabled){ + @Override + public void setAutoFlushFrames(boolean enabled) + { this.autoFlush = enabled; } // renderer:initialize - public void onSurfaceCreated(GL10 gl, EGLConfig cfg) { - logger.info("Using Android"); - initInThread(gl); + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig cfg) + { + logger.info("GL Surface created"); + initInThread(); + renderable.set(true); } // SystemListener:reshape - public void onSurfaceChanged(GL10 gl, int width, int height) { + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) + { settings.setResolution(width, height); listener.reshape(width, height); } // SystemListener:update - public void onDrawFrame(GL10 gl) { - if (needClose.get()){ - deinitInThread(); // ??? + @Override + public void onDrawFrame(GL10 gl) + { + + if (!created.get()) + throw new IllegalStateException("onDrawFrame without create"); + + if (needClose.get()) + { + deinitInThread(); return; } - -// if (wasActive != Display.isActive()){ -// if (!wasActive){ -// listener.gainFocus(); -// wasActive = true; -// }else{ -// listener.loseFocus(); -// wasActive = false; -// } -// } - - if (!created.get()) - throw new IllegalStateException(); - - listener.update(); - - // swap buffers - if (frameRate > 0){ -// Display.sync(frameRate); - // synchronzie to framerate - } + if (renderable.get()) + { + milliStart = System.currentTimeMillis(); + - if (autoFlush) - renderer.onFrame(); - } - - /** - * TODO: get these methods to follow the spec - * @param waitFor - */ - public void create(boolean waitFor) { - if (created.get()){ - logger.warning("create() called when display is already created!"); - return; + + listener.update(); + + + if (autoFlush) + { + renderer.onFrame(); + } + + milliDelta = System.currentTimeMillis() - milliStart; + + // Enforce a FPS cap + if (milliDelta < minFrameDuration) + { + //logger.log(Level.INFO, "Time per frame {0}", milliDelta); + try { + Thread.sleep(minFrameDuration - milliDelta); + } catch (InterruptedException e) { + } + } + } + + } - - public void create(){ - create(false); + + @Override + public boolean isRenderable() + { + return renderable.get(); } - - public void restart() { + + @Override + public void create(boolean waitFor) + { + if (waitFor) + waitFor(true); } - - public boolean isRenderable() { - // TODO isRenderable - return true; + + public void create() + { + create(false); } - /** - * TODO: get these methods to follow the spec - * @param waitFor - */ - public void destroy(boolean waitFor) { + @Override + public void restart() + { + + } + + @Override + public void destroy(boolean waitFor) + { needClose.set(true); + if (waitFor) + waitFor(false); } - - public void destroy(){ + + public void destroy() + { destroy(false); } + + protected void waitFor(boolean createdVal) + { + synchronized (createdLock){ + while (created.get() != createdVal){ + try { + createdLock.wait(); + } catch (InterruptedException ex) { + } + } + } + } } diff --git a/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java b/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java index e28ad5d41..1e7c8b518 100644 --- a/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java +++ b/engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java @@ -2,8 +2,11 @@ package com.jme3.texture.plugins; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Matrix; + import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; +import com.jme3.asset.TextureKey; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import com.jme3.util.BufferUtils; @@ -11,16 +14,19 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; -public class AndroidImageLoader implements AssetLoader { +public class AndroidImageLoader implements AssetLoader +{ - public Object load2(AssetInfo info) throws IOException { + public Object load2(AssetInfo info) throws IOException + { ByteBuffer bb = BufferUtils.createByteBuffer(1 * 1 * 2); bb.put( (byte) 0xff ).put( (byte) 0xff ); bb.clear(); return new Image(Format.RGB5A1, 1, 1, bb); } - public Object load(AssetInfo info) throws IOException { + public Object load(AssetInfo info) throws IOException + { InputStream in = null; Bitmap bitmap = null; try { @@ -36,59 +42,41 @@ public class AndroidImageLoader implements AssetLoader { int width = bitmap.getWidth(); int height = bitmap.getHeight(); - int bytesPerPixel = -1; Format fmt; switch (bitmap.getConfig()){ case ALPHA_8: - bytesPerPixel = 1; fmt = Format.Alpha8; break; case ARGB_4444: - bytesPerPixel = 2; fmt = Format.ARGB4444; break; case ARGB_8888: - bytesPerPixel = 4; fmt = Format.RGBA8; break; - case RGB_565: - bytesPerPixel = 2; + case RGB_565: fmt = Format.RGB565; break; default: return null; } -// if (width > 128 || height > 128){ -// if (width > height){ -// float aspect = (float) height / width; -// width = 128; -// height = (int) (128 * aspect); -// -// }else{ -// float aspect = (float) width / height; -// width = (int) (128 * aspect); -// height = 128; -// } -// bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true); -// } + if ( ((TextureKey)info.getKey()).isFlipY() ) + { + Bitmap newBitmap = null; + Matrix flipMat = new Matrix(); + flipMat.preScale(1.0f, -1.0f); + newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false); + bitmap.recycle(); + bitmap = newBitmap; -// if ( ((TextureKey)info.getKey()).isFlipY() ){ -// Bitmap newBitmap = null; -// Matrix flipMat = new Matrix(); -// flipMat.preScale(1.0f, -1.0f); -// newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false); -// bitmap.recycle(); -// bitmap = newBitmap; -// -// if (bitmap == null){ -// throw new IOException("Failed to load image2: "+info.getKey().getName()); -// } -// } + if (bitmap == null){ + throw new IOException("Failed to flip image: "+info.getKey().getName()); + } + } Image image = new Image(fmt, width, height, null); - image.setEfficentData(bitmap); + image.setEfficentData(bitmap); return image; } diff --git a/engine/src/android/com/jme3/util/FastInteger.java b/engine/src/android/com/jme3/util/FastInteger.java new file mode 100644 index 000000000..49294b4a9 --- /dev/null +++ b/engine/src/android/com/jme3/util/FastInteger.java @@ -0,0 +1,359 @@ +package com.jme3.util; + + +/** + * The wrapper for the primitive type {@code int}. + *

+ * As with the specification, this implementation relies on code laid out in Henry S. Warren, Jr.'s Hacker's + * Delight, (Addison Wesley, 2002) as well as The Aggregate's Magic Algorithms. + * + * @see java.lang.Number + * @since 1.1 + */ +public final class FastInteger { + + /** + * Constant for the maximum {@code int} value, 231-1. + */ + public static final int MAX_VALUE = 0x7FFFFFFF; + + /** + * Constant for the minimum {@code int} value, -231. + */ + public static final int MIN_VALUE = 0x80000000; + + /** + * Constant for the number of bits needed to represent an {@code int} in + * two's complement form. + * + * @since 1.5 + */ + public static final int SIZE = 32; + + /* + * Progressively smaller decimal order of magnitude that can be represented + * by an instance of Integer. Used to help compute the String + * representation. + */ + private static final int[] decimalScale = new int[] { 1000000000, 100000000, + 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 }; + + /** + * Converts the specified integer into its decimal string representation. + * The returned string is a concatenation of a minus sign if the number is + * negative and characters from '0' to '9'. + * + * @param value + * the integer to convert. + * @return the decimal string representation of {@code value}. + */ + public static boolean toCharArray(int value, char[] output) { + if (value == 0) + { + output[0] = '0'; + output[1] = 0; + return true; + } + + // Faster algorithm for smaller Integers + if (value < 1000 && value > -1000) { + + int positive_value = value < 0 ? -value : value; + int first_digit = 0; + if (value < 0) { + output[0] = '-'; + first_digit++; + } + int last_digit = first_digit; + int quot = positive_value; + do { + int res = quot / 10; + int digit_value = quot - ((res << 3) + (res << 1)); + digit_value += '0'; + output[last_digit++] = (char) digit_value; + quot = res; + } while (quot != 0); + + int count = last_digit--; + do { + char tmp = output[last_digit]; + output[last_digit--] = output[first_digit]; + output[first_digit++] = tmp; + } while (first_digit < last_digit); + output[count] = 0; + return true; + } + if (value == MIN_VALUE) { + System.arraycopy("-2147483648".toCharArray(), 0, output, 0, 12); + output[12] = 0; + return true; + } + + + int positive_value = value < 0 ? -value : value; + byte first_digit = 0; + if (value < 0) { + output[0] = '-'; + first_digit++; + } + byte last_digit = first_digit; + byte count; + int number; + boolean start = false; + for (int i = 0; i < 9; i++) { + count = 0; + if (positive_value < (number = decimalScale[i])) { + if (start) { + output[last_digit++] = '0'; + } + continue; + } + + if (i > 0) { + number = (decimalScale[i] << 3); + if (positive_value >= number) { + positive_value -= number; + count += 8; + } + number = (decimalScale[i] << 2); + if (positive_value >= number) { + positive_value -= number; + count += 4; + } + } + number = (decimalScale[i] << 1); + if (positive_value >= number) { + positive_value -= number; + count += 2; + } + if (positive_value >= decimalScale[i]) { + positive_value -= decimalScale[i]; + count++; + } + if (count > 0 && !start) { + start = true; + } + if (start) { + output[last_digit++] = (char) (count + '0'); + } + } + + output[last_digit++] = (char) (positive_value + '0'); + output[last_digit] = 0; + count = last_digit--; + return true; + } + + + /** + * Determines the highest (leftmost) bit of the specified integer that is 1 + * and returns the bit mask value for that bit. This is also referred to as + * the Most Significant 1 Bit. Returns zero if the specified integer is + * zero. + * + * @param i + * the integer to examine. + * @return the bit mask indicating the highest 1 bit in {@code i}. + * @since 1.5 + */ + public static int highestOneBit(int i) { + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return (i & ~(i >>> 1)); + } + + /** + * Determines the lowest (rightmost) bit of the specified integer that is 1 + * and returns the bit mask value for that bit. This is also referred + * to as the Least Significant 1 Bit. Returns zero if the specified integer + * is zero. + * + * @param i + * the integer to examine. + * @return the bit mask indicating the lowest 1 bit in {@code i}. + * @since 1.5 + */ + public static int lowestOneBit(int i) { + return (i & (-i)); + } + + /** + * Determines the number of leading zeros in the specified integer prior to + * the {@link #highestOneBit(int) highest one bit}. + * + * @param i + * the integer to examine. + * @return the number of leading zeros in {@code i}. + * @since 1.5 + */ + public static int numberOfLeadingZeros(int i) { + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return bitCount(~i); + } + + /** + * Determines the number of trailing zeros in the specified integer after + * the {@link #lowestOneBit(int) lowest one bit}. + * + * @param i + * the integer to examine. + * @return the number of trailing zeros in {@code i}. + * @since 1.5 + */ + public static int numberOfTrailingZeros(int i) { + return bitCount((i & -i) - 1); + } + + /** + * Counts the number of 1 bits in the specified integer; this is also + * referred to as population count. + * + * @param i + * the integer to examine. + * @return the number of 1 bits in {@code i}. + * @since 1.5 + */ + public static int bitCount(int i) { + i -= ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + i = (((i >> 4) + i) & 0x0F0F0F0F); + i += (i >> 8); + i += (i >> 16); + return (i & 0x0000003F); + } + + /** + * Rotates the bits of the specified integer to the left by the specified + * number of bits. + * + * @param i + * the integer value to rotate left. + * @param distance + * the number of bits to rotate. + * @return the rotated value. + * @since 1.5 + */ + public static int rotateLeft(int i, int distance) { + if (distance == 0) { + return i; + } + /* + * According to JLS3, 15.19, the right operand of a shift is always + * implicitly masked with 0x1F, which the negation of 'distance' is + * taking advantage of. + */ + return ((i << distance) | (i >>> (-distance))); + } + + /** + * Rotates the bits of the specified integer to the right by the specified + * number of bits. + * + * @param i + * the integer value to rotate right. + * @param distance + * the number of bits to rotate. + * @return the rotated value. + * @since 1.5 + */ + public static int rotateRight(int i, int distance) { + if (distance == 0) { + return i; + } + /* + * According to JLS3, 15.19, the right operand of a shift is always + * implicitly masked with 0x1F, which the negation of 'distance' is + * taking advantage of. + */ + return ((i >>> distance) | (i << (-distance))); + } + + /** + * Reverses the order of the bytes of the specified integer. + * + * @param i + * the integer value for which to reverse the byte order. + * @return the reversed value. + * @since 1.5 + */ + public static int reverseBytes(int i) { + int b3 = i >>> 24; + int b2 = (i >>> 8) & 0xFF00; + int b1 = (i & 0xFF00) << 8; + int b0 = i << 24; + return (b0 | b1 | b2 | b3); + } + + /** + * Reverses the order of the bits of the specified integer. + * + * @param i + * the integer value for which to reverse the bit order. + * @return the reversed value. + * @since 1.5 + */ + public static int reverse(int i) { + // From Hacker's Delight, 7-1, Figure 7-1 + i = (i & 0x55555555) << 1 | (i >> 1) & 0x55555555; + i = (i & 0x33333333) << 2 | (i >> 2) & 0x33333333; + i = (i & 0x0F0F0F0F) << 4 | (i >> 4) & 0x0F0F0F0F; + return reverseBytes(i); + } + + /** + * Returns the value of the {@code signum} function for the specified + * integer. + * + * @param i + * the integer value to check. + * @return -1 if {@code i} is negative, 1 if {@code i} is positive, 0 if + * {@code i} is zero. + * @since 1.5 + */ + public static int signum(int i) { + return (i == 0 ? 0 : (i < 0 ? -1 : 1)); + } + + /** + * Returns a {@code Integer} instance for the specified integer value. + *

+ * If it is not necessary to get a new {@code Integer} instance, it is + * recommended to use this method instead of the constructor, since it + * maintains a cache of instances which may result in better performance. + * + * @param i + * the integer value to store in the instance. + * @return a {@code Integer} instance containing {@code i}. + * @since 1.5 + */ + public static Integer valueOf(int i) { + if (i < -128 || i > 127) { + return new Integer(i); + } + return valueOfCache.CACHE [i+128]; + + } + + static class valueOfCache { + /** + *

+ * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing. + */ + static final Integer[] CACHE = new Integer[256]; + + static { + for(int i=-128; i<=127; i++) { + CACHE[i+128] = new Integer(i); + } + } + } +}